@@ -2,11 +2,48 @@ import { defineNitroPlugin } from '#imports'
22import { resolveSecurityRules } from '../context'
33import { generateRandomNonce } from '../../../utils/crypto'
44
5- const LINK_RE = / < l i n k ( [ ^ > ] * ?> ) / gi
5+ const LINK_RE = / < l i n k \b ( [ ^ > ] * ?> ) / gi
66const NONCE_RE = / n o n c e = " [ ^ " ] + " / i
7- const SCRIPT_RE = / < s c r i p t ( [ ^ > ] * ?> ) / gi
8- const STYLE_RE = / < s t y l e ( [ ^ > ] * ?> ) / gi
7+ const SCRIPT_RE = / < s c r i p t \b ( [ ^ > ] * ?> ) / gi
8+ const STYLE_RE = / < s t y l e \b ( [ ^ > ] * ?> ) / gi
9+ const QUOTE_MASK_RE = / " ( [ ^ " ] * ) " / g
10+ const QUOTE_RESTORE_RE = / _ _ Q U O T E _ P L A C E H O L D E R _ ( \d + ) _ _ / g
911
12+ function injectNonceToTags ( element : string , nonce : string ) {
13+ // Skip non-string elements
14+ if ( typeof element !== 'string' ) {
15+ return element ;
16+ }
17+ const quotes : string [ ] = [ ] ;
18+
19+ // Mask attributes to avoid manipulating stringified elements
20+ let maskedElement = element . replace ( QUOTE_MASK_RE , ( match ) => {
21+ quotes . push ( match ) ;
22+ return `__QUOTE_PLACEHOLDER_${ quotes . length - 1 } __` ;
23+ } ) ;
24+ // Add nonce to all link tags
25+ maskedElement = maskedElement . replace ( LINK_RE , ( match , rest ) => {
26+ if ( NONCE_RE . test ( rest ) ) {
27+ return match . replace ( NONCE_RE , `nonce="${ nonce } "` ) ;
28+ }
29+ return `<link nonce="${ nonce } "` + rest
30+ } )
31+ // Add nonce to all script tags
32+ maskedElement = maskedElement . replace ( SCRIPT_RE , ( match , rest ) => {
33+ return `<script nonce="${ nonce } "` + rest
34+ } )
35+ // Add nonce to all style tags
36+ maskedElement = maskedElement . replace ( STYLE_RE , ( match , rest ) => {
37+ return `<style nonce="${ nonce } "` + rest
38+ } )
39+
40+ // Restore the original quoted content.
41+ const restoredHtml = maskedElement . replace ( QUOTE_RESTORE_RE , ( match , index ) => {
42+ return quotes [ parseInt ( index , 10 ) ] ;
43+ } ) ;
44+
45+ return restoredHtml ;
46+ }
1047
1148/**
1249 * This plugin generates a nonce for the current request and adds it to the HTML.
@@ -52,28 +89,7 @@ export default defineNitroPlugin((nitroApp) => {
5289 type Section = 'body' | 'bodyAppend' | 'bodyPrepend' | 'head'
5390 const sections = [ 'body' , 'bodyAppend' , 'bodyPrepend' , 'head' ] as Section [ ]
5491 for ( const section of sections ) {
55- html [ section ] = html [ section ] . map ( ( element ) => {
56- // Skip non-string elements
57- if ( typeof element !== 'string' ) {
58- return element ;
59- }
60- // Add nonce to all link tags
61- element = element . replace ( LINK_RE , ( match , rest ) => {
62- if ( NONCE_RE . test ( rest ) ) {
63- return match . replace ( NONCE_RE , `nonce="${ nonce } "` ) ;
64- }
65- return `<link nonce="${ nonce } "` + rest
66- } )
67- // Add nonce to all script tags
68- element = element . replace ( SCRIPT_RE , ( match , rest ) => {
69- return `<script nonce="${ nonce } "` + rest
70- } )
71- // Add nonce to all style tags
72- element = element . replace ( STYLE_RE , ( match , rest ) => {
73- return `<style nonce="${ nonce } "` + rest
74- } )
75- return element
76- } )
92+ html [ section ] = html [ section ] . map ( ( element ) => injectNonceToTags ( element , nonce ) )
7793 }
7894
7995 // Add meta header for Vite in development
0 commit comments