2026-06-23 19:25:40 +00:00
|
|
|
import { describe, expect, it, jest } from "@jest/globals";
|
|
|
|
|
import type { NextFunction, Request, Response } from "express";
|
2026-06-24 18:21:16 +00:00
|
|
|
import { createStatusPageDocumentCsp } from "../../../src/api/middleware/statusPageDocumentCsp.ts";
|
2026-06-23 19:25:40 +00:00
|
|
|
|
|
|
|
|
const makeRes = (): Response => {
|
|
|
|
|
const res = {} as Response;
|
|
|
|
|
res.append = jest.fn().mockReturnValue(res) as unknown as Response["append"];
|
|
|
|
|
return res;
|
|
|
|
|
};
|
|
|
|
|
|
2026-06-24 18:21:16 +00:00
|
|
|
const cspMiddleware = createStatusPageDocumentCsp("http://localhost:10001");
|
|
|
|
|
|
|
|
|
|
const run = (req: Partial<Request>) => {
|
|
|
|
|
const res = makeRes();
|
|
|
|
|
const next = jest.fn() as unknown as NextFunction;
|
|
|
|
|
cspMiddleware(req as Request, res, next);
|
|
|
|
|
return { res, next };
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const appendedValue = (res: Response): string => ((res.append as jest.Mock).mock.calls[0] as [string, string])[1];
|
|
|
|
|
|
2026-06-23 19:25:40 +00:00
|
|
|
describe("statusPageDocumentCsp", () => {
|
2026-06-24 18:21:16 +00:00
|
|
|
it("appends a tightened CSP on the public status page path", () => {
|
|
|
|
|
const { res, next } = run({ path: "/status/public/my-status-page", hostname: "localhost" });
|
2026-06-23 19:25:40 +00:00
|
|
|
|
|
|
|
|
expect(res.append).toHaveBeenCalledTimes(1);
|
2026-06-24 18:21:16 +00:00
|
|
|
const value = appendedValue(res);
|
2026-06-23 19:25:40 +00:00
|
|
|
expect(value).toContain("img-src 'self' data:");
|
|
|
|
|
expect(value).toContain("font-src 'self' data: https://fonts.gstatic.com");
|
|
|
|
|
expect(value).toContain("style-src 'self' 'unsafe-inline' https://fonts.googleapis.com");
|
|
|
|
|
expect(next).toHaveBeenCalledTimes(1);
|
|
|
|
|
});
|
|
|
|
|
|
2026-06-24 18:21:16 +00:00
|
|
|
it("does not restrict default-src or connect-src so the page can still reach the API", () => {
|
|
|
|
|
const { res } = run({ path: "/status/public/my-status-page", hostname: "localhost" });
|
|
|
|
|
|
|
|
|
|
const value = appendedValue(res);
|
|
|
|
|
expect(value).not.toContain("default-src");
|
|
|
|
|
expect(value).not.toContain("connect-src");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("appends the CSP on a custom domain document at the host root", () => {
|
|
|
|
|
const { res, next } = run({ path: "/", hostname: "status.example.com" });
|
|
|
|
|
|
|
|
|
|
expect(res.append).toHaveBeenCalledTimes(1);
|
|
|
|
|
expect(appendedValue(res)).toContain("img-src 'self' data:");
|
|
|
|
|
expect(next).toHaveBeenCalledTimes(1);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("leaves the app's own non-public routes untouched", () => {
|
|
|
|
|
const { res, next } = run({ path: "/status/create", hostname: "localhost" });
|
2026-06-23 19:25:40 +00:00
|
|
|
|
|
|
|
|
expect(res.append).not.toHaveBeenCalled();
|
|
|
|
|
expect(next).toHaveBeenCalledTimes(1);
|
|
|
|
|
});
|
|
|
|
|
});
|