CVE-2026-63671: @nuxtjs/mdc Cross-Site Scripting Vulnerability
HIGHCVE-2026-63671 is a HIGH (CVSS 8.1) cross-site scripting vulnerability (CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')) affecting @nuxtjs/mdc libraries in the npm ecosystem. The issue involves href and data:text/html, allowing xss from untrusted markdown at the default configuration. Remediation is available by upgrading affected packages to version 0.22.1.
CVE-2026-63671 at a Glance
Security Impact
CVSS: 8.1
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N).
How to Fix CVE-2026-63671
To remediate this vulnerability, upgrade all affected @nuxtjs/mdc dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
npm install @nuxtjs/mdc@0.22.1
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
npm ls @nuxtjs/mdc
How to Check if You Are Affected
To determine whether your Node.js application is affected by CVE-2026-63671, inspect your project dependencies using npm:
npm ls @nuxtjs/mdc
Analyze the terminal output to locate any version of @nuxtjs/mdc in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 0.22.1), your application is affected.
Note: The npm ls @nuxtjs/mdc command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting'), where the product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users. In @nuxtjs/mdc, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2026-63671 Affected @nuxtjs/mdc Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
@nuxtjs/mdc
|
npm |
< 0.22.1
|
0.22.1
|
References
Frequently Asked Questions
What is CVE-2026-63671?
Which @nuxtjs/mdc versions are affected?
What version fixes CVE-2026-63671?
Is @nuxtjs/mdc affected?
What is the CVSS score of CVE-2026-63671?
What CWE is associated with CVE-2026-63671?
How can I check npm dependencies for CVE-2026-63671?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-mxm6-v9r6-r94c",
"modified" : "2026-09-16T22:14:01Z",
"published" : "2026-09-16T22:14:01Z",
"aliases" : [ "CVE-2026-63671" ],
"summary" : "@nuxtjs/mdc's URL sanitizer misses SVG xlink:href and data:text/html, allowing XSS from untrusted markdown at the default configuration",
"details" : "## Summary\n\n`@nuxtjs/mdc` renders untrusted markdown (including raw HTML) to a Vue component tree. Across two prior advisories it added a URL/attribute sanitizer to block dangerous links in that HTML: `validateProps` / `validateProp` and an `unsafeLinkPrefix` deny-list (`dist/runtime/parser/utils/props.js`). The sanitizer runs at parse time (`dist/runtime/parser/compiler.js`) and `parseMarkdown` enables raw HTML by default (`allowDangerousHtml: true`, `dist/runtime/parser/options.js`), so the sanitizer is the only barrier and it applies with no configuration required.\n\nTwo sibling vectors bypass that sanitizer at the default configuration:\n\n1. SVG anchor `xlink:href`. `validateProp` only scheme-checks attributes named exactly `href` or `src`:\n\n ```\n if (attribute === \"href\" || attribute === \"src\") return isAnchorLinkAllowed(value);\n return true;\n ```\n\n An `xlink:href` (parsed to the hast property `xLinkHref`) is neither, so a `javascript:` URL on an SVG `<a>` is passed through. The renderer maps the property back to the real attribute (`MDCRenderer.vue`: `find(html, \"xLinkHref\").attribute` is `xlink:href`), so the output element is `<a xlink:href=\"javascript:...\">`. Clicking it runs the script in the page origin. Plain `<a href=\"javascript:...\">` is correctly stripped, which is what makes this the un-patched sibling.\n\n2. `<iframe src=\"data:text/html,...\">`. `data:text/html` is present in `unsafeLinkPrefix`, but the check compares it against `url.protocol`:\n\n ```\n if (unsafeLinkPrefix.some((prefix) => url.protocol.toLowerCase().startsWith(prefix))) return false;\n ```\n\n For any data URI `url.protocol` is just `\"data:\"`, so `\"data:\".startsWith(\"data:text/html\")` is always false. Every `data:text/*` entry in the deny-list is therefore dead code, and `<iframe src=\"data:text/html,<script>...</script>\">` is allowed (iframe is not in the render-time `dangerousTags`, which is only `[\"script\",\"base\"]`). The framed document executes script in an opaque origin. For contrast, `srcdoc` and `object` are blocked, so this is a precise gap rather than a general absence of filtering.\n\n## Reproduction\n\nI will attach the zip file for POC, you can simply extract and run `./poc.sh` to install mdc and show the poc in the html file.\n[nuxtjs-mdc-xss_poc.zip](https://github.com/user-attachments/files/29175603/nuxtjs-mdc-xss_poc.zip)\n\nTwo zero-argument checks:\n\n1. `sh poc/poc.sh` installs `@nuxtjs/mdc` and runs `parseMarkdown` (the documented API) at default. It shows the parsed tree retains `a { xLinkHref: \"javascript:...\" }` and `iframe { src: \"data:text/html,...\" }`, while the control payloads `href=\"javascript:...\"` and `srcdoc=...` are removed by the sanitizer. This isolates the sanitizer bypass deterministically.\n2. `poc/poc.sh` also serves `poc/poc.html` over http (data: iframes and javascript: links are restricted under the file:// origin, so http is used). Open the printed URL and click the blue SVG link. The page contains the exact DOM the renderer produces for those parsed nodes; clicking the SVG link executes script in the page origin (same-origin), and the data:text/html iframe executes on load. The page prints VULNERABLE for each that fires.\n\nBoth vectors were confirmed executing in a current Chromium build: the SVG `xlink:href` link runs script in the document origin on click, and the data:text/html iframe runs script on load.\n\n## Suggested fix\n\nIn `validateProp`, scheme-check `xlink:href` (and the hast `xLinkHref`) the same way as `href`/`src`. In `isAnchorLinkAllowed`, compare the dangerous MIME-typed entries against the full URL (or `href`), not against `url.protocol`, so `data:text/html` is actually matched; or add `iframe` to the render-time dangerous-tag set / restrict iframe `src` schemes.",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "npm",
"name" : "@nuxtjs/mdc"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "0.22.1"
} ]
} ]
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/nuxt-content/mdc/security/advisories/GHSA-mxm6-v9r6-r94c"
}, {
"type" : "ADVISORY",
"url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-63671"
}, {
"type" : "WEB",
"url" : "https://github.com/nuxt-content/mdc/pull/491"
}, {
"type" : "WEB",
"url" : "https://github.com/nuxt-content/mdc/commit/61d636c2983f021288e4fc5c4006733b38cf0d53"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/nuxt-content/mdc"
}, {
"type" : "WEB",
"url" : "https://github.com/nuxt-content/mdc/releases/tag/v0.22.1"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-184", "CWE-79" ],
"severity" : "HIGH",
"github_reviewed" : true,
"github_reviewed_at" : "2026-09-16T22:14:01Z",
"nvd_published_at" : "2026-09-16T15:17:40Z"
}
}