Skip to content
lightragGHSA-xpjq-3w4w-w5wr

lightrag-hku: Stored Cross-Site Scripting (XSS) in the LightRAG WebUI chat/answer renderer via ingested content

Medium6.1CVE-2026-86062 · Published Sep 22, 2026

GitHub advisory

Affected versions

PackageAffectedFixed in
lightrag-hku
PyPI
< 1.5.51.5.5
Details and references

### Summary The LightRAG WebUI renders assistant/answer chat content as **raw HTML** , `react-markdown` is configured with `rehypePlugins={[rehypeRaw]}` and `skipHtml={false}` and **no** HTML sanitizer (`rehype-sanitize`), element allow-list, or custom `urlTransform`. Because answer content is derived from user-ingested documents, an attacker who can add a single document can store an HTML/JavaScript payload that executes in the browser of any user who later retrieves it (typically an administrator), leading to auth-token theft from `localStorage` and full API takeover. No authentication is required in the default configuration. ### Details Sink , `lightrag_webui/src/components/retrieval/ChatMessage.tsx`: - Main answer (`MessageMarkdown`, lines ~348-351) and thinking content (lines ~252-272) render with `rehypePlugins={[rehypeRaw, …]}` and `skipHtml={false}`. The `components` map (lines ~111-156) only restyles safe formatting tags (`p`, `h1`–`h4`, `ul`, `ol`, `li`, `code`); there is no `rehype-sanitize`, no `allowedElements`/`disallowedElements`, and no custom `urlTransform`. - Second sink: mermaid is initialized with `securityLevel: 'loose'` (line ~433) and the rendered SVG is injected via `container.innerHTML = svg` (line ~483) + `bindFunctions(container)`. `'loose'` disables mermaid's output sanitization, so a ` ```mermaid ` block in answer content (HTML label / `click` directive) is an additional script-execution path. - Hardening (not code execution): KaTeX is set with `trust: true` (lines ~261/~359). `\href{javascript:…}` is blocked by React 19, but `\includegraphics{URL}` renders a live remote `<img src>` (arbitrary external resource load from the victim's browser). Recommend `trust: false`. Source → sink: `POST /documents/text` or `POST /documents/upload` stores the document → `POST /query` returns it (verbatim when `only_need_context=true`, `lightrag/api/routers/query_routes.py:27`; otherwise echoed by the LLM) → the response is streamed into `assistantMessage.content` (`lightrag_webui/src/features/RetrievalView.tsx:340`) → rendered by the sink above. react-markdown's built-in defenses do NOT cover this: it sanitizes `href`/`src` URLs (so `javascript:` links are blocked) and React ignores string event handlers (so `<img onerror>` is dropped), but raw elements such as `<iframe srcdoc="…">` and `<svg><script>` are rendered unchanged and execute. ### PoC Benign, local-only. Tested at commit `f3378a3` (v1.5.5) with `react@19`, `react-markdown@10.1.0`, `rehype-raw@7.0.0`. **Fastest check (code review, ~10s):** in `ChatMessage.tsx`, the `<ReactMarkdown>` that renders answers uses `rehypePlugins={[rehypeRaw, …]}` with `skipHtml={false}` and no `rehype-sanitize` / allow-list. Per react-markdown's own documentation, `rehype-raw` on untrusted input without `rehype-sanitize` allows HTML injection , that is the vulnerability. **Runnable proof (~2 min) , reproduces the exact renderer config and shows it execute in a browser:** ```bash mkdir xss-check && cd xss-check npm init -y npm install react@19 react-dom@19 react-markdown@10 rehype-raw@7 # save the script below as poc.mjs, then: node poc.mjs # open the generated poc.html in any browser (or headless): # msedge --headless=new --dump-dom "file:///ABS/PATH/poc.html" ``` `poc.mjs`: ```js import React from 'react'; import { renderToStaticMarkup } from 'react-dom/server'; import ReactMarkdown from 'react-markdown'; import rehypeRaw from 'rehype-raw'; import { writeFileSync } from 'fs'; // Stands in for an assistant answer built from an ingested document. const answer = `<iframe srcdoc="<script>` + `var h=parent.document.createElement('h1');h.style.color='red';` + `h.textContent='XSS EXECUTED on '+(parent.document.domain||'this page');` + `parent.document.body.appendChild(h);parent.document.title='XSS-EXECUTED';` + `<\/script>"></iframe>`; // EXACT options from ChatMessage.tsx (rehypeRaw + skipHtml:false, no sanitizer): const body = renderToStat

CVSS 3.1
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
Severity from
GitHub (reviewed advisory)
Weakness
CWE-79
Also known as
CVE-2026-86062

More lightrag advisories

All

Critical advisories by email

Wednesdays: the week’s critical and high advisories in the AI and data stack, with the fixed versions. Only in weeks that have some.

Double opt-in. Unsubscribe any time.