UNPKG

nuxt-security

Version:

🛡️ Security Module for Nuxt based on HTTP Headers and Middleware

50 lines (49 loc) 1.9 kB
import { defineNitroPlugin } from "#imports"; import { resolveSecurityRules } from "../context/index.js"; export default defineNitroPlugin((nitroApp) => { nitroApp.hooks.hook("render:html", (response, { event }) => { if (response.island) { return; } const rules = resolveSecurityRules(event); if (rules.enabled && rules.headers) { const headers = rules.headers; if (headers.contentSecurityPolicy) { const csp = headers.contentSecurityPolicy; const nonce = event.context.security?.nonce; const scriptHashes = event.context.security?.hashes?.script; const styleHashes = event.context.security?.hashes?.style; headers.contentSecurityPolicy = updateCspVariables(csp, nonce, scriptHashes, styleHashes); } } }); }); function updateCspVariables(csp, nonce, scriptHashes, styleHashes) { const generatedCsp = Object.fromEntries(Object.entries(csp).map(([directive, value]) => { if (typeof value === "boolean") { return [directive, value]; } const sources = typeof value === "string" ? value.split(" ").map((token) => token.trim()).filter((token) => token) : value; const modifiedSources = sources.filter((source) => { if (source.startsWith("'nonce-") && source !== "'nonce-{{nonce}}'") { console.warn("[nuxt-security] removing static nonce from CSP header"); return false; } return true; }).map((source) => { if (source === "'nonce-{{nonce}}'") { return nonce ? `'nonce-${nonce}'` : ""; } else { return source; } }).filter((source) => source); if (directive === "script-src" && scriptHashes) { modifiedSources.push(...scriptHashes); } if (directive === "style-src" && styleHashes) { modifiedSources.push(...styleHashes); } return [directive, modifiedSources]; })); return generatedCsp; }