GHSA-mxm6-v9r6-r94c
@nuxtjs/mdc's URL sanitizer misses SVG xlink:href and data:text/html, allowing XSS from untrusted markdown at the default configuration
Quick fix
GHSA-mxm6-v9r6-r94c — @nuxtjs/mdc: upgrade to the fixed version with the command below.
npm install @nuxtjs/mdc@0.22.1Details
## Summary
`@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.
Two sibling vectors bypass that sanitizer at the default configuration:
1. SVG anchor `xlink:href`. `validateProp` only scheme-checks attributes named exactly `href` or `src`:
``` if (attribute === "href" || attribute === "src") return isAnchorLinkAllowed(value); return true; ```
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.
2. `<iframe src="data:text/html,...">`. `data:text/html` is present in `unsafeLinkPrefix`, but the check compares it against `url.protocol`:
``` if (unsafeLinkPrefix.some((prefix) => url.protocol.toLowerCase().startsWith(prefix))) return false; ```
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.
## Reproduction
I 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. [nuxtjs-mdc-xss_poc.zip](https://github.com/user-attachments/files/29175603/nuxtjs-mdc-xss_poc.zip)
Two zero-argument checks:
1. `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. 2. `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.
Both 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.
## Suggested fix
In `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.
Are you affected?
Enter the version of the package you're using.
Affected packages
References
- https://github.com/nuxt-content/mdc/security/advisories/GHSA-mxm6-v9r6-r94c[WEB]
- https://nvd.nist.gov/vuln/detail/CVE-2026-63671[ADVISORY]
- https://github.com/nuxt-content/mdc/pull/491[WEB]
- https://github.com/nuxt-content/mdc/commit/61d636c2983f021288e4fc5c4006733b38cf0d53[WEB]
- https://github.com/nuxt-content/mdc[PACKAGE]
- https://github.com/nuxt-content/mdc/releases/tag/v0.22.1[WEB]