first commit
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
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 = `<!doctype html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Bento</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
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(/<meta\s+http-equiv="Content-Security-Policy"\s+content="[^"]+"/);
|
||||
// 2) Vérification du contenu de la CSP via les directives clés.
|
||||
const cspValue = out.match(/content="([^"]+)"/)?.[1] ?? "";
|
||||
expect(cspValue).toContain("default-src 'self'");
|
||||
expect(cspValue).toContain("frame-ancestors 'none'");
|
||||
expect(cspValue).toContain("object-src 'none'");
|
||||
// En production, aucune directive n'autorise unsafe-eval (HMR Vite ne sert qu'en dev).
|
||||
expect(out).not.toContain("unsafe-eval");
|
||||
});
|
||||
});
|
||||
|
||||
describe("CSP adapts to Matomo origin (Gherkin @int-csp-matomo-origin)", () => {
|
||||
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=()");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user