CVE-2025-7969 Overview
CVE-2025-7969 is a Cross-Site Scripting (XSS) vulnerability affecting the markdown-it JavaScript library at version 14.1.0. The flaw resides in lib/renderer.mjs, the component responsible for rendering parsed Markdown into HTML. Attackers can craft Markdown input that survives neutralization and produces attacker-controlled script content in the generated web page [CWE-79]. The issue was published to the National Vulnerability Database on August 21, 2025 and cataloged under Fluid Attacks advisory FITO. The supplier does not consider this issue to be a vulnerability, and no vendor patch has been released.
Critical Impact
Successful exploitation permits execution of attacker-supplied script in the context of applications that render untrusted Markdown with markdown-it 14.1.0, enabling session theft, UI manipulation, and cross-origin data exposure.
Affected Products
- markdown-it version 14.1.0 (npm package)
- Applications embedding lib/renderer.mjs from markdown-it 14.1.0
- Web front-ends and server-side renderers that pass untrusted Markdown to markdown-it 14.1.0
Discovery Timeline
- 2025-08-21 - CVE-2025-7969 published to NVD
- 2026-06-17 - Last updated in NVD database
Technical Details for CVE-2025-7969
Vulnerability Analysis
The vulnerability is an Improper Neutralization of Input During Web Page Generation flaw in the rendering path of markdown-it. When user-supplied Markdown reaches lib/renderer.mjs, specific input constructs produce HTML output that includes script-executable content rather than escaped, inert text. Applications that accept Markdown from untrusted users, such as comment systems, wikis, chat platforms, and issue trackers, then serve this HTML to other users, propagating the injected payload. Because markdown-it runs on both browser and Node.js, the impact spans client-rendered single-page applications and server-rendered pipelines. The Fluid Attacks advisory documents the reproduction steps and the upstream discussion in GitHub issue #1122.
Root Cause
The defect stems from insufficient neutralization of specific input tokens during HTML serialization inside lib/renderer.mjs. The renderer emits attacker-controllable strings into contexts where they are interpreted as active markup rather than data. The maintainers of markdown-it treat this behavior as expected when HTML rendering is enabled or when sanitization is delegated to the caller, which is why the supplier does not classify it as a vulnerability.
Attack Vector
Exploitation is network-based and requires no privileges or user interaction beyond viewing the rendered content. An attacker submits crafted Markdown to any endpoint that passes input to markdown-it 14.1.0 for HTML generation. When a victim loads the rendered output, the injected payload executes in the victim's browser under the origin of the hosting application. The Fluid Attacks advisory and GitHub issue #1122 describe the specific Markdown constructs that trigger the unsafe output.
No verified public proof-of-concept code is provided in the reference set. For technical reproduction details, consult the Fluid Attacks Advisory: FITO and the upstream GitHub Issue #1122: markdown-it.
Detection Methods for CVE-2025-7969
Indicators of Compromise
- Rendered HTML from user-supplied Markdown containing <script>, javascript: URIs, or event-handler attributes such as onerror= and onload=.
- Outbound requests from user browsers to attacker-controlled domains immediately after loading Markdown-rendered pages.
- Unexpected DOM mutations, cookie access, or document.location changes originating from pages that render third-party Markdown.
Detection Strategies
- Inventory application dependencies and flag any project pinned to markdown-it@14.1.0 using npm ls markdown-it or a Software Composition Analysis tool.
- Deploy Content Security Policy (CSP) violation reporting to surface inline script execution attempts from Markdown-rendered views.
- Fuzz Markdown input surfaces with payloads referenced in GitHub issue #1122 and compare rendered output against an allow-list of safe HTML.
Monitoring Recommendations
- Log and review web application firewall (WAF) blocks on Markdown submission endpoints for XSS signatures.
- Alert on browser CSP report-uri or report-to events tied to pages rendering user Markdown.
- Correlate anomalous session-cookie exfiltration or account-takeover activity with recent Markdown content submissions.
How to Mitigate CVE-2025-7969
Immediate Actions Required
- Post-process markdown-it output through a strict HTML sanitizer such as DOMPurify before serving it to clients.
- Disable HTML passthrough in markdown-it by setting html: false when instantiating the parser for untrusted input.
- Enforce a restrictive Content Security Policy that forbids inline scripts and unknown script origins on any route rendering Markdown.
- Audit all code paths passing user-controlled data to markdown-it 14.1.0 and treat the rendered HTML as untrusted.
Patch Information
No vendor patch is available. The markdown-it maintainers do not consider this behavior a vulnerability and expect consumers to sanitize output or disable inline HTML. Track the upstream discussion in GitHub Issue #1122: markdown-it and the markdown-it repository for any policy change.
Workarounds
- Instantiate the parser with new MarkdownIt({ html: false, linkify: true, typographer: false }) to suppress raw HTML.
- Pipe rendered output through DOMPurify.sanitize(html, { USE_PROFILES: { html: true } }) before insertion into the DOM or HTTP response.
- Escape or reject Markdown links using the javascript:, data:, and vbscript: schemes via a custom validateLink function.
- Serve Markdown-rendered pages with Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'none'.
# Configuration example: harden markdown-it usage in Node.js
npm install dompurify jsdom
cat <<'EOF' > render-safe.mjs
import MarkdownIt from 'markdown-it';
import createDOMPurify from 'dompurify';
import { JSDOM } from 'jsdom';
const window = new JSDOM('').window;
const DOMPurify = createDOMPurify(window);
const md = new MarkdownIt({
html: false,
linkify: true,
typographer: false
});
md.validateLink = (url) => !/^(javascript|data|vbscript):/i.test(url.trim());
export function renderSafeMarkdown(input) {
const rawHtml = md.render(String(input));
return DOMPurify.sanitize(rawHtml, { USE_PROFILES: { html: true } });
}
EOF
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

