import { gzipSync } from "node:zlib";
import { describe, expect, it, vi } from "vitest";
vi.mock("../../../apps/api/src/config.js", () => ({
env: { MAX_SVG_SIZE_MB: 10 },
}));
import {
decompressSvgz,
isSvgBuffer,
sanitizeSvg,
} from "../../../apps/api/src/lib/svg-sanitize.js";
describe("sanitizeSvg", () => {
it("removes DOCTYPE declarations", () => {
const input = Buffer.from(
'',
);
const result = sanitizeSvg(input).toString("utf-8");
expect(result).not.toContain("DOCTYPE");
expect(result).toContain("',
);
const result = sanitizeSvg(input).toString("utf-8");
expect(result).not.toContain("data:text/html");
});
it("blocks file: URIs", () => {
const input = Buffer.from('');
const result = sanitizeSvg(input).toString("utf-8");
expect(result).not.toContain("file:///etc/passwd");
});
it("blocks external url() references", () => {
const input = Buffer.from("");
const result = sanitizeSvg(input).toString("utf-8");
expect(result).not.toContain("https://evil.com");
});
it("blocks file: url() references", () => {
const input = Buffer.from('');
const result = sanitizeSvg(input).toString("utf-8");
expect(result).not.toContain("file:///etc/passwd");
});
it("removes XInclude elements", () => {
const input = Buffer.from(
'',
);
const result = sanitizeSvg(input).toString("utf-8");
expect(result).not.toContain("xi:include");
});
it("preserves valid SVG content", () => {
const input = Buffer.from(
'',
);
const result = sanitizeSvg(input).toString("utf-8");
expect(result).toContain(" {
const configMod = await import("../../../apps/api/src/config.js");
const saved = configMod.env.MAX_SVG_SIZE_MB;
configMod.env.MAX_SVG_SIZE_MB = 0.000001;
try {
const input = Buffer.from('');
expect(() => sanitizeSvg(input)).toThrow("SVG exceeds maximum size");
} finally {
configMod.env.MAX_SVG_SIZE_MB = saved;
}
});
it("does NOT remove xml processing instruction", () => {
const input = Buffer.from('');
const result = sanitizeSvg(input).toString("utf-8");
expect(result).toContain(" {
it("returns non-gzipped buffer unchanged", () => {
const input = Buffer.from('');
const result = decompressSvgz(input);
expect(result).toBe(input);
});
it("decompresses valid SVGZ content", () => {
const svgContent =
'';
const compressed = gzipSync(Buffer.from(svgContent));
const result = decompressSvgz(compressed);
expect(result.toString("utf-8")).toBe(svgContent);
});
it("throws on decompressed content that is not SVG", () => {
const notSvg = gzipSync(Buffer.from("this is just plain text, not SVG at all"));
expect(() => decompressSvgz(notSvg)).toThrow("does not contain valid SVG");
});
it("returns short buffer unchanged (less than 2 bytes)", () => {
const tiny = Buffer.from([0x42]);
expect(decompressSvgz(tiny)).toBe(tiny);
});
it("returns buffer unchanged when first bytes do not match gzip magic", () => {
const notGzip = Buffer.from([0x50, 0x4b, 0x03, 0x04]);
expect(decompressSvgz(notGzip)).toBe(notGzip);
});
});
describe("isSvgBuffer", () => {
it("returns true for buffer starting with {
const input = Buffer.from('');
expect(isSvgBuffer(input)).toBe(true);
});
it("returns true for buffer starting with {
const input = Buffer.from('');
expect(isSvgBuffer(input)).toBe(true);
});
it("returns false for PNG buffer", () => {
const png = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
expect(isSvgBuffer(png)).toBe(false);
});
it("returns false for empty buffer", () => {
expect(isSvgBuffer(Buffer.alloc(0))).toBe(false);
});
it("returns false for plain text buffer", () => {
const text = Buffer.from("Hello, this is not an SVG file");
expect(isSvgBuffer(text)).toBe(false);
});
it("returns true for SVG with leading whitespace", () => {
const input = Buffer.from(' \n ');
expect(isSvgBuffer(input)).toBe(true);
});
it("returns true for {
const input = Buffer.from('\n');
expect(isSvgBuffer(input)).toBe(true);
});
});