From bebba124e884d47156fc08f8ac017f459b75bec6 Mon Sep 17 00:00:00 2001 From: 0xRain Date: Thu, 12 Feb 2026 07:40:40 +0800 Subject: [PATCH] fix(ui): escape raw HTML in chat messages instead of rendering it (#13952) Co-authored-by: 0xRaini <0xRaini@users.noreply.github.com> --- ui/src/ui/markdown.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ui/src/ui/markdown.ts b/ui/src/ui/markdown.ts index 5245b6f519..804c0933ae 100644 --- a/ui/src/ui/markdown.ts +++ b/ui/src/ui/markdown.ts @@ -112,7 +112,9 @@ export function toSanitizedMarkdownHtml(markdown: string): string { } return sanitized; } - const rendered = marked.parse(`${truncated.text}${suffix}`) as string; + const rendered = marked.parse(`${truncated.text}${suffix}`, { + renderer: htmlEscapeRenderer, + }) as string; const sanitized = DOMPurify.sanitize(rendered, { ALLOWED_TAGS: allowedTags, ALLOWED_ATTR: allowedAttrs, @@ -123,6 +125,13 @@ export function toSanitizedMarkdownHtml(markdown: string): string { return sanitized; } +// Prevent raw HTML in chat messages from being rendered as formatted HTML. +// Display it as escaped text so users see the literal markup. +// Security is handled by DOMPurify, but rendering pasted HTML (e.g. error +// pages) as formatted output is confusing UX (#13937). +const htmlEscapeRenderer = new marked.Renderer(); +htmlEscapeRenderer.html = ({ text }: { text: string }) => escapeHtml(text); + function escapeHtml(value: string): string { return value .replace(/&/g, "&")