CVE-2025-24981
Description
MDC is a tool to take regular Markdown and write documents interacting deeply with a Vue component. In affected versions unsafe parsing logic of the URL from markdown can lead to arbitrary JavaScript code due to a bypass to the existing guards around the javascript: protocol scheme in the URL. The parsing logic implement in props.ts maintains a deny-list approach to filtering potential malicious payload. It does so by matching protocol schemes like javascript: and others. These security guards can be bypassed by an adversarial that provides JavaScript URLs with HTML entities encoded via hex string. Users who consume this library and perform markdown parsing from unvalidated sources could result in rendering vulnerable XSS anchor links. This vulnerability has been addressed in version 0.13.3 and all users are advised to upgrade. There are no known workarounds for this vulnerability.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
@nuxtjs/mdcnpm | < 0.13.3 | 0.13.3 |
Patches
399097738b556395f380a6de92 files changed · +47 −2
src/runtime/parser/utils/props.ts+19 −1 modified@@ -9,13 +9,31 @@ export const unsafeLinkPrefix = [ 'data:text/xml' ] +function isAnchorLinkAllowed(value: string) { + const decodedUrl = decodeURIComponent(value) + const urlSanitized = decodedUrl.replace(/&#x([0-9a-f]+);?/gi, '') + .replace(/&#(\d+);?/g, '') + .replace(/&[a-z]+;?/gi, '') + + try { + const url = new URL(urlSanitized) + if (unsafeLinkPrefix.some(prefix => url.protocol.toLowerCase().startsWith(prefix))) { + return false + } + } catch { + return false + } + + return true +} + export const validateProp = (attribute: string, value: string) => { if (attribute.startsWith('on')) { return false } if (attribute === 'href' || attribute === 'src') { - return !unsafeLinkPrefix.some(prefix => value.toLowerCase().startsWith(prefix)) + return isAnchorLinkAllowed(value) } return true
test/markdown/xss.test.ts+28 −1 modified@@ -10,6 +10,7 @@ const md = `\ [XSS](vbscript:alert(document.domain)) <javascript:prompt(document.cookie)> [x](y '<style>') +<a href="jav	ascript:alert('XSS');">Click Me</a> <!-- image --> @@ -31,7 +32,7 @@ const md = `\ `.trim() -it('XSS', async () => { +it('XSS generic payloads', async () => { const { data, body } = await parseMarkdown(md) expect(Object.keys(data)).toHaveLength(2) @@ -41,3 +42,29 @@ it('XSS', async () => { expect(Object.entries(props as Record<string, any>).every(([k, v]) => validateProp(k, v))).toBeTruthy() } }) + +it('XSS payloads with HTML entities should be caught', async () => { + const md = `\ +## XSS payloads with HTML entities +<a href="jav	ascript:alert('XSS');">Click Me 1</a> +<a href="jav
ascript:alert('XSS');">Click Me 2</a> +<a href="jav ascript:alert('XSS');">Click Me 3</a> + + +`.trim() + + // set the number of assertions to expect + expect.assertions(4) + + const { data, body } = await parseMarkdown(md) + + expect(Object.keys(data)).toHaveLength(2) + + for (const node of (body.children[1] as MDCElement).children) { + const props = (node as MDCElement).props || {} + + if ((node as MDCElement).tag === 'a') { + expect(Object.keys(props)).toHaveLength(0) + } + } +})
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-j82m-pc2v-2484ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-24981ghsaADVISORY
- github.com/nuxt-modules/mdc/blob/main/src/runtime/parser/utils/props.tsnvdWEB
- github.com/nuxt-modules/mdc/commit/99097738b5561639e9bf247c55d8103236618bf3nvdWEB
- github.com/nuxt-modules/mdc/security/advisories/GHSA-j82m-pc2v-2484nvdWEB
News mentions
0No linked articles in our index yet.