Ghost/ghost/express-dynamic-redirects/test/DynamicRedirectManager.test.js
Prathamesh Gawas 3424222597
🐛 Fixed redirects with special characters (#15533)
closes: https://github.com/TryGhost/Ghost/issues/15267

- This was because the URLs were not being encoded and matched correctly - it is solved by encoding the URL before adding to the router.
2022-10-13 11:41:20 +01:00

418 lines
14 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const should = require('should');
const DynamicRedirectManager = require('../');
const urlJoin = (...parts) => {
let url = parts.join('/');
return url.replace(/(^|[^:])\/\/+/g, '$1/');
};
describe('DynamicRedirectManager', function () {
let headers;
let status;
let location;
let req;
let res;
beforeEach(function () {
headers = null;
status = null;
location = null;
req = {
method: 'GET'
};
res = {
set(_headers) {
headers = _headers;
},
redirect(_status, _location) {
status = _status;
location = _location;
}
};
});
describe('no subdirectory configuration', function () {
let manager;
beforeEach(function () {
manager = new DynamicRedirectManager({
permanentMaxAge: 100,
getSubdirectoryURL: (pathname) => {
return urlJoin('', pathname);
}
});
});
it('Prioritizes the query params of the redirect', function () {
manager.addRedirect('/test-params', '/result?q=abc', {
permanent: true
});
req.url = '/test-params/?q=123&lang=js';
manager.handleRequest(req, res, function next() {
should.fail(true, false, 'next should NOT have been called');
});
should.equal(headers['Cache-Control'], 'public, max-age=100');
should.equal(status, 301);
should.equal(location, '/result?q=abc&lang=js');
});
it('Allows redirects to be removed', function () {
const id = manager.addRedirect('/test-params', '/result?q=abc', {permanent: true});
manager.removeRedirect(id);
req.url = '/test-params/?q=123&lang=js';
manager.handleRequest(req, res, function next() {
should.ok(true, 'next should have been called');
});
should.equal(headers, null);
should.equal(status, null);
should.equal(location, null);
});
it('Can add same redirect multiple times and remove it once', function () {
manager.addRedirect('/test-params', '/result?q=abc', {permanent: true});
const id = manager.addRedirect('/test-params', '/result?q=abc', {permanent: true});
manager.removeRedirect(id);
req.url = '/test-params/?q=123&lang=js';
manager.handleRequest(req, res, function next() {
should.ok(true, 'next should have been called');
});
should.equal(headers, null);
should.equal(status, null);
should.equal(location, null);
});
it('The routing works when passed an invalid regexp for the from parameter', function () {
const from = '/invalid_regex/(/size/[a-zA-Z0-9_-.]*/[a-zA-Z0-9_-.]*/[0-9]*/[0-9]*/)([a-zA-Z0-9_-.]*)';
const to = '/';
manager.addRedirect(from , to, {
permanent: false
});
req.url = '/test-params/';
manager.handleRequest(req, res, function next() {
should.ok(true, 'next should have been called');
});
should.equal(headers, null);
should.equal(status, null);
should.equal(location, null);
});
it('Throws an error if unexpected internal component throws unknown error', function () {
// override internal behavior to throw an unknown error
manager.setupRedirect = () => {
throw new Error('Unknown error');
};
const from = '/match-me';
const to = '/redirect-fails';
try {
manager.addRedirect(from , to);
should.fail(false, 'Should have thrown an error');
} catch (e) {
e.message.should.equal('Unknown error');
}
});
it('removes all redirects', function () {
const from = '/redirect-me';
const to = '/redirected';
manager.addRedirect(from , to);
req.url = '/redirect-me';
manager.removeAllRedirects();
manager.redirectIds.should.be.empty();
manager.redirects.should.be.empty();
manager.handleRequest(req, res, function next() {
should.ok(true, 'next should have been called');
});
});
describe('Substitution regex redirects', function () {
it('Works with substitution redirect case and no trailing slash', function (){
const from = '^/post/[0-9]+/([a-z0-9\\-]+)';
const to = '/$1';
manager.addRedirect(from , to);
req.url = '/post/10/a-nice-blog-post';
manager.handleRequest(req, res, function next() {
should.fail(true, 'next should NOT have been called');
});
// NOTE: max-age is "0" because it's not a permanent redirect
should.equal(headers['Cache-Control'], 'public, max-age=0');
should.equal(status, 302);
should.equal(location, '/a-nice-blog-post');
});
it('Works with substitution redirect case and a trailing slash', function (){
const from = '^/post/[0-9]+/([a-z0-9\\-]+)';
const to = '/$1';
manager.addRedirect(from , to);
req.url = '/post/10/a-nice-blog-post/';
manager.handleRequest(req, res, function next() {
should.fail(true, 'next should NOT have been called');
});
// NOTE: max-age is "0" because it's not a permanent redirect
should.equal(headers['Cache-Control'], 'public, max-age=0');
should.equal(status, 302);
should.equal(location, '/a-nice-blog-post');
});
it('Redirects keeping the query params for substitution regexp', function (){
const from = '^/post/[0-9]+/([a-z0-9\\-]+)';
const to = '/$1';
manager.addRedirect(from , to);
req.url = '/post/10/a-nice-blog-post?a=b';
manager.handleRequest(req, res, function next() {
should.fail(true, 'next should NOT have been called');
});
// NOTE: max-age is "0" because it's not a permanent redirect
should.equal(headers['Cache-Control'], 'public, max-age=0');
should.equal(status, 302);
should.equal(location, '/a-nice-blog-post?a=b');
});
it('Redirects keeping the query params', function (){
const from = '^\\/topic\\/';
const to = '/';
manager.addRedirect(from , to);
req.url = '/topic?something=good';
manager.handleRequest(req, res, function next() {
should.fail(true, 'next should NOT have been called');
});
// NOTE: max-age is "0" because it's not a permanent redirect
should.equal(headers['Cache-Control'], 'public, max-age=0');
should.equal(status, 302);
should.equal(location, '/?something=good');
});
});
describe('Case sensitivity', function () {
it('with case insensitive', function () {
const from = '/^\\/case-insensitive/i';
const to = '/redirected-insensitive';
manager.addRedirect(from , to);
req.url = '/CaSe-InSeNsItIvE';
manager.handleRequest(req, res, function next() {
should.fail(true, 'next should NOT have been called');
});
// NOTE: max-age is "0" because it's not a permanent redirect
should.equal(headers['Cache-Control'], 'public, max-age=0');
should.equal(status, 302);
should.equal(location, '/redirected-insensitive');
});
it('with case sensitive', function () {
const from = '^\\/Case-Sensitive';
const to = '/redirected-sensitive';
manager.addRedirect(from , to);
req.url = '/Case-Sensitive';
manager.handleRequest(req, res, function next() {
should.fail(true, 'next should NOT have been called');
});
// NOTE: max-age is "0" because it's not a permanent redirect
should.equal(headers['Cache-Control'], 'public, max-age=0');
should.equal(status, 302);
should.equal(location, '/redirected-sensitive');
});
it('defaults to case sensitive', function () {
const from = '^\\/Default-Sensitive';
const to = '/redirected-default';
manager.addRedirect(from , to);
req.url = '/Default-Sensitive';
manager.handleRequest(req, res, function next() {
should.fail(true, 'next should NOT have been called');
});
should.equal(headers['Cache-Control'], 'public, max-age=0');
should.equal(status, 302);
should.equal(location, '/redirected-default');
});
it('should not redirect with case sensitive', function () {
const from = '^\\/Case-Sensitive';
const to = '/redirected-insensitive';
manager.addRedirect(from , to);
req.url = '/casE-sensitivE';
manager.handleRequest(req, res, function next() {
should.ok(true, 'next should have been called');
});
should.equal(headers, null);
should.equal(status, null);
should.equal(location, null);
});
it('should not redirect with default case sensitive', function () {
const from = '^\\/Default-Sensitive';
const to = '/redirected-default';
manager.addRedirect(from , to);
req.url = '/defaulT-sensitivE';
manager.handleRequest(req, res, function next() {
should.ok(true, 'next should have been called');
});
should.equal(headers, null);
should.equal(status, null);
should.equal(location, null);
});
});
describe('External url redirect', function () {
it('with trailing slash', function () {
const from = '/external-url';
const to = 'https://ghost.org';
manager.addRedirect(from , to);
req.url = '/external-url/';
manager.handleRequest(req, res, function next() {
should.fail(true, 'next should NOT have been called');
});
// NOTE: max-age is "0" because it's not a permanent redirect
should.equal(headers['Cache-Control'], 'public, max-age=0');
should.equal(status, 302);
should.equal(location, 'https://ghost.org/');
});
it('without trailing slash', function () {
const from = '/external-url';
const to = 'https://ghost.org';
manager.addRedirect(from , to);
req.url = '/external-url';
manager.handleRequest(req, res, function next() {
should.fail(true, 'next should NOT have been called');
});
// NOTE: max-age is "0" because it's not a permanent redirect
should.equal(headers['Cache-Control'], 'public, max-age=0');
should.equal(status, 302);
should.equal(location, 'https://ghost.org/');
});
it('with capturing group', function () {
const from = '/external-url/(.*)';
const to = 'https://ghost.org/$1';
manager.addRedirect(from , to);
req.url = '/external-url/docs';
manager.handleRequest(req, res, function next() {
should.fail(true, 'next should NOT have been called');
});
// NOTE: max-age is "0" because it's not a permanent redirect
should.equal(headers['Cache-Control'], 'public, max-age=0');
should.equal(status, 302);
should.equal(location, 'https://ghost.org/docs');
});
});
describe('Url with special character redirect', function () {
it('redirects urls with special characters', function () {
const from = '/joloonii-surgaltuud/а-анлал/';
const to = '/joloonii-angilal/а-ангилал';
manager.addRedirect(from, to);
req.url = from;
manager.handleRequest(req, res, function next() {
should.fail(true, false, 'next should NOT have been called');
});
// NOTE: max-age is "0" because it's not a permanent redirect
should.equal(headers['Cache-Control'], 'public, max-age=0');
should.equal(status, 302);
should.equal(location, '/joloonii-angilal/а-ангилал');
});
});
});
describe('with subdirectory configuration', function () {
let manager;
beforeEach(function () {
manager = new DynamicRedirectManager({
permanentMaxAge: 100,
getSubdirectoryURL: (pathname) => {
return urlJoin('', pathname);
}
});
});
it('should include the subdirectory', function () {
const from = '/my-old-blog-post/';
const to = '/revamped-url/';
manager.addRedirect(from , to, {permanent: true});
req.url = '/blog/my-old-blog-post/';
manager.handleRequest(req, res, function next() {
should.fail(true, 'next should NOT have been called');
});
should.equal(headers['Cache-Control'], 'public, max-age=100');
should.equal(status, 301);
should.equal(location, '/blog/revamped-url/');
});
});
});