@@ -501,116 +501,47 @@ describe('deepEqual', () => {
501501} )
502502
503503describe ( 'decodePath' , ( ) => {
504- it ( 'should decode a path segment with no ignored items existing' , ( ) => {
505- const itemsToCheck = [ '%25' , '%5C' ]
506- const stringToCheck =
507- 'https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B'
508- const expectedResult = 'https://mozilla.org/?x=шеллы'
509-
510- const result = decodePath ( stringToCheck , itemsToCheck )
511-
512- expect ( result ) . toBe ( expectedResult )
513- } )
514-
515- it ( 'should decode a path segment with one ignored character and one ignored item existing' , ( ) => {
516- const itemsToCheck = [ '%25' ]
517- const stringToCheck =
518- 'https://mozilla.org/?x=%25%D1%88%D0%B5%5C%D0%BB%D0%BB%D1%8B'
519- const expectedResult = 'https://mozilla.org/?x=%25ше\\ллы'
520-
521- const result = decodePath ( stringToCheck , itemsToCheck )
522-
523- expect ( result ) . toBe ( expectedResult )
524- } )
525-
526- it ( 'should decode a path segment with multiple ignored characters and first ignored item existing' , ( ) => {
527- const itemsToCheck = [ '%25' , '%5C' ]
528- let stringToCheck =
529- 'https://mozilla.org/?x=%25%D1%88%D0%B5%D0%BB%D0%BB%D1%8B'
530- let expectedResult = 'https://mozilla.org/?x=%25шеллы'
531-
532- let result = decodePath ( stringToCheck , itemsToCheck )
533-
534- expect ( result ) . toBe ( expectedResult )
535-
536- stringToCheck = 'https://mozilla.org/?x=%D1%88%D0%B5%5C%D0%BB%D0%BB%D1%8B'
537- expectedResult = 'https://mozilla.org/?x=ше%5Cллы'
538-
539- result = decodePath ( stringToCheck , itemsToCheck )
540-
541- expect ( result ) . toBe ( expectedResult )
542- } )
543-
544- it ( 'should decode a path segment with multiple ignored characters and other ignored item existing' , ( ) => {
545- const itemsToCheck = [ '%25' , '%5C' ]
546- let stringToCheck =
547- 'https://mozilla.org/?x=%5C%D1%88%D0%B5%D0%BB%D0%BB%D1%8B'
548- let expectedResult = 'https://mozilla.org/?x=%5Cшеллы'
549-
550- let result = decodePath ( stringToCheck , itemsToCheck )
551-
552- expect ( result ) . toBe ( expectedResult )
553-
554- stringToCheck = 'https://mozilla.org/?x=%D1%88%D0%B5%5C%D0%BB%D0%BB%D1%8B'
555- expectedResult = 'https://mozilla.org/?x=ше%5Cллы'
556-
557- result = decodePath ( stringToCheck , itemsToCheck )
558-
559- expect ( result ) . toBe ( expectedResult )
560- } )
561-
562- it ( 'should decode a path segment with multiple ignored characters and multiple ignored items existing' , ( ) => {
563- const itemsToCheck = [ '%25' , '%5C' ]
564- const stringToCheck =
565- 'https://mozilla.org/?x=%25%D1%88%D0%B5%5C%D0%BB%D0%BB%D1%8B'
566- const expectedResult = 'https://mozilla.org/?x=%25ше%5Cллы'
567-
568- const result = decodePath ( stringToCheck , itemsToCheck )
569-
570- expect ( result ) . toBe ( expectedResult )
571- } )
572-
573504 it ( 'should decode a path segment, ignoring `%` and `\\` by default, with multiple ignored items existing' , ( ) => {
574505 const stringToCheck =
575506 'https://mozilla.org/?x=%25%D1%88%D0%B5%5C%D0%BB%D0%BB%D1%8B%2F'
576507 const expectedResult = 'https://mozilla.org/?x=%25ше%5Cллы%2F'
577508
578- const result = decodePath ( stringToCheck )
509+ const result = decodePath ( stringToCheck ) . path
579510
580511 expect ( result ) . toBe ( expectedResult )
581512 } )
582513
583514 it ( 'should handle malformed percent-encodings gracefully' , ( ) => {
584515 const stringToCheck = 'path%ZZ%D1%88test%5C%C3%A9'
585516 // Malformed sequences should remain as-is, valid ones decoded
586- const result = decodePath ( stringToCheck )
517+ const result = decodePath ( stringToCheck ) . path
587518 expect ( result ) . toBe ( `path%ZZ%D1%88test%5Cé` )
588519 } )
589520
590521 it ( 'should return empty string unchanged' , ( ) => {
591- expect ( decodePath ( '' ) ) . toBe ( '' )
522+ expect ( decodePath ( '' ) . path ) . toBe ( '' )
592523 } )
593524
594525 it ( 'should return strings without encoding unchanged' , ( ) => {
595526 const stringToCheck = 'plain-text-path'
596- expect ( decodePath ( stringToCheck ) ) . toBe ( stringToCheck )
527+ expect ( decodePath ( stringToCheck ) . path ) . toBe ( stringToCheck )
597528 } )
598529
599530 it ( 'should handle consecutive ignored characters' , ( ) => {
600531 const stringToCheck = 'test%25%25end'
601532 const expectedResult = 'test%25%25end'
602- expect ( decodePath ( stringToCheck ) ) . toBe ( expectedResult )
533+ expect ( decodePath ( stringToCheck ) . path ) . toBe ( expectedResult )
603534 } )
604535
605536 it ( 'should handle multiple ignored items of the same type with varying case' , ( ) => {
606537 const stringToCheck = '/params-ps/named/foo%2Fabc/c%2Fh'
607538 const expectedResult = '/params-ps/named/foo%2Fabc/c%2Fh'
608- expect ( decodePath ( stringToCheck ) ) . toBe ( expectedResult )
539+ expect ( decodePath ( stringToCheck ) . path ) . toBe ( expectedResult )
609540
610541 const stringToCheckWithLowerCase = '/params-ps/named/foo%2Fabc/c%5C%2f%5cAh'
611542 const expectedResultWithLowerCase =
612543 '/params-ps/named/foo%2Fabc/c%5C%2f%5cAh'
613- expect ( decodePath ( stringToCheckWithLowerCase ) ) . toBe (
544+ expect ( decodePath ( stringToCheckWithLowerCase ) . path ) . toBe (
614545 expectedResultWithLowerCase ,
615546 )
616547 } )
@@ -620,49 +551,61 @@ describe('decodePath', () => {
620551 // /%0d/google.com/ decodes to /\r/google.com/ which becomes //google.com/
621552 // This must be sanitized to prevent protocol-relative URL interpretation
622553 const result = decodePath ( '/%0d/google.com/' )
623- expect ( result ) . toBe ( '/google.com/' )
624- expect ( result ) . not . toMatch ( / ^ \/ \/ / )
554+ expect ( result . path ) . toBe ( '/google.com/' )
555+ expect ( result . path ) . not . toMatch ( / ^ \/ \/ / )
556+ expect ( result . handledProtocolRelativeURL ) . toBe ( true )
625557 } )
626558
627559 it ( 'should strip LF (%0a) to prevent open redirect' , ( ) => {
628560 const result = decodePath ( '/%0a/evil.com/' )
629- expect ( result ) . toBe ( '/evil.com/' )
630- expect ( result ) . not . toMatch ( / ^ \/ \/ / )
561+ expect ( result . path ) . toBe ( '/evil.com/' )
562+ expect ( result . path ) . not . toMatch ( / ^ \/ \/ / )
563+ expect ( result . handledProtocolRelativeURL ) . toBe ( true )
631564 } )
632565
633566 it ( 'should strip CRLF (%0d%0a) to prevent open redirect' , ( ) => {
634567 const result = decodePath ( '/%0d%0a/evil.com/' )
635- expect ( result ) . toBe ( '/evil.com/' )
636- expect ( result ) . not . toMatch ( / ^ \/ \/ / )
568+ expect ( result . path ) . toBe ( '/evil.com/' )
569+ expect ( result . path ) . not . toMatch ( / ^ \/ \/ / )
570+ expect ( result . handledProtocolRelativeURL ) . toBe ( true )
637571 } )
638572
639573 it ( 'should strip multiple control characters to prevent open redirect' , ( ) => {
640574 const result = decodePath ( '/%0d%0d%0d/evil.com/' )
641- expect ( result ) . toBe ( '/evil.com/' )
642- expect ( result ) . not . toMatch ( / ^ \/ \/ / )
575+ expect ( result . path ) . toBe ( '/evil.com/' )
576+ expect ( result . path ) . not . toMatch ( / ^ \/ \/ / )
577+ expect ( result . handledProtocolRelativeURL ) . toBe ( true )
643578 } )
644579
645580 it ( 'should strip null bytes and other control characters' , ( ) => {
646581 const result = decodePath ( '/%00/test/' )
647- expect ( result ) . toBe ( '/test/' )
582+ expect ( result . path ) . toBe ( '/test/' )
583+ expect ( result . handledProtocolRelativeURL ) . toBe ( true )
648584 } )
649585
650586 it ( 'should collapse leading double slashes to prevent protocol-relative URLs' , ( ) => {
651587 // After stripping control chars, ensure we don't end up with //evil.com
652588 const result = decodePath ( '/%0d%0a/evil.com/path' )
653589 // Should resolve to localhost, not evil.com
654- const url = new URL ( result , 'http://localhost:3000' )
590+ const url = new URL ( result . path , 'http://localhost:3000' )
655591 expect ( url . origin ) . toBe ( 'http://localhost:3000' )
592+ expect ( result . handledProtocolRelativeURL ) . toBe ( true )
656593 } )
657594
658595 it ( 'should handle normal paths unchanged' , ( ) => {
659- expect ( decodePath ( '/users/profile/' ) ) . toBe ( '/users/profile/' )
660- expect ( decodePath ( '/api/v1/data' ) ) . toBe ( '/api/v1/data' )
596+ expect ( decodePath ( '/users/profile/' ) . path ) . toBe ( '/users/profile/' )
597+ expect ( decodePath ( '/users/profile/' ) . handledProtocolRelativeURL ) . toBe (
598+ false ,
599+ )
600+ expect ( decodePath ( '/api/v1/data' ) . path ) . toBe ( '/api/v1/data' )
601+ expect ( decodePath ( '/api/v1/data' ) . handledProtocolRelativeURL ) . toBe ( false )
661602 } )
662603
663604 it ( 'should handle double slash only input' , ( ) => {
664605 // Direct // input should also be collapsed
665- expect ( decodePath ( '//' ) ) . toBe ( '/' )
606+ const result = decodePath ( '//' )
607+ expect ( result . path ) . toBe ( '/' )
608+ expect ( result . handledProtocolRelativeURL ) . toBe ( true )
666609 } )
667610 } )
668611} )
0 commit comments