Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2025-66400

CVE-2025-66400: Unifiedjs Mdast-util-to-hast XSS Flaw

CVE-2025-66400 is a cross-site scripting vulnerability in Unifiedjs Mdast-util-to-hast that allows attackers to inject malicious classnames via character references. This article covers technical details, affected versions, impact, and mitigation strategies.

Updated:

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.

javascript
// 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.
bash
# 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.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.