import { describe, expect, it } from "vitest"; import { buildCspHeader, buildServeJson, injectCspMetaIntoHtml, toOrigin } from "./vite-plugin-csp"; /** Petite fabrique de HTML représentative de l'index de l'app. */ const sampleIndexHtml = ` Bento
`; describe("CSP HTML injection (Gherkin @int-csp-html-injection)", () => { it("le HTML de production est instrumenté avec une meta CSP durcie", () => { const csp = buildCspHeader("production", null); const out = injectCspMetaIntoHtml(sampleIndexHtml, csp); // 1) Présence de la balise meta (regex non gourmande sur la valeur). expect(out).toMatch(/ { it("avec VITE_MATOMO_URL=https://analytics.example.com/, l'origine est ajoutée aux directives externes", () => { const origin = toOrigin("https://analytics.example.com/"); expect(origin).toBe("https://analytics.example.com"); const csp = buildCspHeader("production", origin); expect(csp).toMatch(/script-src 'self' 'https:\/\/analytics\.example\.com'/); expect(csp).toMatch(/connect-src 'self' 'https:\/\/analytics\.example\.com'/); expect(csp).toMatch(/img-src 'self' 'https:\/\/analytics\.example\.com'/); // default-src ne s'élargit pas avec Matomo. expect(csp).toMatch(/default-src 'self'/); }); }); describe("serve.json headers (Gherkin @int-csp-serve-json)", () => { it("déclare la CSP sur les HTML et les en-têtes statiques sur tout le contenu", () => { const sj = buildServeJson(null); const htmlRule = sj.headers.find((r) => r.source === "**/*.html"); expect(htmlRule).toBeDefined(); const byKey = (key: string) => htmlRule!.headers.find((h) => h.key === key); expect(byKey("Content-Security-Policy")?.value).toBeDefined(); expect(byKey("X-Content-Type-Options")?.value).toBe("nosniff"); expect(byKey("X-Frame-Options")?.value).toBe("DENY"); expect(byKey("Referrer-Policy")?.value).toBe("strict-origin-when-cross-origin"); const pp = byKey("Permissions-Policy")?.value ?? ""; expect(pp).toContain("camera=()"); expect(pp).toContain("microphone=()"); expect(pp).toContain("geolocation=()"); }); });