fix(deps): update dependency @nuxtjs/mdc to ^0.22.0 [security] #58

Open
renovate-bot wants to merge 1 commit from renovate/npm-nuxtjs-mdc-vulnerability into main
Member

This PR contains the following updates:

Package Change Age Confidence
@nuxtjs/mdc ^0.21.1 → ^0.22.0 age confidence

CVE-2026-63671 / GHSA-mxm6-v9r6-r94c

More information

Details

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

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.

Severity

  • CVSS Score: 8.1 / 10 (High)
  • Vector String: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N

References

This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).

❗ Important

Release Notes retrieval for this PR were skipped because no github.com credentials were available.
If you are self-hosted, please see this instruction.


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 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.


  • If you want to rebase/retry this PR, check this box

This PR has been generated by Mend Renovate CLI.

This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---| | [@nuxtjs/mdc](https://github.com/nuxt-content/mdc) | [`^0.21.1` → `^0.22.0`](https://renovatebot.com/diffs/npm/@nuxtjs%2fmdc/0.21.1/0.22.1) | ![age](https://developer.mend.io/api/mc/badges/age/npm/@nuxtjs%2fmdc/0.22.1?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@nuxtjs%2fmdc/0.21.1/0.22.1?slim=true) | --- ### @&#8203;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](https://nvd.nist.gov/vuln/detail/CVE-2026-63671) / [GHSA-mxm6-v9r6-r94c](https://github.com/advisories/GHSA-mxm6-v9r6-r94c) <details> <summary>More information</summary> #### Details ##### 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. #### Severity - CVSS Score: 8.1 / 10 (High) - Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N` #### References - [https://github.com/nuxt-content/mdc/security/advisories/GHSA-mxm6-v9r6-r94c](https://github.com/nuxt-content/mdc/security/advisories/GHSA-mxm6-v9r6-r94c) - [https://nvd.nist.gov/vuln/detail/CVE-2026-63671](https://nvd.nist.gov/vuln/detail/CVE-2026-63671) - [https://github.com/nuxt-content/mdc/pull/491](https://github.com/nuxt-content/mdc/pull/491) - [https://github.com/nuxt-content/mdc/commit/61d636c2983f021288e4fc5c4006733b38cf0d53](https://github.com/nuxt-content/mdc/commit/61d636c2983f021288e4fc5c4006733b38cf0d53) - [https://github.com/nuxt-content/mdc](https://github.com/nuxt-content/mdc) - [https://github.com/nuxt-content/mdc/releases/tag/v0.22.1](https://github.com/nuxt-content/mdc/releases/tag/v0.22.1) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-mxm6-v9r6-r94c) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> > ❗ **Important** > > Release Notes retrieval for this PR were skipped because no github.com credentials were available. > If you are self-hosted, please see [this instruction](https://github.com/renovatebot/renovate/blob/master/docs/usage/examples/self-hosting.md#githubcom-token-for-release-notes). --- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **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. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC40MS4xIiwidXBkYXRlZEluVmVyIjoiNDQuMTE1LjUiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbInNlY3VyaXR5L3Z1bG4iXX0=-->
fix(deps): update dependency @nuxtjs/mdc to ^0.22.0 [security]
Some checks failed
baseline-security / baseline (push) Failing after 1m39s
scan / trivy-fs (push) Failing after 1m23s
baseline-security / baseline (pull_request) Failing after 1m49s
scan / trivy-fs (pull_request) Failing after 1m8s
ai-review / review (pull_request) Successful in 4m39s
c1de1b2c0b

🔧 Security-gate fix map

The gate failed on these dependency findings — fastest path to green for each:

finding package installed → fixed do this
CVE-2026-63671 (HIGH) @nuxtjs/mdc 0.21.1 → 0.22.1 merge #58 — fix(deps): update dependency @nuxtjs/mdc to ^0.22.0 [securit
GHSA-j95f-988m-3j2f (HIGH) @tiptap/core 3.28.0 → 3.30.5 no fix PR yet — npm update core --package-lock-only
CVE-2026-84375 (HIGH) js-yaml 4.3.1 → 4.3.2, 3.15.2 no fix PR yet — npm update js-yaml --package-lock-only
GHSA-rgj7-g3m4-5g8c (HIGH) sharp 0.35.3 → 0.35.4 no fix PR yet — npm update sharp --package-lock-only
CVE-2026-84370 (HIGH) svgo 4.0.2 → 2.8.4, 3.3.5, 4.1.0 no fix PR yet — npm update svgo --package-lock-only

⚠ main is itself red right now — this PR likely inherits the backlog rather than adding it. Fixing main (rows above) unblocks every open PR at once.

Posted 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.

### 🔧 Security-gate fix map <!-- tti-fixmap:c1de1b2c0b0d7827d87c00657e38393685f81da5 --> The gate failed on these dependency findings — fastest path to green for each: | finding | package | installed → fixed | do this | |---|---|---|---| | CVE-2026-63671 (HIGH) | `@nuxtjs/mdc` | 0.21.1 → 0.22.1 | merge #58 — fix(deps): update dependency @nuxtjs/mdc to ^0.22.0 [securit | | GHSA-j95f-988m-3j2f (HIGH) | `@tiptap/core` | 3.28.0 → 3.30.5 | no fix PR yet — `npm update core --package-lock-only` | | CVE-2026-84375 (HIGH) | `js-yaml` | 4.3.1 → 4.3.2, 3.15.2 | no fix PR yet — `npm update js-yaml --package-lock-only` | | GHSA-rgj7-g3m4-5g8c (HIGH) | `sharp` | 0.35.3 → 0.35.4 | no fix PR yet — `npm update sharp --package-lock-only` | | CVE-2026-84370 (HIGH) | `svgo` | 4.0.2 → 2.8.4, 3.3.5, 4.1.0 | no fix PR yet — `npm update svgo --package-lock-only` | > ⚠ `main` is itself red right now — this PR likely **inherits** the backlog rather than adding it. Fixing `main` (rows above) unblocks every open PR at once. <sub>Posted 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.</sub>
ai-review-bot left a comment

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 lockfile
The 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.

Fix: Align the nested @nuxtjs/mdc entry with the top‑level version (0.22.2) to avoid duplicate versions.

⚑ 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

  • GPT-OSS 120B (1 finding, confidence 0.93): The change correctly updates the dependency version, but the lockfile now contains duplicate differing versions of @nuxtjs/mdc, which should be reconciled.
  • Gemma 4 31B (0 findings, confidence 1.0): The changes correctly update @nuxtjs/mdc to ^0.22.0 and synchronize the package versions across package.json and package-lock.json to 2.2.0, addressing the security update requested in the PR title.
  • Devstral 2 123B (3 findings, confidence 0.85): The changes introduce a version mismatch and a duplicate entry for @nuxtjs/mdc, which should be addressed to ensure consistency and avoid potential conflicts.
  • Laguna S 2.1 (0 findings, confidence 0.95): The PR is a clean and correct security-driven bump of @nuxtjs/mdc from ^0.21.1 to ^0.22.0 (resolved 0.22.2), with the lockfile regenerated to include the upstream dependency chain updates (notably @sh

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#1 Duplicate @nuxtjs/mdc versions in lockfile · also raised by: Devstral 2 123B · confirmed: Gemma 4 31B, Laguna S 2.1 · refuted: — · support 3
  • Devstral 2 123B#1 Version mismatch in @nuxtjs/mdc dependency · confirmed: — · refuted: GPT-OSS 120B, Gemma 4 31B, Laguna S 2.1 · support 0
  • Devstral 2 123B#3 Version range mismatch in package.json · confirmed: — · refuted: GPT-OSS 120B, Gemma 4 31B, Laguna S 2.1 · support 0

Raised but refuted (left out of the review above)

  • Devstral 2 123B#1 Version 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. Therefo
  • Devstral 2 123B#3 Version 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.

### AI review · advisory <!-- tti-rv:rv-20260917054316-a6bc48: --> **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 lockfile The 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. > **Fix:** Align the nested @nuxtjs/mdc entry with the top‑level version (0.22.2) to avoid duplicate versions. <sub>⚑ 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)</sub> <details> <summary>Panel debate — how this review was reached</summary> **Grounding** — context: 2 files under review · 90 codebase · 14 standards chunks (best-grounded: GPT-OSS 120B; smaller windows saw less) **Round 1 — independent reviews** - **GPT-OSS 120B** (1 finding, confidence 0.93): The change correctly updates the dependency version, but the lockfile now contains duplicate differing versions of @nuxtjs/mdc, which should be reconciled. - **Gemma 4 31B** (0 findings, confidence 1.0): The changes correctly update @nuxtjs/mdc to ^0.22.0 and synchronize the package versions across package.json and package-lock.json to 2.2.0, addressing the security update requested in the PR title. - **Devstral 2 123B** (3 findings, confidence 0.85): The changes introduce a version mismatch and a duplicate entry for @nuxtjs/mdc, which should be addressed to ensure consistency and avoid potential conflicts. - **Laguna S 2.1** (0 findings, confidence 0.95): The PR is a clean and correct security-driven bump of @nuxtjs/mdc from ^0.21.1 to ^0.22.0 (resolved 0.22.2), with the lockfile regenerated to include the upstream dependency chain updates (notably @sh **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#1` Duplicate @nuxtjs/mdc versions in lockfile · also raised by: Devstral 2 123B · confirmed: Gemma 4 31B, Laguna S 2.1 · refuted: — · support 3 - `Devstral 2 123B#1` Version mismatch in @nuxtjs/mdc dependency · confirmed: — · refuted: GPT-OSS 120B, Gemma 4 31B, Laguna S 2.1 · support 0 - `Devstral 2 123B#3` Version range mismatch in package.json · confirmed: — · refuted: GPT-OSS 120B, Gemma 4 31B, Laguna S 2.1 · support 0 **Raised but refuted** (left out of the review above) - `Devstral 2 123B#1` Version 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. Therefo - `Devstral 2 123B#3` Version 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. <sub>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.</sub> </details> <sub>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`.</sub>
Some checks failed
baseline-security / baseline (push) Failing after 1m39s
scan / trivy-fs (push) Failing after 1m23s
baseline-security / baseline (pull_request) Failing after 1m49s
Required
Details
scan / trivy-fs (pull_request) Failing after 1m8s
ai-review / review (pull_request) Successful in 4m39s
This pull request doesn't have enough approvals yet. 0 of 1 approvals granted.
This branch is out-of-date with the base branch
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin renovate/npm-nuxtjs-mdc-vulnerability:renovate/npm-nuxtjs-mdc-vulnerability
git switch renovate/npm-nuxtjs-mdc-vulnerability
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
tti/tti-ux!58
No description provided.