Files
Checkmate/server/test/unit/validation/statusPageValidation.test.ts
Buco7854 3685e0c476 feat: reject custom CSS that references external resources
Reject status page custom CSS containing @import or a url() / image-set()
/ image() / cross-fade() whose target is http(s) or protocol-relative,
while allowing data: and relative URLs. The check normalizes comments and
CSS escapes first so obfuscated forms cannot slip past. Enforced in the
create/update body validation.
2026-06-22 23:49:58 +00:00

64 lines
2.3 KiB
TypeScript

import { describe, expect, it } from "@jest/globals";
import { createStatusPageBodyValidation } from "../../../src/api/validation/statusPageValidation.ts";
const baseCreateBody = (overrides: Record<string, unknown> = {}) => ({
type: "uptime",
companyName: "Test Co",
url: "my-status-page",
monitors: ["0123456789abcdef01234567"],
isPublished: true,
showUptimePercentage: true,
...overrides,
});
describe("createStatusPageBodyValidation", () => {
it("accepts a valid body without customCSS", () => {
const result = createStatusPageBodyValidation.safeParse(baseCreateBody());
expect(result.success).toBe(true);
});
it("accepts and preserves customCSS", () => {
const customCSS = ".status-page > footer { display: none; }";
const result = createStatusPageBodyValidation.safeParse(baseCreateBody({ customCSS }));
expect(result.success).toBe(true);
if (result.success) {
expect(result.data.customCSS).toBe(customCSS);
}
});
it("accepts an empty customCSS so it can be cleared", () => {
const result = createStatusPageBodyValidation.safeParse(baseCreateBody({ customCSS: "" }));
expect(result.success).toBe(true);
if (result.success) {
expect(result.data.customCSS).toBe("");
}
});
it("rejects customCSS over the maximum length", () => {
const result = createStatusPageBodyValidation.safeParse(baseCreateBody({ customCSS: "a".repeat(100001) }));
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues).toEqual(
expect.arrayContaining([expect.objectContaining({ message: "Custom CSS must be at most 100000 characters", path: ["customCSS"] })])
);
}
});
it("rejects a non-string customCSS", () => {
const result = createStatusPageBodyValidation.safeParse(baseCreateBody({ customCSS: 42 }));
expect(result.success).toBe(false);
});
it("rejects customCSS that references an external resource", () => {
const result = createStatusPageBodyValidation.safeParse(baseCreateBody({ customCSS: 'body{background:url("https://evil.example/beacon")}' }));
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues).toEqual(
expect.arrayContaining([
expect.objectContaining({ message: "Custom CSS cannot reference external URLs or use @import", path: ["customCSS"] }),
])
);
}
});
});