import assert from "node:assert/strict"; import { readFile } from "node:fs/promises"; import test from "node:test"; import { CONTENT_SECURITY_POLICY, PERMISSIONS_POLICY, withSecurityHeaders, } from "../worker/security-headers.ts"; test("applies the browser security baseline without changing the response", async () => { const request = new Request("https://cloud.example.test/dashboard"); const response = withSecurityHeaders( request, new Response("ok", { status: 201, headers: { "Cache-Control": "private, no-store", "Content-Type": "text/plain; charset=utf-8", }, }), ); assert.equal(response.status, 201); assert.equal(await response.text(), "ok"); assert.equal(response.headers.get("Cache-Control"), "private, no-store"); assert.equal(response.headers.get("Content-Type"), "text/plain; charset=utf-8"); assert.equal(response.headers.get("Content-Security-Policy"), CONTENT_SECURITY_POLICY); assert.equal(response.headers.get("Permissions-Policy"), PERMISSIONS_POLICY); assert.equal(response.headers.get("Referrer-Policy"), "no-referrer"); assert.equal(response.headers.get("X-Content-Type-Options"), "nosniff"); assert.equal(response.headers.get("X-DNS-Prefetch-Control"), "off"); assert.equal(response.headers.get("X-Frame-Options"), "DENY"); assert.equal(response.headers.get("X-Permitted-Cross-Domain-Policies"), "none"); assert.equal(response.headers.get("Strict-Transport-Security"), "max-age=31536000"); }); test("keeps the baseline CSP restrictive while documenting its inline compatibility exception", () => { assert.match(CONTENT_SECURITY_POLICY, /default-src 'self'/); assert.match(CONTENT_SECURITY_POLICY, /object-src 'none'/); assert.match(CONTENT_SECURITY_POLICY, /frame-ancestors 'none'/); assert.match(CONTENT_SECURITY_POLICY, /form-action 'self'/); assert.match(CONTENT_SECURITY_POLICY, /script-src 'self' 'unsafe-inline'/); assert.doesNotMatch(CONTENT_SECURITY_POLICY, /unsafe-eval/); assert.doesNotMatch(CONTENT_SECURITY_POLICY, /script-src[^;]*(?:\*|https:)/); assert.match(PERMISSIONS_POLICY, /camera=\(\)/); assert.match(PERMISSIONS_POLICY, /microphone=\(\)/); assert.match(PERMISSIONS_POLICY, /payment=\(\)/); }); test("never advertises HSTS on a plaintext development origin", () => { const response = withSecurityHeaders( new Request("http://127.0.0.1:3000/"), new Response(null, { headers: { "Strict-Transport-Security": "max-age=999999" }, }), ); assert.equal(response.headers.get("Strict-Transport-Security"), null); assert.equal(response.headers.get("X-Frame-Options"), "DENY"); }); test("prevents browsers and intermediaries from caching account and API data", () => { for (const path of ["/api/account/export", "/dashboard", "/dashboard/hosts", "/admin"]) { const response = withSecurityHeaders( new Request(`https://cloud.example.test${path}`), new Response("private", { headers: { "Cache-Control": "public, max-age=3600" } }), ); assert.equal(response.headers.get("Cache-Control"), "private, no-store"); } const publicResponse = withSecurityHeaders( new Request("https://cloud.example.test/trust"), new Response("public", { headers: { "Cache-Control": "public, max-age=300" } }), ); assert.equal(publicResponse.headers.get("Cache-Control"), "public, max-age=300"); }); test("wraps both application and image responses at the Worker exit", async () => { const source = await readFile(new URL("../worker/index.ts", import.meta.url), "utf8"); assert.match(source, /return withSecurityHeaders\(request, response\);[\s\S]*handler\.fetch/); assert.match(source, /handler\.fetch[\s\S]*return withSecurityHeaders\(request, response\);/); });