import { readFileSync } from "fs"; import { describe, expect, it } from "vitest"; describe("export HTML markdown link sanitization", () => { const templateJs = readFileSync(new URL("../src/core/export-html/template.js", import.meta.url), "utf-8"); it("overrides the marked link renderer to block javascript: protocol", () => { expect(templateJs).toMatch(/link\s*\(\s*token\s*\)/); expect(templateJs).toMatch(/javascript/i); expect(templateJs).toMatch(/vbscript/i); }); it("overrides the marked image renderer to block javascript: protocol", () => { expect(templateJs).toMatch(/image\s*\(\s*token\s*\)/); }); it("escapes href attributes in the custom link renderer", () => { expect(templateJs).toMatch(/escapeHtml\(href\)/); }); it("escapes image mimeType attributes", () => { expect(templateJs).not.toMatch(/\$\{img\.mimeType\}/); expect(templateJs).toMatch(/escapeHtml\(img\.mimeType/); }); it("escapes image data attributes", () => { expect(templateJs).not.toMatch(/;base64,\$\{img\.data\}"/); expect(templateJs).toMatch(/;base64,\$\{escapeHtml\(img\.data \|\| (?:''|"")\)\}"/); }); it("escapes entry IDs before inserting them into attributes", () => { expect(templateJs).not.toMatch(/id="\$\{entryId\}"/); expect(templateJs).not.toMatch(/data-entry-id="\$\{entryId\}"/); expect(templateJs).toMatch(/entry-\$\{escapeHtml\(entry\.id\)\}/); expect(templateJs).toMatch(/data-entry-id="\$\{escapeHtml\(entryId\)\}"/); }); it("escapes tree metadata rendered from session fields", () => { expect(templateJs).not.toMatch(/\[\$\{msg\.toolName \|\| 'tool'\}\]/); expect(templateJs).not.toMatch(/\[\$\{msg\.role\}\]/); expect(templateJs).not.toMatch(/\[model: \$\{entry\.modelId\}\]/); expect(templateJs).not.toMatch(/\[thinking: \$\{entry\.thinkingLevel\}\]/); expect(templateJs).not.toMatch(/\[service tier: \$\{entry\.serviceTier\}\]/); expect(templateJs).not.toMatch(/\[\$\{entry\.type\}\]/); expect(templateJs).toMatch(/\$\{escapeHtml\(msg\.toolName \|\| 'tool'\)\}/); expect(templateJs).toMatch(/\$\{escapeHtml\(msg\.role\)\}/); expect(templateJs).toMatch(/\$\{escapeHtml\(entry\.modelId\)\}/); expect(templateJs).toMatch(/\$\{escapeHtml\(entry\.thinkingLevel\)\}/); expect(templateJs).toMatch(/\$\{escapeHtml\(entry\.serviceTier \|\| 'default'\)\}/); expect(templateJs).toMatch(/\$\{escapeHtml\(entry\.type\)\}/); }); it("escapes model names in the exported header", () => { expect(templateJs).not.toMatch(/\$\{globalStats\.models\.join\(', '\) \|\| 'unknown'\}/); expect(templateJs).toMatch(/\$\{escapeHtml\(globalStats\.models\.join\(', '\) \|\| 'unknown'\)\}/); }); });