From fc2d2d595cccd6046d73f42f3d35800695f7eb85 Mon Sep 17 00:00:00 2001 From: Bryan Tegomoh Date: Mon, 25 May 2026 12:51:35 -0500 Subject: [PATCH] fix(ui): keep local file markdown links inert --- ui/src/ui/markdown.test.ts | 14 ++++++++++++++ ui/src/ui/markdown.ts | 11 +++++++++++ 2 files changed, 25 insertions(+) diff --git a/ui/src/ui/markdown.test.ts b/ui/src/ui/markdown.test.ts index 050dd52a166..a600eb49bd6 100644 --- a/ui/src/ui/markdown.test.ts +++ b/ui/src/ui/markdown.test.ts @@ -569,6 +569,20 @@ PY const html = toSanitizedMarkdownHtml("[click](file:///etc/passwd)"); expect(html).toBe("

click

\n"); }); + + it("strips href from host-local absolute file paths", () => { + const html = toSanitizedMarkdownHtml( + "[report.docx](/Users/test/.openclaw/data/skills/output/report.docx)", + ); + expect(html).toBe("

report.docx

\n"); + }); + + it("keeps app-relative links navigable", () => { + const html = toSanitizedMarkdownHtml("[usage](/usage)"); + expect(html).toBe( + '

usage

\n', + ); + }); }); describe("ReDoS protection", () => { diff --git a/ui/src/ui/markdown.ts b/ui/src/ui/markdown.ts index 7ec0461d28f..3f0d64ac197 100644 --- a/ui/src/ui/markdown.ts +++ b/ui/src/ui/markdown.ts @@ -84,6 +84,8 @@ const MARKDOWN_PARSE_LIMIT = 40_000; const MARKDOWN_CACHE_LIMIT = 200; const MARKDOWN_CACHE_MAX_CHARS = 50_000; const INLINE_DATA_IMAGE_RE = /^data:image\/[a-z0-9.+-]+;base64,/i; +const HOST_LOCAL_FILE_HREF_RE = + /^(?:~\/|\/(?:Users|home|tmp|private\/tmp|var\/folders|private\/var\/folders)\/|\/[A-Za-z]:\/|[A-Za-z]:[\\/])/; const markdownCache = new Map(); const TAIL_LINK_BLUR_CLASS = "chat-link-tail-blur"; @@ -135,6 +137,10 @@ function shouldRenderCodeBlockCopy(env: unknown): boolean { return (env as Partial | undefined)?.codeBlockChrome !== "none"; } +function isHostLocalFileHref(href: string): boolean { + return HOST_LOCAL_FILE_HREF_RE.test(href.trim()); +} + function installHooks() { if (hooksInstalled) { return; @@ -150,6 +156,11 @@ function installHooks() { return; } + if (isHostLocalFileHref(href)) { + node.removeAttribute("href"); + return; + } + // Block dangerous URL schemes (javascript:, data:, vbscript:, etc.) try { const url = new URL(href, window.location.href);