mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
Coverage 83.6 to 87.36% lines, 81.63 to 84.14% branches. Mutation testing across five packages: image-engine 85, media-engine 92, doc-engine 87, shared+enterprise 86, apps/api security and jobs slice. Runs all five lanes weekly. Fixes the silently-broken mutation CI (babel pin), a redact-pdf envelope-shape test bug, an untested enterprise license valid-signature path, and an audit test that only exercised a hand-copied reproduction. Test and config only, no product code changes beyond the babel pin and one test-only oidc export. Full suite: 16,712 pass, 0 fail.
107 lines
4.1 KiB
TypeScript
107 lines
4.1 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { buildCsp, getSecurityHeaders } from "../../../apps/api/src/lib/csp.js";
|
|
|
|
// Mutation-focused coverage for csp.ts. The sibling csp.test.ts asserts
|
|
// per-directive membership; these tests pin the EXACT header strings and the
|
|
// getSecurityHeaders map so string-literal, array-order, and array-removal
|
|
// mutants inside buildCsp (L20 connect-src) and getSecurityHeaders (L33-L40)
|
|
// have no equivalent survivors.
|
|
|
|
const CONNECT_SRC =
|
|
"'self' blob: data: https://us.i.posthog.com https://us-assets.i.posthog.com https://*.ingest.us.sentry.io";
|
|
|
|
const APP_CSP =
|
|
"default-src 'self'; " +
|
|
"script-src 'self' https://us-assets.i.posthog.com; " +
|
|
"style-src 'self' 'unsafe-inline'; " +
|
|
"img-src 'self' blob: data:; " +
|
|
"media-src 'self' blob:; " +
|
|
`connect-src ${CONNECT_SRC}; ` +
|
|
"font-src 'self' data:; " +
|
|
"object-src 'none'; " +
|
|
"frame-ancestors 'none'; " +
|
|
"base-uri 'self'; " +
|
|
"form-action 'self'";
|
|
|
|
const DOCS_CSP =
|
|
"default-src 'self'; " +
|
|
"script-src 'self' 'unsafe-inline' https://us-assets.i.posthog.com; " +
|
|
"style-src 'self' 'unsafe-inline'; " +
|
|
"img-src 'self' blob: data:; " +
|
|
"media-src 'self' blob:; " +
|
|
`connect-src ${CONNECT_SRC}; ` +
|
|
"font-src 'self' data:; " +
|
|
"object-src 'none'; " +
|
|
"base-uri 'self'; " +
|
|
"form-action 'self'";
|
|
|
|
describe("buildCsp exact header strings", () => {
|
|
it("returns the exact app policy for isDocs=false", () => {
|
|
expect(buildCsp(false)).toBe(APP_CSP);
|
|
});
|
|
|
|
it("returns the exact docs policy for isDocs=true", () => {
|
|
expect(buildCsp(true)).toBe(DOCS_CSP);
|
|
});
|
|
|
|
// Pins the L20 connect-src list: exact string kills any reorder, drop, or
|
|
// literal mutation of the POSTHOG/SENTRY origins or the blob:/data: entries.
|
|
it("builds the identical connect-src directive in both modes", () => {
|
|
expect(buildCsp(false)).toContain(`connect-src ${CONNECT_SRC};`);
|
|
expect(buildCsp(true)).toContain(`connect-src ${CONNECT_SRC};`);
|
|
});
|
|
|
|
// Pins the L22-L24 script-src ternary: the two branches must differ by exactly
|
|
// the 'unsafe-inline' token, so flipping the isDocs condition changes output.
|
|
it("docs script-src differs from app script-src by only 'unsafe-inline'", () => {
|
|
expect(buildCsp(true)).toContain(
|
|
"script-src 'self' 'unsafe-inline' https://us-assets.i.posthog.com;",
|
|
);
|
|
expect(buildCsp(false)).toContain("script-src 'self' https://us-assets.i.posthog.com;");
|
|
expect(buildCsp(false)).not.toBe(buildCsp(true));
|
|
});
|
|
|
|
// The L26 isDocs branch adds frame-ancestors only on the app policy.
|
|
it("emits frame-ancestors 'none' for the app policy but omits it for docs", () => {
|
|
expect(buildCsp(false)).toContain("frame-ancestors 'none';");
|
|
expect(buildCsp(true)).not.toContain("frame-ancestors");
|
|
});
|
|
});
|
|
|
|
describe("getSecurityHeaders exact map", () => {
|
|
it("returns every header with its exact value", () => {
|
|
expect(getSecurityHeaders()).toEqual({
|
|
"X-Content-Type-Options": "nosniff",
|
|
"X-Frame-Options": "DENY",
|
|
"X-XSS-Protection": "0",
|
|
"Referrer-Policy": "strict-origin-when-cross-origin",
|
|
"Permissions-Policy": "camera=(), microphone=(), geolocation=()",
|
|
"Strict-Transport-Security": "max-age=31536000; includeSubDomains",
|
|
"Content-Security-Policy": APP_CSP,
|
|
});
|
|
});
|
|
|
|
// Pins the buildCsp(false) argument on L41: the header must carry the app
|
|
// policy (with frame-ancestors), never the docs policy.
|
|
it("wires the Content-Security-Policy header to the app buildCsp(false) output", () => {
|
|
const headers = getSecurityHeaders();
|
|
expect(headers["Content-Security-Policy"]).toBe(buildCsp(false));
|
|
expect(headers["Content-Security-Policy"]).not.toBe(buildCsp(true));
|
|
expect(headers["Content-Security-Policy"]).toContain("frame-ancestors 'none'");
|
|
});
|
|
|
|
it("exposes exactly the seven expected header keys", () => {
|
|
expect(Object.keys(getSecurityHeaders()).sort()).toEqual(
|
|
[
|
|
"Content-Security-Policy",
|
|
"Permissions-Policy",
|
|
"Referrer-Policy",
|
|
"Strict-Transport-Security",
|
|
"X-Content-Type-Options",
|
|
"X-Frame-Options",
|
|
"X-XSS-Protection",
|
|
].sort(),
|
|
);
|
|
});
|
|
});
|