CVE-2025-66400 Overview
CVE-2025-66400 affects mdast-util-to-hast, a utility in the unified.js ecosystem that converts Markdown Abstract Syntax Trees (mdast) into HTML Abstract Syntax Trees (hast). Versions from 13.0.0 up to but not including 13.2.1 permit attackers to inject multiple unprefixed CSS class names into rendered output by embedding HTML character references inside fenced code block language identifiers. The flaw allows user-supplied markdown to render with arbitrary class names that match site-wide styles, enabling visual spoofing of trusted page elements. The issue is resolved in version 13.2.1.
Critical Impact
Attackers can inject arbitrary CSS class names through Markdown code fences, allowing user-supplied content to mimic legitimate page elements and facilitate UI redress or phishing attacks.
Affected Products
- unifiedjs mdast-util-to-hast versions 13.0.0 through 13.2.0
- Downstream remark-rehype consumers using the affected range
- Node.js applications rendering untrusted Markdown via the unified ecosystem
Discovery Timeline
- 2025-12-01 - CVE-2025-66400 published to NVD
- 2026-02-06 - Last updated in NVD database
Technical Details for CVE-2025-66400
Vulnerability Analysis
The vulnerability resides in lib/handlers/code.js, the handler responsible for transforming fenced code blocks into hast <code> elements. The handler reads node.lang and prepends language- to construct a className property on the rendered element. The fix references the case js python ruby, where character references decode to whitespace inside the language string. Because the previous logic concatenated the raw node.lang value into the class list without splitting on whitespace, the resulting className array could be interpreted by the downstream serializer as multiple class names.
This behavior maps to [CWE-20] Improper Input Validation. The impact is limited to integrity of the rendered DOM, consistent with the partial integrity rating in the CVSS vector. There is no direct script execution, but injected classes can apply existing site styles to attacker-controlled markdown regions.
Root Cause
The root cause is the assumption that node.lang contains a single token. Markdown parsers preserve character references such as (space) and (tab) inside the language identifier. When the handler built ['language-' + node.lang], the resulting string contained whitespace, which HTML treats as a class separator at serialization time. The first patch removed the legacy regex-based splitting; the second patch correctly splits on whitespace and uses only the first language token, mirroring GitHub and CommonMark behavior.
Attack Vector
Exploitation requires that an application render attacker-controlled Markdown using mdast-util-to-hast (directly or via remark-rehype). The attacker authors a fenced code block with a crafted info string, for example using character references to insert whitespace followed by additional class tokens. After conversion, the <code> element carries multiple class names, allowing the attacker's code block to inherit styles intended for navigation, alerts, or other trusted elements.
// Security patch in lib/handlers/code.js — corrected class handling
const value = node.value ? node.value + '\n' : ''
/** @type {Properties} */
const properties = {}
// Someone can write `js python ruby`.
const language = node.lang ? node.lang.split(/\s+/) : []
// GH/CM still drop the non-first languages.
if (language.length > 0) {
properties.className = ['language-' + language[0]]
}
// Create `<code>`.
Source: syntax-tree/mdast-util-to-hast commit ab3a795
Detection Methods for CVE-2025-66400
Indicators of Compromise
- Markdown source containing fenced code blocks whose info string includes character references such as , , or
- Rendered HTML where <code> elements carry multiple class tokens or class names not prefixed with language-
- User-generated content displaying styling that matches unrelated UI components (badges, alerts, navigation)
Detection Strategies
- Inventory Node.js dependencies and flag any project resolving mdast-util-to-hast to a version between 13.0.0 and 13.2.0 inclusive.
- Add a server-side post-render check that asserts every <code> class begins with language- and contains no whitespace.
- Run regression tests using payloads such as ```js python to confirm only a single sanitized class is emitted.
Monitoring Recommendations
- Monitor SCA and dependency scanners (npm audit, GitHub Dependabot) for advisory GHSA-4fh9-h7wg-q85m.
- Log Markdown submissions containing numeric character references inside the first line of fenced code blocks for review.
- Track template rendering errors or unexpected class combinations in front-end QA pipelines.
How to Mitigate CVE-2025-66400
Immediate Actions Required
- Upgrade mdast-util-to-hast to version 13.2.1 or later across all applications and build pipelines.
- Rebuild and redeploy any static sites or services that pre-render Markdown using the affected version.
- Audit user-generated Markdown stored in databases or content stores for fenced code blocks containing character references in the language tag.
Patch Information
The fix is delivered in mdast-util-to-hast13.2.1. The relevant commits are 6fc783a, which removes the legacy regex split on node.lang, and ab3a795, which splits on whitespace and retains only the first language token. See the GitHub Security Advisory GHSA-4fh9-h7wg-q85m for full details.
Workarounds
- Strip or reject Markdown code fence info strings that contain numeric character references before parsing.
- Apply a Content Security Policy and scoped CSS so that injected unprefixed class names cannot inherit sensitive site styles.
- Post-process generated hast trees to enforce a single language-* class on <code> nodes when upgrading is not yet possible.
# Upgrade the vulnerable package to the patched release
npm install mdast-util-to-hast@^13.2.1
# Verify resolved version in the dependency tree
npm ls mdast-util-to-hast
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

