@@ -83,11 +83,13 @@ export default {
8383 console . error ( `TURNSTILE_SECRET_KEY is missing` ) ;
8484 }
8585
86+ const envTurnstileInline = env . TURNSTILE_INLINE !== 'no' ;
87+ const nonce = crypto . randomUUID ( ) ;
8688 const headHandler = new TurnstileHeadHandler ( env . TURNSTILE_RANDOM ?? '' ) ;
8789 const turnstileHandler = new TurnstileBodyHandler (
8890 env . TURNSTILE_SITE_KEY ,
8991 fieldName ,
90- ! ! env . TURNSTILE_INLINE ,
92+ envTurnstileInline ? nonce : undefined ,
9193 env . TURNSTILE_RANDOM ?? '' ,
9294 env . TURNSTILE_BACKENDS ?? '' ,
9395 ) ;
@@ -166,28 +168,70 @@ export default {
166168 const response = new Response ( responseOriginal . body , responseOriginal ) ;
167169 if ( response . headers . has ( 'content-security-policy' ) ) {
168170 const csp : Array < string > = [ ] ;
169- let setScriptSrc = true ;
170- let setFrameSrc = true ;
171+ const cspMap : Record < string , string > = { } ;
171172 for ( const option of ( response . headers . get ( 'content-security-policy' ) ?? '' ) . split ( / [ , ; ] \s * / ) ) {
172- if ( option . startsWith ( 'script-src ' ) && ! option . includes ( ' https://challenges.cloudflare.com' ) ) {
173- csp . push ( option . trim ( ) . concat ( ' https://challenges.cloudflare.com' ) ) ;
174- setScriptSrc = false ;
175- } else if ( option . startsWith ( 'frame-src ' ) && ! option . includes ( ' https://challenges.cloudflare.com' ) ) {
176- csp . push ( option . trim ( ) . concat ( ' https://challenges.cloudflare.com' ) ) ;
177- setFrameSrc = false ;
178- } else if ( option . startsWith ( 'connect-src: ' ) && ! option . includes ( ` 'self'` ) ) {
179- csp . push ( option . trim ( ) . concat ( ` 'self'` ) ) ;
180- } else if ( option . trim ( ) . length > 0 ) {
181- csp . push ( option . trim ( ) ) ;
173+ if ( option . trim ( ) . length === 0 ) {
174+ continue ;
175+ }
176+ const [ directive , ...rest ] = option . trim ( ) . split ( ' ' ) ;
177+ const value = rest . join ( ' ' ) ;
178+ if ( cspMap [ directive ] === undefined ) {
179+ csp . push ( directive ) ;
180+ }
181+ cspMap [ directive ] = ( cspMap [ directive ] ?? '' ) . concat ( ' ' , ( value ?? '' ) . trim ( ) ) . trim ( ) ;
182+ }
183+ {
184+ let scriptSrc = cspMap [ 'script-src' ] ?? cspMap [ 'default-src' ] ?? '' ;
185+ if ( ! scriptSrc . includes ( 'https://challenges.cloudflare.com' ) ) {
186+ scriptSrc = scriptSrc . concat ( ' https://challenges.cloudflare.com' ) . trim ( ) ;
187+ if ( cspMap [ 'script-src' ] === undefined ) {
188+ csp . push ( 'script-src' ) ;
189+ }
190+ cspMap [ 'script-src' ] = scriptSrc ;
182191 }
183192 }
184- if ( setScriptSrc ) {
185- csp . push ( `script-src 'self' https://challenges.cloudflare.com` ) ;
186- setScriptSrc = false ;
193+ {
194+ let frameSrc = cspMap [ 'frame-src' ] ?? cspMap [ 'default-src' ] ?? '' ;
195+ if ( ! frameSrc . includes ( 'https://challenges.cloudflare.com' ) ) {
196+ frameSrc = frameSrc . concat ( ' https://challenges.cloudflare.com' ) . trim ( ) ;
197+ if ( cspMap [ 'frame-src' ] === undefined ) {
198+ csp . push ( 'frame-src' ) ;
199+ }
200+ cspMap [ 'frame-src' ] = frameSrc ;
201+ }
202+ }
203+ {
204+ let connectSrc = cspMap [ 'connect-src' ] ?? cspMap [ 'default-src' ] ?? '' ;
205+ if ( ! connectSrc . includes ( `'self'` ) ) {
206+ connectSrc = connectSrc . concat ( ` 'self'` ) . trim ( ) ;
207+ if ( cspMap [ 'connect-src' ] === undefined ) {
208+ csp . push ( 'connect-src' ) ;
209+ }
210+ cspMap [ 'connect-src' ] = connectSrc ;
211+ }
187212 }
188- if ( setFrameSrc ) {
189- csp . push ( `frame-src 'self' https://challenges.cloudflare.com` ) ;
190- setFrameSrc = false ;
213+ {
214+ if ( envTurnstileInline && ! ( cspMap [ 'script-src' ] ?? '' ) . includes ( `'nonce-${ nonce } '` ) ) {
215+ const scriptSrc = ( cspMap [ 'script-src' ] ?? cspMap [ 'default-src' ] ?? '' ) . concat ( ` 'nonce-${ nonce } '` ) . trim ( ) ;
216+ if ( cspMap [ 'script-src' ] === undefined ) {
217+ csp . push ( 'script-src' ) ;
218+ }
219+ cspMap [ 'script-src' ] = scriptSrc ;
220+ }
221+ }
222+ {
223+ if ( ! envTurnstileInline && ! ( cspMap [ 'script-src' ] ?? cspMap [ 'default-src' ] ?? '' ) . includes ( `'self'` ) ) {
224+ const scriptSrc = ( cspMap [ 'script-src' ] ?? cspMap [ 'default-src' ] ?? '' ) . concat ( ` 'self'` ) . trim ( ) ;
225+ if ( cspMap [ 'script-src' ] === undefined ) {
226+ csp . push ( 'script-src' ) ;
227+ }
228+ cspMap [ 'script-src' ] = scriptSrc ;
229+ }
230+ }
231+
232+ for ( let cspIndex = 0 ; cspIndex < csp . length ; cspIndex += 1 ) {
233+ const directive = csp [ cspIndex ] ;
234+ csp [ cspIndex ] = `${ directive } ${ cspMap [ directive ] } ` ;
191235 }
192236 response . headers . set ( 'Content-Security-Policy' , csp . join ( '; ' ) ) ;
193237 }
0 commit comments