From 80537d779a895c0d4f6bb735c514fc3cf0e3a10f Mon Sep 17 00:00:00 2001 From: Jory Irving Date: Thu, 3 Sep 2026 21:33:57 -0600 Subject: [PATCH] test(middleware): pin NODE_ENV for the CSP assertions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit applySecurityHeaders skips the CSP in development on purpose, so the security header assertions only hold outside it. They never said so: they inherited whatever the runner set, and passed only because vitest 4 overrode NODE_ENV to 'test' inside the test environment despite 'npm run test' passing NODE_ENV=development. vitest 5 does not override it. The value reaches the middleware as 'development', the CSP branch is skipped, and twelve assertions read a null header — the whole of the remaining failure on the vitest 5 bump. Stub the value in the block that depends on it, and add the test the dev exemption never had: in development there is no CSP, but the static headers are still set. That branch flipping is what went unnoticed. Verified on both: 2529 pass on vitest 4, and 31/31 in this file on vitest 5 with NODE_ENV=development. --- src/middleware.test.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/middleware.test.ts b/src/middleware.test.ts index 181a81ff..76fef56d 100644 --- a/src/middleware.test.ts +++ b/src/middleware.test.ts @@ -385,13 +385,37 @@ describe("security headers", () => { clearAll(); resetAuthCaches(); mocks.getToken.mockReset(); + // applySecurityHeaders skips the CSP in development on purpose (see the + // comment there), so these assertions only hold outside it. Pin the value + // rather than inheriting whatever the runner happens to set: `npm run + // test` passes NODE_ENV=development, and this block passed only because + // vitest 4 overrode it to "test" inside the environment. vitest 5 does + // not, and every CSP assertion here silently read a null header. + vi.stubEnv("NODE_ENV", "production"); }); afterEach(() => { + vi.unstubAllEnvs(); clearAll(); resetAuthCaches(); }); + it("does not set a CSP in development, but still sets the static headers", async () => { + // The dev exemption is deliberate: Next invokes the proxy several times per + // document request and the browser intersects the resulting CSP headers, + // which collapses script-src and breaks hydration. Asserting it here means + // the exemption cannot be removed, or silently inverted by a runner's + // NODE_ENV handling, without a test noticing. + vi.stubEnv("NODE_ENV", "development"); + process.env.DISPATCH_AUTH_MODE = "disabled"; + + const res = await middleware(makeRequest("/board")); + + expect(res.headers.get("content-security-policy")).toBeNull(); + expect(res.headers.get("x-content-type-options")).toBe("nosniff"); + expect(res.headers.get("x-frame-options")).toBe("DENY"); + }); + it("injects security headers on disabled mode responses", async () => { process.env.DISPATCH_AUTH_MODE = "disabled";