From 36988e820e80792c924e6c1514b6d29737ac5238 Mon Sep 17 00:00:00 2001 From: Nathan Rajlich Date: Thu, 4 Jun 2020 08:10:05 -0700 Subject: [PATCH] Prevent open redirects when `cleanUrls` config is enabled (#122) * Prevent open redirects when `cleanUrls` config is enabled * Cov --- src/index.js | 18 ++++++++---------- test/integration.js | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/index.js b/src/index.js index 85bbe76..05e3430 100644 --- a/src/index.js +++ b/src/index.js @@ -127,15 +127,19 @@ const shouldRedirect = (decodedPath, {redirects = [], trailingSlash}, cleanUrl) return null; } - let cleanedUrl = false; - // By stripping the HTML parts from the decoded // path *before* handling the trailing slash, we make // sure that only *one* redirect occurs if both // config options are used. if (cleanUrl && matchHTML.test(decodedPath)) { decodedPath = decodedPath.replace(matchHTML, ''); - cleanedUrl = true; + if (decodedPath.indexOf('//') > -1) { + decodedPath = decodedPath.replace(/\/+/g, '/'); + } + return { + target: ensureSlashStart(decodedPath), + statusCode: defaultType + }; } if (slashing) { @@ -163,13 +167,6 @@ const shouldRedirect = (decodedPath, {redirects = [], trailingSlash}, cleanUrl) } } - if (cleanedUrl) { - return { - target: ensureSlashStart(decodedPath), - statusCode: defaultType - }; - } - // This is currently the fastest way to // iterate over an array for (let index = 0; index < redirects.length; index++) { @@ -412,6 +409,7 @@ const renderDirectory = async (current, acceptsJSON, handlers, methods, config, return 1; } + /* istanbul ignore next */ if (a.base < b.base) { return -1; } diff --git a/test/integration.js b/test/integration.js index 61b812b..18a1511 100644 --- a/test/integration.js +++ b/test/integration.js @@ -278,6 +278,20 @@ test('set `trailingSlash` config property to `false`', async t => { t.is(location, target); }); +test('set `cleanUrls` config property should prevent open redirects', async t => { + const url = await getUrl({ + cleanUrls: true + }); + + const response = await fetch(`${url}//haveibeenpwned.com/index`, { + redirect: 'manual', + follow: 0 + }); + + const location = response.headers.get('location'); + t.is(location, `${url}/haveibeenpwned.com`); +}); + test('set `rewrites` config property to wildcard path', async t => { const destination = '.dotfile'; const related = path.join(fixturesFull, destination);