CVE-2026-41318
Description
AnythingLLM is an application that turns pieces of content into context that any LLM can use as references during chatting. Prior to version 1.12.1, AnythingLLM's in-chat markdown renderer has an unsafe custom rule for images that interpolates the markdown image's alt text into an HTML alt="..." attribute without any HTML encoding. Every call-site in the app wraps renderMarkdown(...) with DOMPurify.sanitize(...) as defense-in-depth — except the Chartable component, which renders chart captions with no sanitization. The chart caption is the natural-language text the LLM emits around a create-chart tool call, so any attacker who can influence the LLM's output — most cheaply via indirect prompt injection in a shared workspace document, or directly if they can create a chart record in a multi-user workspace — can trigger stored DOM-level XSS in every other user's browser when they open that conversation. AnythingLLM chat history is loaded server-side via GET /api/workspace/:slug/chats and rendered directly into the chat UI. Version 1.12.1 contains a patch for this issue.
Affected products
1Patches
1f5fa03f4728eMerge commit from fork
2 files changed · +5 −4
frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/Chartable/index.jsx+3 −2 modified@@ -31,6 +31,7 @@ import CustomCell from "./CustomCell.jsx"; import Tooltip from "./CustomTooltip.jsx"; import { safeJsonParse } from "@/utils/request.js"; import renderMarkdown from "@/utils/chat/markdown.js"; +import DOMPurify from "dompurify"; import { memo, useCallback, useState } from "react"; import { saveAs } from "file-saver"; import { useGenerateImage } from "recharts-to-png"; @@ -394,7 +395,7 @@ export function Chartable({ props }) { <span className="flex flex-col gap-y-1 mt-2" dangerouslySetInnerHTML={{ - __html: renderMarkdown(content.caption), + __html: DOMPurify.sanitize(renderMarkdown(content.caption)), }} /> </div> @@ -413,7 +414,7 @@ export function Chartable({ props }) { <span className="flex flex-col gap-y-1 mt-2" dangerouslySetInnerHTML={{ - __html: renderMarkdown(content.caption), + __html: DOMPurify.sanitize(renderMarkdown(content.caption)), }} /> </div>
frontend/src/utils/chat/markdown.js+2 −2 modified@@ -63,7 +63,7 @@ markdown.renderer.rules.strong_close = () => "</strong>"; markdown.renderer.rules.link_open = (tokens, idx) => { const token = tokens[idx]; const href = token.attrs.find((attr) => attr[0] === "href"); - return `<a href="${href[1]}" target="_blank" rel="noopener noreferrer">`; + return `<a href="${HTMLEncode(href[1])}" target="_blank" rel="noopener noreferrer">`; }; // Custom renderer for responsive images rendered in markdown @@ -73,7 +73,7 @@ markdown.renderer.rules.image = function (tokens, idx) { const src = token.attrs[srcIndex][1]; const alt = token.content || ""; - return `<div class="w-full max-w-[800px]"><img src="${src}" alt="${alt}" class="w-full h-auto" /></div>`; + return `<div class="w-full max-w-[800px]"><img src="${HTMLEncode(src)}" alt="${HTMLEncode(alt)}" class="w-full h-auto" /></div>`; }; markdown.use(markdownItKatexPlugin);
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
2News mentions
0No linked articles in our index yet.