UNPKG

@nosecone/next

Version:

Protect your Next.js application with secure headers

96 lines (95 loc) 3.66 kB
import nosecone, { defaults as defaults$1, withVercelToolbar } from "nosecone"; //#region src/index.ts /** * Nosecone Next.js defaults. * * The type spells out the concrete directive shape rather than the wider * `Options`: consumers extend the defaults by spreading * `defaults.contentSecurityPolicy.directives`, so every directive must stay * a concrete readonly array, as it was before `isolatedDeclarations` required * an explicit annotation here. Everything is derived from `typeof * baseDefaults` so the type cannot drift from the `nosecone` package. */ const defaults = { ...defaults$1, contentSecurityPolicy: { directives: { ...defaults$1.contentSecurityPolicy.directives, scriptSrc: [...defaults$1.contentSecurityPolicy.directives.scriptSrc, ...nextScriptSrc()], styleSrc: [...defaults$1.contentSecurityPolicy.directives.styleSrc, ...nextStyleSrc()] } } }; /** * Create security headers. * * @deprecated * Use the named export `nosecone` instead. */ var src_default = nosecone; function createNonce() { return `'nonce-${btoa(crypto.randomUUID())}'`; } function nextScriptSrc() { return process.env.NODE_ENV === "development" ? [createNonce, "'unsafe-eval'"] : [createNonce]; } const nonceSourceRegex = /^'nonce-([A-Za-z0-9+/_-]+={0,2})'$/; /** * Extract the nonce from a `Content-Security-Policy` header value. * * @param value * Value of the `Content-Security-Policy` header. * @returns * The nonce, or `undefined` if there is no nonce in the header. */ function nonceFromContentSecurityPolicy(value) { const directives = value.split(";").map((directive) => directive.trim()); const directive = directives.find((directive) => directive.startsWith("script-src")) ?? directives.find((directive) => directive.startsWith("default-src")); if (!directive) return; for (const source of directive.split(/\s+/).slice(1)) { const match = source.trim().match(nonceSourceRegex); if (match) return match[1]; } } /** * Get the `Content-Security-Policy` nonce for the current request. * * Nosecone generates a unique nonce for every request when the * `Content-Security-Policy` is configured with our defaults. Next.js applies * that nonce to the scripts it renders automatically, but third-party scripts * that do not use the Next.js `<Script>` component need the value passed in * manually. For example, PostHog requires the nonce in its `init` call. * * Call this from a Server Component, Route Handler, or anywhere else * `next/headers` is available. The page must be dynamically rendered for the * nonce to be available — see the example in the `README`. * * @returns * The nonce for the current request, or `undefined` if the * `Content-Security-Policy` is disabled or does not contain a nonce. */ async function nonce() { const { headers } = await import("next/headers.js"); const headerList = await headers(); const contentSecurityPolicy = headerList.get("content-security-policy") ?? headerList.get("content-security-policy-report-only"); if (typeof contentSecurityPolicy !== "string") return; return nonceFromContentSecurityPolicy(contentSecurityPolicy); } function nextStyleSrc() { return ["'unsafe-inline'"]; } /** * Create Next.js middleware that sets secure headers on every request. * * @param options * Configuration to provide to Nosecone. * @returns * Next.js middleware that sets secure headers. */ function createMiddleware(options = defaults) { return async () => { const headers = nosecone(options); headers.set("x-middleware-next", "1"); return new Response(null, { headers }); }; } //#endregion export { createMiddleware, src_default as default, defaults, nonce, nosecone, withVercelToolbar };