@@ -115,9 +115,11 @@ describe("popupDocument", () => {
115115 expect ( html ) . not . toContain ( "<details" ) ;
116116 } ) ;
117117
118- it ( "HTML-escapes the BroadcastChannel name so attacker-controlled names cannot break out" , ( ) => {
119- const html = popupDocument ( successPayload , 'evil"name' ) ;
120- expect ( html ) . toContain ( 'new BroadcastChannel("evil"name")' ) ;
118+ it ( "serializes the channel name as JavaScript so the exact channel is preserved" , ( ) => {
119+ const html = popupDocument ( successPayload , 'evil"name\\path' ) ;
120+ expect ( html ) . toContain ( 'new BroadcastChannel("evil\\"name\\\\path")' ) ;
121+ expect ( html ) . toContain ( 'localStorage.setItem("evil\\"name\\\\path",JSON.stringify(p))' ) ;
122+ expect ( html ) . not . toContain ( "evil"name" ) ;
121123 } ) ;
122124
123125 it ( "escapes < > & in the serialized script payload to prevent </script> breakout" , ( ) => {
@@ -138,6 +140,15 @@ describe("popupDocument", () => {
138140 expect ( scriptLiteral ) . toContain ( "\\u003c/script\\u003e" ) ;
139141 } ) ;
140142
143+ it ( "escapes < > & in the serialized channel name to prevent </script> breakout" , ( ) => {
144+ const html = popupDocument ( successPayload , 'channel</script><img src=x onerror="alert(1)">' ) ;
145+ const scriptMatch = / < s c r i p t > ( [ \s \S ] * ?) < \/ s c r i p t > / . exec ( html ) ;
146+ expect ( scriptMatch ) . not . toBeNull ( ) ;
147+ const script = scriptMatch ! [ 1 ] ! ;
148+ expect ( script ) . not . toContain ( "</script>" ) ;
149+ expect ( script ) . toContain ( "channel\\u003c/script\\u003e" ) ;
150+ } ) ;
151+
141152 it ( "posts to window.opener AND falls back to BroadcastChannel with the given channel name" , ( ) => {
142153 const html = popupDocument ( successPayload , "executor:openapi-oauth-result" ) ;
143154 expect ( html ) . toContain ( "window.opener.postMessage(p,window.location.origin)" ) ;
0 commit comments