Skip to content

Commit 0947b92

Browse files
authored
Merge pull request from GHSA-7p99-3798-f85c
1 parent c619f67 commit 0947b92

File tree

2 files changed

+19
-1
lines changed

2 files changed

+19
-1
lines changed

Diff for: lib/context.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,8 @@ class ResponseContext {
187187
returnTo = options.returnTo;
188188
debug('req.oidc.login() called with returnTo: %s', returnTo);
189189
} else if (req.method === 'GET' && req.originalUrl) {
190-
returnTo = req.originalUrl;
190+
// Collapse any leading slashes to a single slash to prevent Open Redirects
191+
returnTo = req.originalUrl.replace(/^\/+/, '/');
191192
debug('req.oidc.login() without returnTo, using: %s', returnTo);
192193
}
193194

Diff for: test/requiresAuth.tests.js

+17
Original file line numberDiff line numberDiff line change
@@ -354,4 +354,21 @@ describe('requiresAuth', () => {
354354
assert.equal(response.statusCode, 401);
355355
sinon.assert.notCalled(checkSpy);
356356
});
357+
358+
it('should collapse leading slashes on returnTo', async () => {
359+
server = await createServer(auth(defaultConfig));
360+
const payloads = ['//google.com', '///google.com', '//google.com'];
361+
for (const payload of payloads) {
362+
const response = await request({ url: `${baseUrl}${payload}` });
363+
const state = new URL(response.headers.location).searchParams.get(
364+
'state'
365+
);
366+
const decoded = Buffer.from(state, 'base64');
367+
const parsed = JSON.parse(decoded);
368+
369+
assert.equal(response.statusCode, 302);
370+
assert.include(response.headers.location, 'https://op.example.com');
371+
assert.equal(parsed.returnTo, '/google.com');
372+
}
373+
});
357374
});

0 commit comments

Comments
 (0)