New API has Potential XSS in its MarkdownRenderer component
Description
New API is a large language mode (LLM) gateway and artificial intelligence (AI) asset management system. Prior to version 0.10.8-alpha.9, a potential unsafe operation occurs in component MarkdownRenderer.jsx, allowing for Cross-Site Scripting(XSS) when the model outputs items containing `` tag. Version 0.10.8-alpha.9 fixes the issue.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
New API prior to 0.10.8-alpha.9 has a stored XSS vulnerability in MarkdownRenderer.jsx due to unsafe use of dangerouslySetInnerHTML.
Vulnerability
Overview
CVE-2026-25802 is a cross-site scripting (XSS) vulnerability in New API, an LLM gateway and AI asset management system. The flaw resides in the MarkdownRenderer.jsx component, which uses dangerouslySetInnerHTML to render AI-generated HTML content without sanitization [1][2]. This allows an attacker to inject arbitrary JavaScript, such as `` tags, into the rendered output.
Exploitation
An attacker can exploit this by crafting a prompt that causes the LLM to output to include a `` tag. For example, asking the model to write a script that redirects to an external site will result in the script being executed in the user's browser when the response is rendered in the playground [2]. The attack requires no special privileges beyond access to the playground interface, and the malicious script is stored in the chat history, meaning it will re-execute every time the page is loaded [2].
Impact
Successful exploitation leads to arbitrary JavaScript execution in the context of the victim's session. This can be used to steal session cookies, perform actions on behalf of the user, or redirect to phishing pages. The stored nature of the XSS makes it particularly dangerous, as users cannot easily remove the malicious content from their history [2].
Mitigation
The issue is fixed in version 0.10.8-alpha.9. The fix introduces an escapeHtml function and sanitizes AI-generated HTML before rendering, preventing script injection [4]. Users should upgrade immediately. As a workaround, the advisory, the vendor suggests rendering previews in an iframe sandbox or purifying dangerous HTML strings [2].
- GitHub - QuantumNous/new-api: A unified AI model hub for aggregation & distribution. It supports cross-converting various LLMs into OpenAI-compatible, Claude-compatible, or Gemini-compatible formats. A centralized gateway for personal and enterprise model management. 🍥
- Potential XSS occurs in component MarkdownRenderer
- 🔒 fix(security): sanitize AI-generated HTML to prevent XSS in playground · QuantumNous/new-api@ab5456e
AI Insight generated on May 19, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/QuantumNous/new-apiGo | < 0.10.8-alpha.9 | 0.10.8-alpha.9 |
Affected products
2- Range: <0.10.8-alpha.9
- QuantumNous/new-apiv5Range: < 0.10.8-alpha.9
Patches
1ab5456eb1049🔒 fix(security): sanitize AI-generated HTML to prevent XSS in playground
2 files changed · +82 −16
web/src/components/common/markdown/MarkdownRenderer.jsx+44 −1 modified@@ -93,6 +93,49 @@ export function Mermaid(props) { ); } +function SandboxedHtmlPreview({ code }) { + const iframeRef = useRef(null); + const [iframeHeight, setIframeHeight] = useState(150); + + useEffect(() => { + const iframe = iframeRef.current; + if (!iframe) return; + + const handleLoad = () => { + try { + const doc = iframe.contentDocument || iframe.contentWindow?.document; + if (doc) { + const height = + doc.documentElement.scrollHeight || doc.body.scrollHeight; + setIframeHeight(Math.min(Math.max(height + 16, 60), 600)); + } + } catch { + // sandbox restrictions may prevent access, that's fine + } + }; + + iframe.addEventListener('load', handleLoad); + return () => iframe.removeEventListener('load', handleLoad); + }, [code]); + + return ( + <iframe + ref={iframeRef} + sandbox='allow-same-origin' + srcDoc={code} + title='HTML Preview' + style={{ + width: '100%', + height: `${iframeHeight}px`, + border: 'none', + overflow: 'auto', + backgroundColor: '#fff', + borderRadius: '4px', + }} + /> + ); +} + export function PreCode(props) { const ref = useRef(null); const [mermaidCode, setMermaidCode] = useState(''); @@ -227,7 +270,7 @@ export function PreCode(props) { > HTML预览: </div> - <div dangerouslySetInnerHTML={{ __html: htmlCode }} /> + <SandboxedHtmlPreview code={htmlCode} /> </div> )} </>
web/src/components/playground/CodeViewer.jsx+38 −15 modified@@ -91,22 +91,45 @@ const codeThemeStyles = { }, }; +const escapeHtml = (str) => { + return str + .replace(/&/g, '&') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +}; + const highlightJson = (str) => { - return str.replace( - /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?)/g, - (match) => { - let color = '#b5cea8'; - if (/^"/.test(match)) { - color = /:$/.test(match) ? '#9cdcfe' : '#ce9178'; - } else if (/true|false|null/.test(match)) { - color = '#569cd6'; - } - return `<span style="color: ${color}">${match}</span>`; - }, - ); + const tokenRegex = + /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?)/g; + + let result = ''; + let lastIndex = 0; + let match; + + while ((match = tokenRegex.exec(str)) !== null) { + // Escape non-token text (structural chars like {, }, [, ], :, comma, whitespace) + result += escapeHtml(str.slice(lastIndex, match.index)); + + const token = match[0]; + let color = '#b5cea8'; + if (/^"/.test(token)) { + color = /:$/.test(token) ? '#9cdcfe' : '#ce9178'; + } else if (/true|false|null/.test(token)) { + color = '#569cd6'; + } + // Escape token content before wrapping in span + result += `<span style="color: ${color}">${escapeHtml(token)}</span>`; + lastIndex = tokenRegex.lastIndex; + } + + // Escape remaining text + result += escapeHtml(str.slice(lastIndex)); + return result; }; -const linkRegex = /(https?:\/\/[^\s<"'\]),;}]+)/g; +const linkRegex = /(https?:\/\/(?:[^\s<"'\]),;&}]|&)+)/g; const linkifyHtml = (html) => { const parts = html.split(/(<[^>]+>)/g); @@ -184,14 +207,14 @@ const CodeViewer = ({ content, title, language = 'json' }) => { const highlightedContent = useMemo(() => { if (contentMetrics.isVeryLarge && !isExpanded) { - return displayContent; + return escapeHtml(displayContent); } if (isJsonLike(displayContent, language)) { return highlightJson(displayContent); } - return displayContent; + return escapeHtml(displayContent); }, [displayContent, language, contentMetrics.isVeryLarge, isExpanded]); const renderedContent = useMemo(() => {
Vulnerability mechanics
Synthesis attempt was rejected by the grounding validator. Re-run pending.
References
5- github.com/advisories/GHSA-299v-8pq9-5gjqghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-25802ghsaADVISORY
- github.com/QuantumNous/new-api/commit/ab5456eb1049aa8a0f3e51f359907ec7fff38b4bghsax_refsource_MISCWEB
- github.com/QuantumNous/new-api/security/advisories/GHSA-299v-8pq9-5gjqghsax_refsource_CONFIRMWEB
- pkg.go.dev/vuln/GO-2026-4532ghsaWEB
News mentions
0No linked articles in our index yet.