File tree 2 files changed +19
-1
lines changed
2 files changed +19
-1
lines changed Original file line number Diff line number Diff 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
Original file line number Diff line number Diff 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} ) ;
You can’t perform that action at this time.
0 commit comments