diff --git a/packages/core-shared/src/security/next/middleware.test.ts b/packages/core-shared/src/security/next/middleware.test.ts index 2d0a633..76edffa 100644 --- a/packages/core-shared/src/security/next/middleware.test.ts +++ b/packages/core-shared/src/security/next/middleware.test.ts @@ -90,6 +90,34 @@ describe("withSecurityHeaders", () => { expect(call[0]?.request?.headers?.get("x-nonce")).toBeTruthy(); }); + it("sets the CSP on the forwarded request headers (Next reads the nonce from there)", () => { + withSecurityHeaders(makeRequest()); + + const call = vi.mocked(NextResponse.next).mock.calls[0] as [ + { request?: { headers?: Headers } } | undefined, + ]; + const requestCsp = call[0]?.request?.headers?.get( + "Content-Security-Policy", + ); + expect(requestCsp).toBeTruthy(); + expect(requestCsp).toBe(mock._store.get("Content-Security-Policy")); + }); + + it("request-header CSP carries the nonce in production mode", () => { + vi.stubEnv("NODE_ENV", "production"); + + withSecurityHeaders(makeRequest()); + + const call = vi.mocked(NextResponse.next).mock.calls[0] as [ + { request?: { headers?: Headers } } | undefined, + ]; + const requestCsp = call[0]?.request?.headers?.get( + "Content-Security-Policy", + ); + const nonce = call[0]?.request?.headers?.get("x-nonce"); + expect(requestCsp).toContain(`'nonce-${nonce}'`); + }); + it("returns the NextResponse from NextResponse.next", () => { const result = withSecurityHeaders(makeRequest()); diff --git a/packages/core-shared/src/security/next/middleware.ts b/packages/core-shared/src/security/next/middleware.ts index dc4dde4..195c5ea 100644 --- a/packages/core-shared/src/security/next/middleware.ts +++ b/packages/core-shared/src/security/next/middleware.ts @@ -7,9 +7,16 @@ export function withSecurityHeaders(request: NextRequest): NextResponse { const nonce = generateNonce(); const mode = process.env.NODE_ENV === "production" ? "prod" : "dev"; const secHeaders = buildSecurityHeaders({ mode, nonce }); + const csp = secHeaders["Content-Security-Policy"]; const requestHeaders = new Headers(request.headers); requestHeaders.set("x-nonce", nonce); + // Next.js only propagates a nonce to its own