diff --git a/reserve/src/dispatcher.js b/reserve/src/dispatcher.js index 4dea7909..b5927121 100644 --- a/reserve/src/dispatcher.js +++ b/reserve/src/dispatcher.js @@ -137,8 +137,12 @@ function dispatch (context, url, index = 0) { module.exports = function (configuration, request, response) { let { url } = request if (url.indexOf('.') !== -1 || url.indexOf('%') !== -1) { - const { pathname, search, hash } = new URL(url, 'p:/') - url = decodeURIComponent(pathname) + search + hash + try { + const { pathname, search, hash } = new URL(url, 'p:/') + url = decodeURIComponent(pathname.replace(/%0\d|%1\d/g, '')) + search + hash + } catch (e) { + url = 400 + } } const { @@ -152,6 +156,7 @@ module.exports = function (configuration, request, response) { id, internal: !!request[$requestInternal], method: request.method, + incomingUrl: request.url, url, headers: { ...request.headers }, start: new Date(), diff --git a/reserve/src/dispatcher.spec.js b/reserve/src/dispatcher.spec.js index 6fd13811..85407bb1 100644 --- a/reserve/src/dispatcher.spec.js +++ b/reserve/src/dispatcher.spec.js @@ -493,7 +493,7 @@ describe('dispatcher', () => { }) }) - describe.only('URL normalization for security', () => { + describe('URL normalization for security', () => { it('avoids path traversal by normalizing the URL if needed (using root ../)', () => { const request = new Request('GET') request.setForgedUrl('/../file.txt') @@ -553,5 +553,29 @@ describe('dispatcher', () => { assert.strictEqual(response.toString(), 'Hello World!') }) }) + + for (let i = 1; i < 32; ++i) { + it('works around forbidden chars', () => { + const request = new Request('GET') + request.setForgedUrl(`/test/%2E%2E/file%${Number(i).toString(8).padStart(2, '0')}.txt`) + return dispatch({ request }) + .then(({ emitted, response }) => { + assert.ok(!hasError(emitted)) + assert.strictEqual(response.statusCode, 200) + assert.strictEqual(response.headers['Content-Type'], textMimeType) + assert.strictEqual(response.toString(), 'Hello World!') + }) + }) + } + + it('rejects malformed URLs', () => { + const request = new Request('GET') + request.setForgedUrl('/test/%2E%2E/file%0.txt') + return dispatch({ request }) + .then(({ emitted, response }) => { + assert.ok(!hasError(emitted)) + assert.strictEqual(response.statusCode, 400) + }) + }) }) })