Skip to content

Commit

Permalink
fix nonce issue
Browse files Browse the repository at this point in the history
- in SSG mode, modify the 99-cspNonce nitro plugin
- in SSR mode, modify the headers for prerendered routes
  • Loading branch information
vejja committed Oct 17, 2023
1 parent 0fe16bb commit 20b0966
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 7 deletions.
17 changes: 16 additions & 1 deletion src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ export default defineNuxtModule<ModuleOptions>({

setSecurityRouteRules(nuxt, securityOptions)

// Remove Content-Security-Policy header in pre-rendered routes
// When pre-rendered, the CSP is provided via html <meta> instead
// If kept, this would block the site from rendering
removeCspHeaderForPrerenderedRoutes(nuxt)

if (nuxt.options.security.requestSizeLimiter) {
addServerHandler({
handler: normalize(
Expand Down Expand Up @@ -125,7 +130,6 @@ export default defineNuxtModule<ModuleOptions>({
)
})
}

if (nuxt.options.security.nonce) {
addServerHandler({
handler: normalize(
Expand Down Expand Up @@ -209,6 +213,17 @@ const setSecurityRouteRules = (nuxt: Nuxt, securityOptions: ModuleOptions) => {
}
}

const removeCspHeaderForPrerenderedRoutes = (nuxt: Nuxt) => {
const nitroRouteRules = nuxt.options.nitro.routeRules
for (const route in nitroRouteRules) {
const routeRules = nitroRouteRules[route]
if (routeRules.prerender) {
routeRules.headers = routeRules.headers || {}
routeRules.headers['Content-Security-Policy'] = ''
}
}
}

const registerSecurityNitroPlugins = (
nuxt: Nuxt,
securityOptions: ModuleOptions
Expand Down
11 changes: 7 additions & 4 deletions src/runtime/nitro/plugins/99-cspNonce.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,13 @@ const tagNotPrecededByQuotes = (tag: string) => new RegExp(`(?<!['|"])<${tag}`,

export default <NitroAppPlugin> function (nitro) {
nitro.hooks.hook('render:html', (html: NuxtRenderHTMLContext, { event }: { event: H3Event }) => {
console.log('pre', isPrerendering(event))
if (isPrerendering(event)) {
// In SSG mode, do not inject nonces in html
// However first make sure we erase nonce placeholders from CSP meta
html.head = html.head.map((meta) => {
if (!meta.startsWith('<meta http-equiv="Content-Security-Policy"')) { return meta }
return meta.replaceAll("'nonce-{{nonce}}'", '')
})
return
}
const nonce = parseNonce(`${event.node.res.getHeader('Content-Security-Policy')}`)
Expand Down Expand Up @@ -61,10 +66,8 @@ export default <NitroAppPlugin> function (nitro) {
}

/**
* Only enable behavior if Content Security pPolicy is enabled,
* initial page is prerendered and generated file type is HTML.
* Detect if page is being pre-rendered
* @param event H3Event
* @param options ModuleOptions
* @returns boolean
*/
function isPrerendering(event: H3Event): boolean {
Expand Down
3 changes: 1 addition & 2 deletions test/nonce.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,11 @@ describe('[nuxt-security] Nonce', async () => {
const meta = body.match(/<meta http-equiv="Content-Security-Policy" content="(.*?)"(.*?)>/)
const content = meta?.[1]
const cspNonces = content?.match(/'nonce-(.*?)'/)

expect(res).toBeDefined()
expect(res).toBeTruthy()
expect(content).toBeDefined()
expect(injectedNonces).toBe(null)
expect(cspNonces).toBe(null)
expect(content).toBe("base-uri 'self'; font-src 'self' https: data:; form-action 'self'; frame-ancestors 'self'; img-src 'self' data:; object-src 'none'; script-src-attr 'self' 'strict-dynamic'; style-src 'self' ; upgrade-insecure-requests; script-src 'self' 'strict-dynamic'")
})
})

0 comments on commit 20b0966

Please sign in to comment.