import { readFileSync } from "node:fs"; import { join } from "node:path"; import { describe, expect, it } from "vitest"; import { sanitizeSvg } from "../../apps/api/src/lib/svg-sanitize.js"; const FIXTURES_DIR = join(__dirname, "../fixtures/security"); function loadFixture(name: string): Buffer { return readFileSync(join(FIXTURES_DIR, name)); } function sanitize(name: string): string { return sanitizeSvg(loadFixture(name)).toString("utf-8"); } describe("SVG sanitizer -- attack payload fixtures", () => { it("strips "); expect(result).not.toContain("alert(1)"); // SVG wrapper should survive expect(result).toContain(" { const result = sanitize("svg-xss-event-handler.svg"); expect(result).not.toMatch(/\bonload\s*=/i); // The event handler is replaced with data-removed="" (value stripped) expect(result).toContain('data-removed=""'); // The payload text should not appear in any executable context expect(result).not.toMatch(/on\w+\s*=\s*["']alert/i); }); it("removes DOCTYPE with XXE file-read entity (svg-xxe-file-read.svg)", () => { const result = sanitize("svg-xxe-file-read.svg"); expect(result).not.toMatch(/ { const result = sanitize("svg-xxe-ssrf.svg"); expect(result).not.toMatch(/ { const result = sanitize("svg-foreign-object.svg"); expect(result).not.toContain(""); expect(result).not.toContain(" { const result = sanitize("svg-data-uri.svg"); // The data:text/html payload should be neutralized expect(result).not.toMatch(/href\s*=\s*["']data:text\/html/i); expect(result).not.toContain(""); }); it("strips XInclude elements and namespace (svg-xinclude.svg)", () => { const result = sanitize("svg-xinclude.svg"); expect(result).not.toContain("xi:include"); expect(result).not.toContain("xmlns:xi"); expect(result).not.toContain("file:///etc/passwd"); }); it("strips CDATA sections to prevent script bypass (svg-cdata-bypass.svg)", () => { const result = sanitize("svg-cdata-bypass.svg"); expect(result).not.toContain("CDATA"); expect(result).not.toContain("alert(document.cookie)"); // Script tags should also be removed expect(result).not.toContain(" { const result = sanitize("svg-entity-bypass.svg"); // After entity decoding, javascript: should be caught and neutralized expect(result).not.toMatch(/href\s*=\s*["']javascript:/i); // The javascript: scheme must be gone (replaced with safe data:, prefix) expect(result).not.toContain("javascript:"); }); it("strips elements that inject URIs (svg-animate-inject.svg)", () => { const result = sanitize("svg-animate-inject.svg"); expect(result).not.toContain(" elements that inject attributes (svg-set-inject.svg)", () => { const result = sanitize("svg-set-inject.svg"); expect(result).not.toContain(" { it("preserves a minimal clean SVG unchanged", () => { const clean = ''; const result = sanitizeSvg(Buffer.from(clean)).toString("utf-8"); expect(result).toBe(clean); }); it("preserves internal CSS styles", () => { const clean = ''; const result = sanitizeSvg(Buffer.from(clean)).toString("utf-8"); expect(result).toContain("