Detect custom domains by comparing the request host to the app host and apply the tightened CSP there too, not just on the public path. Run the middleware before express.static so static-served root documents get the header, and keep helmet after static so its default-src does not block custom domain API calls.
57 lines
2.1 KiB
TypeScript
57 lines
2.1 KiB
TypeScript
import { describe, expect, it, jest } from "@jest/globals";
|
|
import type { NextFunction, Request, Response } from "express";
|
|
import { createStatusPageDocumentCsp } from "../../../src/api/middleware/statusPageDocumentCsp.ts";
|
|
|
|
const makeRes = (): Response => {
|
|
const res = {} as Response;
|
|
res.append = jest.fn().mockReturnValue(res) as unknown as Response["append"];
|
|
return res;
|
|
};
|
|
|
|
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];
|
|
|
|
describe("statusPageDocumentCsp", () => {
|
|
it("appends a tightened CSP on the public status page path", () => {
|
|
const { res, next } = run({ path: "/status/public/my-status-page", hostname: "localhost" });
|
|
|
|
expect(res.append).toHaveBeenCalledTimes(1);
|
|
const value = appendedValue(res);
|
|
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);
|
|
});
|
|
|
|
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" });
|
|
|
|
expect(res.append).not.toHaveBeenCalled();
|
|
expect(next).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|