




















@@ -414,10 +414,90 @@ describe("export html security hardening", () => {
414414requireElement(messages.querySelector(`img[src="${dataImage}"]`), "data markdown image missing");
415415});
416416417+it("flattens unsafe markdown links while preserving safe links", async () => {
418+const session: SessionData = {
419+header: { id: "session-5", timestamp: now() },
420+entries: [
421+{
422+id: "1",
423+parentId: null,
424+timestamp: now(),
425+type: "message",
426+message: {
427+role: "user",
428+content: [
429+"[script](javascript:alert(1))",
430+"[encoded](javascript:alert(2))",
431+"[split](java	script:alert(3))",
432+"[zero-width](java​script:alert(4))",
433+"[surrogate](java�script:alert(5))",
434+'[safe](https://example.com/report "report")',
435+].join("\n"),
436+},
437+},
438+{
439+id: "2",
440+parentId: "1",
441+timestamp: now(),
442+type: "message",
443+message: {
444+role: "assistant",
445+content: [
446+{
447+type: "text",
448+text: "[data](data:text/html;base64,PGgxPnBvYzwvaDE+) [mail](mailto:test@example.com)",
449+},
450+],
451+},
452+},
453+{
454+id: "3",
455+parentId: "2",
456+timestamp: now(),
457+type: "branch_summary",
458+summary: "[relative](./notes.md)",
459+},
460+{
461+id: "4",
462+parentId: "3",
463+timestamp: now(),
464+type: "custom_message",
465+customType: "x",
466+display: true,
467+content: "[hash](#entry-1)",
468+},
469+],
470+leafId: "4",
471+systemPrompt: "",
472+tools: [],
473+};
474+475+const { document } = await renderTemplate(session);
476+const messages = requireElement(document.getElementById("messages"), "messages root missing");
477+const hrefs = Array.from(messages.querySelectorAll("a"), (link) => link.getAttribute("href"));
478+479+expect(hrefs).toEqual([
480+"https://example.com/report",
481+"mailto:test@example.com",
482+"./notes.md",
483+"#entry-1",
484+]);
485+expect(messages.querySelector("a")?.getAttribute("title")).toBe("report");
486+expect(messages.textContent).toContain("script");
487+expect(messages.textContent).toContain("encoded");
488+expect(messages.textContent).toContain("split");
489+expect(messages.textContent).toContain("zero-width");
490+expect(messages.textContent).toContain("surrogate");
491+expect(messages.textContent).toContain("data");
492+expect(hrefs.some((href) => href?.startsWith("javascript:") || href?.startsWith("data:"))).toBe(
493+false,
494+);
495+});
496+417497it("escapes markdown data-image attributes", async () => {
418498const dataImage = "data:image/png;base64,AAAA";
419499const session: SessionData = {
420-header: { id: "session-5", timestamp: now() },
500+header: { id: "session-6", timestamp: now() },
421501entries: [
422502{
423503id: "1",
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。