From 0947b92164a2c5f661ebcc183d37e7f21de719ad Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Tue, 29 Mar 2022 12:11:14 +0100 Subject: [PATCH] Merge pull request from GHSA-7p99-3798-f85c --- lib/context.js | 3 ++- test/requiresAuth.tests.js | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/context.js b/lib/context.js index c95e363e..0949773b 100644 --- a/lib/context.js +++ b/lib/context.js @@ -187,7 +187,8 @@ class ResponseContext { returnTo = options.returnTo; debug('req.oidc.login() called with returnTo: %s', returnTo); } else if (req.method === 'GET' && req.originalUrl) { - returnTo = req.originalUrl; + // Collapse any leading slashes to a single slash to prevent Open Redirects + returnTo = req.originalUrl.replace(/^\/+/, '/'); debug('req.oidc.login() without returnTo, using: %s', returnTo); } diff --git a/test/requiresAuth.tests.js b/test/requiresAuth.tests.js index 25c8bee8..818fe912 100644 --- a/test/requiresAuth.tests.js +++ b/test/requiresAuth.tests.js @@ -354,4 +354,21 @@ describe('requiresAuth', () => { assert.equal(response.statusCode, 401); sinon.assert.notCalled(checkSpy); }); + + it('should collapse leading slashes on returnTo', async () => { + server = await createServer(auth(defaultConfig)); + const payloads = ['//google.com', '///google.com', '//google.com']; + for (const payload of payloads) { + const response = await request({ url: `${baseUrl}${payload}` }); + const state = new URL(response.headers.location).searchParams.get( + 'state' + ); + const decoded = Buffer.from(state, 'base64'); + const parsed = JSON.parse(decoded); + + assert.equal(response.statusCode, 302); + assert.include(response.headers.location, 'https://op.example.com'); + assert.equal(parsed.returnTo, '/google.com'); + } + }); });