From 67f26a42f1cec40899a98bb47e2d1c5ac7dd8a76 Mon Sep 17 00:00:00 2001 From: Julio Cesar Date: Wed, 20 Aug 2025 17:26:37 +0200 Subject: [PATCH] Add test suite for CORS and CSP validation in Express app --- test-backend/test/run-tests.ts | 63 ++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 test-backend/test/run-tests.ts diff --git a/test-backend/test/run-tests.ts b/test-backend/test/run-tests.ts new file mode 100644 index 0000000..4f5d22c --- /dev/null +++ b/test-backend/test/run-tests.ts @@ -0,0 +1,63 @@ +import http from "http" +import fetch from "node-fetch" +import app from "../app" + +async function listenOnRandomPort() { + return new Promise<{ server: http.Server; port: number }>( + (resolve, reject) => { + const server = http.createServer(app) + server.listen(0, () => { + // @ts-ignore + const addr = server.address() + if (!addr || typeof addr === "string") + return reject(new Error("Failed to get server address")) + resolve({ server, port: addr.port }) + }) + } + ) +} + +async function run() { + const FRONTEND = process.env.FRONTEND_URL || "http://localhost:5173" + const { server, port } = await listenOnRandomPort() + const base = `http://localhost:${port}` + + try { + // 1) Allowed origin + const allowed = await fetch(base + "/api/meters", { + headers: { Origin: FRONTEND }, + }) + console.log("Allowed origin status:", allowed.status) + const csp = allowed.headers.get("content-security-policy") + const nonce = allowed.headers.get("x-csp-nonce") + console.log("CSP header present:", !!csp) + console.log("X-CSP-Nonce present:", !!nonce) + + if (allowed.status !== 200) throw new Error("Allowed origin request failed") + if (!csp) throw new Error("CSP header missing") + if (!nonce) throw new Error("X-CSP-Nonce missing") + + // 2) Disallowed origin + const disallowed = await fetch(base + "/api/meters", { + headers: { Origin: "https://evil.com" }, + }) + console.log("Disallowed origin status:", disallowed.status) + if (disallowed.status !== 403) + throw new Error("Disallowed origin not rejected") + + // 3) Missing origin -> should be rejected now + const missing = await fetch(base + "/api/meters") + console.log("Missing origin status:", missing.status) + if (missing.status !== 403) + throw new Error("Request without Origin header should be rejected") + + console.log("\nAll tests passed") + } catch (err) { + console.error("Test failure:", err) + process.exitCode = 1 + } finally { + server.close() + } +} + +run()