fix(deps): update dependency @nuxtjs/mdc to ^0.22.0 [security] #58
No reviewers
Labels
No labels
idea
points
1
points
13
points
2
points
3
points
5
points
8
priority
p0
priority
p1
priority
p2
priority
p3
state
blocked
state
done
state
in-progress
state
ready
state
review
state
triage
status
declined
status
in-progress
status
planned
status
proposed
status
shipped
status
under-review
type
bug
type
epic
type
feature
type
spike
type
story
type
task
No milestone
No project
No assignees
3 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
tti/tti-ux!58
Loading…
Reference in a new issue
No description provided.
Delete branch "renovate/npm-nuxtjs-mdc-vulnerability"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
This PR contains the following updates:
^0.21.1→^0.22.0@nuxtjs/mdc's URL sanitizer misses SVG xlink:href and data:text/html, allowing XSS from untrusted markdown at the default configuration
CVE-2026-63671 / GHSA-mxm6-v9r6-r94c
More information
Details
Summary
@nuxtjs/mdcrenders 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/validatePropand anunsafeLinkPrefixdeny-list (dist/runtime/parser/utils/props.js). The sanitizer runs at parse time (dist/runtime/parser/compiler.js) andparseMarkdownenables 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:
SVG anchor
xlink:href.validateProponly scheme-checks attributes named exactlyhreforsrc:An
xlink:href(parsed to the hast propertyxLinkHref) is neither, so ajavascript:URL on an SVG<a>is passed through. The renderer maps the property back to the real attribute (MDCRenderer.vue:find(html, "xLinkHref").attributeisxlink: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.<iframe src="data:text/html,...">.data:text/htmlis present inunsafeLinkPrefix, but the check compares it againsturl.protocol:For any data URI
url.protocolis just"data:", so"data:".startsWith("data:text/html")is always false. Everydata: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-timedangerousTags, which is only["script","base"]). The framed document executes script in an opaque origin. For contrast,srcdocandobjectare 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.shto install mdc and show the poc in the html file.nuxtjs-mdc-xss_poc.zip
Two zero-argument checks:
sh poc/poc.shinstalls@nuxtjs/mdcand runsparseMarkdown(the documented API) at default. It shows the parsed tree retainsa { xLinkHref: "javascript:..." }andiframe { src: "data:text/html,..." }, while the control payloadshref="javascript:..."andsrcdoc=...are removed by the sanitizer. This isolates the sanitizer bypass deterministically.poc/poc.shalso servespoc/poc.htmlover 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:hreflink runs script in the document origin on click, and the data:text/html iframe runs script on load.Suggested fix
In
validateProp, scheme-checkxlink:href(and the hastxLinkHref) the same way ashref/src. InisAnchorLinkAllowed, compare the dangerous MIME-typed entries against the full URL (orhref), not againsturl.protocol, sodata:text/htmlis actually matched; or addiframeto the render-time dangerous-tag set / restrict iframesrcschemes.Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:NReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
Configuration
📅 Schedule: (UTC)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR has been generated by Mend Renovate CLI.
🔧 Security-gate fix map
The gate failed on these dependency findings — fastest path to green for each:
@nuxtjs/mdc@tiptap/corenpm update core --package-lock-onlyjs-yamlnpm update js-yaml --package-lock-onlysharpnpm update sharp --package-lock-onlysvgonpm update svgo --package-lock-onlyPosted once per head commit by the baseline gate (M2). A Renovate PR that only touches a manifest with no lockfile change is a broken pre-2026-08-06 artifact — check its diff before merging.
AI review · advisory
Verdict: 1 thing worth fixing (1 medium).
Findings that didn't map to a diff line:
package-lock.json:6352· MEDIUM — Duplicate @nuxtjs/mdc versions in lockfileThe lockfile contains two different versions of @nuxtjs/mdc (0.22.2 at the top level and 0.21.1 nested under @tti/tti-ux), which can lead to runtime conflicts or duplicated bundles.
⚑ panel: GPT-OSS 120B · Gemma 4 31B · Devstral 2 123B · Laguna S 2.1 — 3 distinct (from 4 reviewer findings), 1 confirmed, 0 below threshold, 2 refuted · web: not used · context: 2 files under review · 90 codebase · 14 standards chunks (best-grounded: GPT-OSS 120B; smaller windows saw less)
Panel debate — how this review was reached
Grounding — context: 2 files under review · 90 codebase · 14 standards chunks (best-grounded: GPT-OSS 120B; smaller windows saw less)
Round 1 — independent reviews
Grouping — 4 reviewer findings describe 3 distinct defects; reviewers who found the same defect independently count as support.
Round 2 — cross-examination
GPT-OSS 120B#1Duplicate @nuxtjs/mdc versions in lockfile · also raised by: Devstral 2 123B · confirmed: Gemma 4 31B, Laguna S 2.1 · refuted: — · support 3Devstral 2 123B#1Version mismatch in @nuxtjs/mdc dependency · confirmed: — · refuted: GPT-OSS 120B, Gemma 4 31B, Laguna S 2.1 · support 0Devstral 2 123B#3Version range mismatch in package.json · confirmed: — · refuted: GPT-OSS 120B, Gemma 4 31B, Laguna S 2.1 · support 0Raised but refuted (left out of the review above)
Devstral 2 123B#1Version mismatch in @nuxtjs/mdc dependency — The package-lock.json shows @nuxtjs/mdc at version 0.22.2, but the package.json specifies "^0.22.0", which includes all 0.22.x patch versions. TherefoDevstral 2 123B#3Version range mismatch in package.json — The package.json declares "@nuxtjs/mdc": "^0.22.0", which allows any 0.22.x version. The lockfile records version 0.22.2, which is within that range,Synthesis — Laguna S 2.1 wrote the final review from 1 confirmed finding · promotion: support ≥ 2, and no refutation at high severity.
Transcript
rv-20260917054316-a6bc48— full round outputs, web results, and model reasoning are viewable by anyone with access to this repository via the AI gateway.Advisory — never a merge gate. Findings are ordered by how well the panel's own evidence checks out, strongest first. React 👍/👎 on any inline comment to tell it whether it was worth flagging — that is the only feedback this system gets, and every threshold in it is tuned from those reactions. Transcript
rv-20260917054316-a6bc48.View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.