Skip to content
Open
28 changes: 9 additions & 19 deletions src/runtime/nitro/plugins/40-cspSsrNonce.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ import { defineNitroPlugin } from 'nitropack/runtime'
import { resolveSecurityRules } from '../context'
import { generateRandomNonce } from '../../../utils/crypto'

const LINK_RE = /<link\b([^>]*?>)/gi
const NONCE_RE = /nonce="[^"]+"/i
const SCRIPT_RE = /<script\b([^>]*?>)/gi
const STYLE_RE = /<style\b([^>]*?>)/gi
const QUOTE_MASK_RE = /"([^"\\]*(?:\\.[^"\\]*)*)"/g;
const NONCE_ELEM_RE = /<(link|script|style)\b([^>]*?>)/gi
const NONCE_RE = /\bnonce=__QUOTE_PLACEHOLDER_(\d+)__/i
const QUOTE_MASK_RE = /(?<!\\)"[^"\\]*(?:\\.[^"\\]*)*"/g
const QUOTE_RESTORE_RE = /__QUOTE_PLACEHOLDER_(\d+)__/g

function injectNonceToTags(element: string, nonce: string) {
Expand All @@ -19,27 +17,19 @@ function injectNonceToTags(element: string, nonce: string) {
// Mask attributes to avoid manipulating stringified elements
let maskedElement = element.replace(QUOTE_MASK_RE, (match) => {
quotes.push(match);
return `__QUOTE_PLACEHOLDER_${quotes.length - 1}__`;
return `__QUOTE_PLACEHOLDER_${quotes.length - 1}__`
});
// Add nonce to all link tags
maskedElement = maskedElement.replace(LINK_RE, (match, rest) => {
// Add nonce to all necessary tags
maskedElement = maskedElement.replace(NONCE_ELEM_RE, (match, elem, rest) => {
if (NONCE_RE.test(rest)) {
return match.replace(NONCE_RE, `nonce="${nonce}"`);
return match.replace(NONCE_RE, `nonce="${nonce}"`)
}
return `<link nonce="${nonce}"` + rest
})
// Add nonce to all script tags
maskedElement = maskedElement.replace(SCRIPT_RE, (match, rest) => {
return `<script nonce="${nonce}"` + rest
})
// Add nonce to all style tags
maskedElement = maskedElement.replace(STYLE_RE, (match, rest) => {
return `<style nonce="${nonce}"` + rest
return `<${elem} nonce="${nonce}"` + rest
})

// Restore the original quoted content.
const restoredHtml = maskedElement.replace(QUOTE_RESTORE_RE, (match, index) => {
return quotes[parseInt(index, 10)];
return quotes[parseInt(index, 10)]
});

return restoredHtml;
Expand Down
Loading