🐛 Fixed regex match replacement when dealing with external URLs (#11781)

refs #10898

- Execute string replacement on external paths
- Take non-top-level base URLs into consideration (to avoid #10776 dups)
- Added tests for all of the above cases
This commit is contained in:
Federico Tibaldo 2020-04-30 08:51:36 +02:00 committed by GitHub
parent e6dbf4ce47
commit 4fcc31015b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 74 additions and 11 deletions

View File

@ -4,6 +4,7 @@ const url = require('url');
const path = require('path');
const debug = require('ghost-ignition').debug('web:shared:mw:custom-redirects');
const config = require('../../../config');
const urlUtils = require('../../../lib/url-utils');
const errors = require('@tryghost/errors');
const {logging, i18n} = require('../../../lib/common');
const redirectsService = require('../../../../frontend/services/redirects');
@ -51,13 +52,17 @@ _private.registerRoutes = () => {
debug('register', redirect.from);
customRedirectsRouter.get(new RegExp(redirect.from, options), function (req, res) {
const maxAge = redirect.permanent ? config.get('caching:customRedirects:maxAge') : 0;
const fromURL = url.parse(req.originalUrl);
const toURL = url.parse(redirect.to);
const currentURL = url.parse(req.originalUrl);
toURL.pathname = (toURL.hostname)
? toURL.pathname
: fromURL.pathname.replace(new RegExp(redirect.from, options), toURL.pathname);
toURL.search = fromURL.search;
// if the URL points to an external website, remove Ghost's base path
/** @see https://github.com/TryGhost/Ghost/issues/10776 */
const currentPath = (toURL.hostname)
? currentURL.pathname.replace(urlUtils.getSubdir(), '')
: currentURL.pathname;
toURL.pathname = currentPath.replace(new RegExp(redirect.from, options), toURL.pathname);
toURL.search = currentURL.search;
res.set({
'Cache-Control': `public, max-age=${maxAge}`

View File

@ -60,10 +60,10 @@ describe('Redirects API', function () {
.then((res) => {
res.headers['content-disposition'].should.eql('Attachment; filename="redirects.json"');
res.headers['content-type'].should.eql('application/json; charset=utf-8');
res.headers['content-length'].should.eql('698');
res.headers['content-length'].should.eql('756');
res.body.should.not.be.empty();
res.body.length.should.equal(12);
res.body.length.should.equal(13);
});
});
});

View File

@ -60,10 +60,10 @@ describe('Redirects API', function () {
.then((res) => {
res.headers['content-disposition'].should.eql('Attachment; filename="redirects.json"');
res.headers['content-type'].should.eql('application/json; charset=utf-8');
res.headers['content-length'].should.eql('698');
res.headers['content-length'].should.eql('756');
res.body.should.not.be.empty();
res.body.length.should.equal(12);
res.body.length.should.equal(13);
});
});
});

View File

@ -60,10 +60,10 @@ describe('Redirects API', function () {
.then((res) => {
res.headers['content-disposition'].should.eql('Attachment; filename="redirects.json"');
res.headers['content-type'].should.eql('application/json; charset=utf-8');
res.headers['content-length'].should.eql('698');
res.headers['content-length'].should.eql('756');
res.body.should.not.be.empty();
res.body.length.should.equal(12);
res.body.length.should.equal(13);
});
});
});

View File

@ -769,6 +769,60 @@ describe('Frontend Routing', function () {
doEnd(done)(err, res);
});
});
it('with capturing group', function (done) {
request.get('/external-url/docs')
.expect(302)
.expect('Cache-Control', testUtils.cacheRules.public)
.end(function (err, res) {
res.headers.location.should.eql('https://ghost.org/docs');
doEnd(done)(err, res);
});
});
});
});
describe('Subdirectory redirects (use redirects.json from test/utils/fixtures/data)', function () {
var ghostServer;
before(function () {
configUtils.set('url', 'http://localhost:2370/blog/');
urlUtils.stubUrlUtilsFromConfig();
return ghost({forceStart: true})
.then(function (_ghostServer) {
ghostServer = _ghostServer;
request = supertest.agent(config.get('server:host') + ':' + config.get('server:port'));
});
});
after(function () {
configUtils.restore();
urlUtils.restore();
});
describe('internal url redirect', function () {
it('shold include the subdirectory', function (done) {
request.get('/blog/my-old-blog-post/')
.expect(302)
.expect('Cache-Control', testUtils.cacheRules.public)
.end(function (err, res) {
res.headers.location.should.eql('/blog/revamped-url/');
doEnd(done)(err, res);
});
});
});
describe('external url redirect', function () {
it('shold not include the subdirectory', function (done) {
request.get('/blog/external-url/docs')
.expect(302)
.expect('Cache-Control', testUtils.cacheRules.public)
.end(function (err, res) {
res.headers.location.should.eql('https://ghost.org/docs');
doEnd(done)(err, res);
});
});
});
});
});

View File

@ -46,5 +46,9 @@
{
"from": "/external-url",
"to": "https://ghost.org"
},
{
"from": "/external-url/(.*)",
"to": "https://ghost.org/$1"
}
]