import { test, expect } from "bun:test"; import { withSecurityHeaders, isWebSocketOriginAllowed, resolveRequestUrl } from "../src/index.ts"; const req = (headers: Record = {}) => new Request("https://x/", { headers }); function scriptSrc(csp: string): string { return csp .split(";") .map((s) => s.trim()) .find((s) => s.startsWith("script-src"))!; } test("CSP nonce is added to script-src and drops unsafe-inline", () => { const res = withSecurityHeaders(req(), new Response("x"), "development", undefined, "ABC123"); const directive = scriptSrc(res.headers.get("content-security-policy")!); expect(directive).toContain("'nonce-ABC123'"); expect(directive).not.toContain("'unsafe-inline'"); }); test("without a nonce, dev script-src keeps unsafe-inline (for HMR)", () => { const res = withSecurityHeaders(req(), new Response("x"), "development"); expect(scriptSrc(res.headers.get("content-security-policy")!)).toContain("'unsafe-inline'"); }); test("CORS credentials + origin:* is refused (credentials dropped)", () => { const res = withSecurityHeaders( req({ origin: "https://evil.test" }), new Response("x"), "production", { cors: { enabled: true, origin: "*", credentials: true }, }, ); expect(res.headers.get("access-control-allow-credentials")).toBeNull(); }); test("production sets HSTS + strict CSP", () => { const res = withSecurityHeaders(req(), new Response("x"), "production"); expect(res.headers.get("strict-transport-security")).toContain("max-age="); expect(res.headers.get("content-security-policy")).toContain("default-src 'self'"); }); test("permissions policy overrides merge with restrictive defaults", () => { const res = withSecurityHeaders(req(), new Response("x"), "development", { permissionsPolicy: { camera: ["self"] }, }); const policy = res.headers.get("permissions-policy")!; expect(policy).toContain("camera=(self)"); expect(policy).toContain("microphone=()"); }); test("isWebSocketOriginAllowed blocks cross-site WS (CSWSH), allows same-origin", () => { const wsReq = (origin: string | null, host: string) => new Request("http://x/realtime/c", { headers: origin ? { origin, host } : { host }, }); expect(isWebSocketOriginAllowed(wsReq("http://app.test", "app.test"))).toBe(true); // same-origin expect(isWebSocketOriginAllowed(wsReq("http://evil.test", "app.test"))).toBe(false); // cross-site expect(isWebSocketOriginAllowed(wsReq(null, "app.test"))).toBe(true); // native client, no cookies // Explicit CORS allowlist opens a cross-origin WS. expect( isWebSocketOriginAllowed(wsReq("http://other.test", "app.test"), { cors: { enabled: true, origin: "http://other.test" }, }), ).toBe(true); }); test("resolveRequestUrl honors standard Forwarded headers behind trusted proxies", () => { const request = new Request("http://127.0.0.1:3000/path", { headers: { forwarded: 'for=192.0.2.1;proto=https;host="workroot.in"' }, }); expect(resolveRequestUrl(request, true).href).toBe("https://workroot.in/path"); expect(resolveRequestUrl(request, false).href).toBe("http://127.0.0.1:3000/path"); }); test("default permissions policy does not emit unsupported unload", () => { const res = withSecurityHeaders(req(), new Response("x"), "development"); expect(res.headers.get("permissions-policy") ?? "").not.toContain("unload="); });