From bc600d3f085802c45aa8f38c30e6e8c986bde6cc Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Mon, 27 Jul 2026 07:36:41 +0200 Subject: [PATCH] fix: escape KaTeX render-error fallback to prevent XSS via {@html} (#26718) KatexRenderer rendered the raw math source through {@html} whenever renderToString threw. throwOnError only suppresses KaTeX ParseError, so a RangeError (maximum call stack size exceeded, reachable with deeply-nested brace input) escaped into the catch and re-exposed the unescaped source. Because the math tokenizer captures everything between the delimiters verbatim, that source can carry an HTML/JS payload which then executed in the viewer's browser on the application origin, a stored, cross-user XSS reachable through normal chat/channel/shared-chat rendering. Escape the fallback so the source is shown as text and is never injected as HTML. Valid math is unaffected, it still renders through the success path. Co-authored-by: maxntv --- .../components/chat/Messages/Markdown/KatexRenderer.svelte | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lib/components/chat/Messages/Markdown/KatexRenderer.svelte b/src/lib/components/chat/Messages/Markdown/KatexRenderer.svelte index cfe7d8b1f7..63d2119cfd 100644 --- a/src/lib/components/chat/Messages/Markdown/KatexRenderer.svelte +++ b/src/lib/components/chat/Messages/Markdown/KatexRenderer.svelte @@ -38,7 +38,11 @@ try { renderedHTML = renderToString(content, { displayMode, throwOnError: false }); } catch { - renderedHTML = content; + // throwOnError only suppresses ParseError; RangeError on deep nesting escapes, so escape the fallback (never {@html} raw source) + renderedHTML = content + .replaceAll('&', '&') + .replaceAll('<', '<') + .replaceAll('>', '>'); } }