@@ -10,22 +10,21 @@ import {APP_ID as APP_ID_TOKEN, PLATFORM_ID} from '@angular/core';
1010import { TestBed } from '@angular/core/testing' ;
1111
1212import { getDocument } from '../src/render3/interfaces/document' ;
13- import { escapeTransferStateContent , makeStateKey , TransferState , unescapeTransferStateContent } from '../src/transfer_state' ;
13+ import { makeStateKey , TransferState } from '../src/transfer_state' ;
1414
15- ( function ( ) {
1615function removeScriptTag ( doc : Document , id : string ) {
1716 const existing = doc . getElementById ( id ) ;
1817 if ( existing ) {
1918 doc . body . removeChild ( existing ) ;
2019 }
2120}
2221
23- function addScriptTag ( doc : Document , appId : string , data : { } ) {
22+ function addScriptTag ( doc : Document , appId : string , data : object | string ) {
2423 const script = doc . createElement ( 'script' ) ;
2524 const id = appId + '-state' ;
2625 script . id = id ;
2726 script . setAttribute ( 'type' , 'application/json' ) ;
28- script . textContent = escapeTransferStateContent ( JSON . stringify ( data ) ) ;
27+ script . textContent = typeof data === 'string' ? data : JSON . stringify ( data ) ;
2928
3029 // Remove any stale script tags.
3130 removeScriptTag ( doc , id ) ;
@@ -129,19 +128,26 @@ describe('TransferState', () => {
129128 transferState . remove ( TEST_KEY ) ;
130129 expect ( transferState . isEmpty ) . toBeTrue ( ) ;
131130 } ) ;
132- } ) ;
133131
134- describe ( 'escape/unescape' , ( ) => {
135- it ( 'works with all escaped characters' , ( ) => {
136- const testString = '</script><script>alert(\'Hello&\' + "World");' ;
137- const testObj = { testString} ;
138- const escaped = escapeTransferStateContent ( JSON . stringify ( testObj ) ) ;
139- expect ( escaped ) . toBe (
140- '{&q;testString&q;:&q;&l;/script&g;&l;script&g;' +
141- 'alert(&s;Hello&a;&s; + \\&q;World\\&q;);&q;}' ) ;
142-
143- const unescapedObj = JSON . parse ( unescapeTransferStateContent ( escaped ) ) as { testString : string } ;
144- expect ( unescapedObj [ 'testString' ] ) . toBe ( testString ) ;
132+ it ( 'should encode `<` to avoid breaking out of <script> tag in serialized output' , ( ) => {
133+ const transferState = TestBed . inject ( TransferState ) ;
134+
135+ // The state is empty initially.
136+ expect ( transferState . isEmpty ) . toBeTrue ( ) ;
137+
138+ transferState . set ( DELAYED_KEY , '</script><script>alert(\'Hello&\' + "World");' ) ;
139+ expect ( transferState . toJson ( ) )
140+ . toBe ( `{"delayed":"\\u003C/script>\\u003Cscript>alert('Hello&' + \\"World\\");"}` ) ;
141+ } ) ;
142+
143+ it ( 'should decode `\\u003C` (<) when restoring stating' , ( ) => {
144+ const encodedState =
145+ `{"delayed":"\\u003C/script>\\u003Cscript>alert('Hello&' + \\"World\\");"}` ;
146+ addScriptTag ( doc , APP_ID , encodedState ) ;
147+ const transferState = TestBed . inject ( TransferState ) ;
148+
149+ expect ( transferState . toJson ( ) ) . toBe ( encodedState ) ;
150+ expect ( transferState . get ( DELAYED_KEY , null ) )
151+ . toBe ( '</script><script>alert(\'Hello&\' + "World");' ) ;
145152 } ) ;
146153} ) ;
147- } ) ( ) ;
0 commit comments