@@ -18,6 +18,9 @@ const getCsps = (html: string) => {
1818const ONE_HASH_CSP =
1919 / s c r i p t - s r c ' s t r i c t - d y n a m i c ' ' s h a 2 5 6 - [ ^ ' ] + ' h t t p s : ' u n s a f e - i n l i n e ' ; o b j e c t - s r c ' n o n e ' ; b a s e - u r i ' s e l f ' ; / ;
2020
21+ const TWO_HASH_CSP =
22+ / s c r i p t - s r c ' s t r i c t - d y n a m i c ' (?: ' s h a 2 5 6 - [ ^ ' ] + ' ) { 2 } h t t p s : ' u n s a f e - i n l i n e ' ; o b j e c t - s r c ' n o n e ' ; b a s e - u r i ' s e l f ' ; / ;
23+
2124const FOUR_HASH_CSP =
2225 / s c r i p t - s r c ' s t r i c t - d y n a m i c ' (?: ' s h a 2 5 6 - [ ^ ' ] + ' ) { 4 } h t t p s : ' u n s a f e - i n l i n e ' ; o b j e c t - s r c ' n o n e ' ; b a s e - u r i ' s e l f ' ; / ;
2326
@@ -55,7 +58,7 @@ describe('auto-csp', () => {
5558 const csps = getCsps ( result ) ;
5659 expect ( csps . length ) . toBe ( 1 ) ;
5760 expect ( csps [ 0 ] ) . toMatch ( ONE_HASH_CSP ) ;
58- expect ( result ) . toContain ( `var scripts = [['./main.js', undefined , false, false]];` ) ;
61+ expect ( result ) . toContain ( `var scripts = [['./main.js', '' , false, false]];` ) ;
5962 } ) ;
6063
6164 it ( 'should rewrite a single source script in place' , async ( ) => {
@@ -75,14 +78,15 @@ describe('auto-csp', () => {
7578 expect ( csps [ 0 ] ) . toMatch ( ONE_HASH_CSP ) ;
7679 // Our loader script appears after the HTML text content.
7780 expect ( result ) . toMatch (
78- / S o m e t e x t < \/ d i v > \s * < s c r i p t > \s * v a r s c r i p t s = \[ \[ ' .\/ m a i n .j s ' , u n d e f i n e d , f a l s e , f a l s e \] \] ; / ,
81+ / S o m e t e x t < \/ d i v > \s * < s c r i p t > \s * v a r s c r i p t s = \[ \[ ' .\/ m a i n .j s ' , ' ' , f a l s e , f a l s e \] \] ; / ,
7982 ) ;
8083 } ) ;
8184
8285 it ( 'should rewrite a multiple source scripts with attributes' , async ( ) => {
8386 const result = await autoCsp ( `
8487 <html>
8588 <head>
89+ <script src="./head.js"></script>
8690 </head>
8791 <body>
8892 <script src="./main1.js"></script>
@@ -96,13 +100,15 @@ describe('auto-csp', () => {
96100
97101 const csps = getCsps ( result ) ;
98102 expect ( csps . length ) . toBe ( 1 ) ;
99- expect ( csps [ 0 ] ) . toMatch ( ONE_HASH_CSP ) ;
103+ expect ( csps [ 0 ] ) . toMatch ( TWO_HASH_CSP ) ;
100104 expect ( result ) . toContain (
101105 // eslint-disable-next-line max-len
102- `var scripts = [['./main1.js', undefined , false, false],['./main2.js', undefined , true, false],['./main3.js', 'module', true, true]];` ,
106+ `var scripts = [['./main1.js', '' , false, false],['./main2.js', '' , true, false],['./main3.js', 'module', true, true]];` ,
103107 ) ;
104- // Only one loader script is created.
105- expect ( Array . from ( result . matchAll ( / < s c r i p t > / g) ) . length ) . toEqual ( 1 ) ;
108+ // Head loader script is in the head.
109+ expect ( result ) . toContain ( `</script></head>` ) ;
110+ // Only two loader scripts are created.
111+ expect ( Array . from ( result . matchAll ( / < s c r i p t > / g) ) . length ) . toEqual ( 2 ) ;
106112 } ) ;
107113
108114 it ( 'should rewrite source scripts with weird URLs' , async ( ) => {
@@ -160,14 +166,40 @@ describe('auto-csp', () => {
160166 // Loader script for main.js and main2.js appear after 'foo' and before 'bar'.
161167 expect ( result ) . toMatch (
162168 // eslint-disable-next-line max-len
163- / c o n s o l e .l o g \( ' f o o ' \) ; < \/ s c r i p t > \s * < s c r i p t > \s * v a r s c r i p t s = \[ \[ ' .\/ m a i n .j s ' , u n d e f i n e d , f a l s e , f a l s e \] , \[ ' .\/ m a i n 2 .j s ' , u n d e f i n e d , f a l s e , f a l s e \] \] ; [ \s \S ] * c o n s o l e .l o g \( ' b a r ' \) ; / ,
169+ / c o n s o l e .l o g \( ' f o o ' \) ; < \/ s c r i p t > \s * < s c r i p t > \s * v a r s c r i p t s = \[ \[ ' .\/ m a i n .j s ' , ' ' , f a l s e , f a l s e \] , \[ ' .\/ m a i n 2 .j s ' , ' ' , f a l s e , f a l s e \] \] ; [ \s \S ] * c o n s o l e .l o g \( ' b a r ' \) ; / ,
164170 ) ;
165171 // Loader script for main3.js and main4.js appear after 'bar'.
166172 expect ( result ) . toMatch (
167173 // eslint-disable-next-line max-len
168- / c o n s o l e .l o g \( ' b a r ' \) ; < \/ s c r i p t > \s * < s c r i p t > \s * v a r s c r i p t s = \[ \[ ' .\/ m a i n 3 .j s ' , u n d e f i n e d , f a l s e , f a l s e \] , \[ ' .\/ m a i n 4 .j s ' , u n d e f i n e d , f a l s e , f a l s e \] \] ; / ,
174+ / c o n s o l e .l o g \( ' b a r ' \) ; < \/ s c r i p t > \s * < s c r i p t > \s * v a r s c r i p t s = \[ \[ ' .\/ m a i n 3 .j s ' , ' ' , f a l s e , f a l s e \] , \[ ' .\/ m a i n 4 .j s ' , ' ' , f a l s e , f a l s e \] \] ; / ,
169175 ) ;
170176 // Exactly 4 scripts should be left.
171177 expect ( Array . from ( result . matchAll ( / < s c r i p t > / g) ) . length ) . toEqual ( 4 ) ;
172178 } ) ;
179+
180+ it ( 'should write a loader script that appends to head' , async ( ) => {
181+ const result = await autoCsp ( `
182+ <html>
183+ <head>
184+ <script src="./head.js"></script>
185+ </head>
186+ <body>
187+ <div>Some text </div>
188+ </body>
189+ </html>
190+ ` ) ;
191+
192+ const csps = getCsps ( result ) ;
193+ expect ( csps . length ) . toBe ( 1 ) ;
194+ expect ( csps [ 0 ] ) . toMatch ( ONE_HASH_CSP ) ;
195+
196+ expect ( result ) . toContain (
197+ // eslint-disable-next-line max-len
198+ `document.lastElementChild.appendChild` ,
199+ ) ;
200+ // Head loader script is in the head.
201+ expect ( result ) . toContain ( `</script></head>` ) ;
202+ // Only one loader script is created.
203+ expect ( Array . from ( result . matchAll ( / < s c r i p t > / g) ) . length ) . toEqual ( 1 ) ;
204+ } ) ;
173205} ) ;
0 commit comments