CVE-2026-84992: md-editor-v3: XSS via fenced-code language rendering bypass
Summary MdPreview interpolates a fenced-code language into HTML attributes without escaping it. A crafted info string therefore executes JavaScript even when the shipped XSSPlugin is enabled.
Details useMarkdownIt() (packages/MdEditor/layouts/Content/composition/useMarkdownIt.ts:206) registers a highlight callback whose final return inserts language into both class="language-${language}" and an unquoted language=${language} attribute without escaping. Both highlighting paths reach this return. XSSPlugin() filters only existing htmlblock and htmlinline tokens before rendering, so it cannot inspect this renderer-generated HTML.
PoC The Vue application enables the shipped XSSPlugin and renders attacker-controlled Markdown. noHighlight: true only makes reproduction deterministic; the default highlighting path reaches the same unsafe return. Use this as src/main.js:
js import { createApp, h } from 'vue'; import { MdPreview, XSSPlugin, config } from 'md-editor-v3';
config({ markdownItPlugins: p => [...p, { type: 'xss', plugin: XSSPlugin, options: {} }] }); const markdown = 'x"><details/open/ontoggle=alert(document.domain)>\nSAFE\n'; createApp({ render: () => h(MdPreview, { editorId: 'poc', modelValue: markdown, noHighlight: true }) }).mount('#app');
Create and run the app, replacing src/main.js when indicated:
sh npm create vite@latest poc -- --template vue cd poc npm install npm install md-editor-v3@6.5.3 Replace src/main.js with the code above. npm run dev
Opening the displayed URL automatically shows the application hostname in an alert.
Impact An attacker who can supply Markdown can execute JavaScript in the application origin when a victim renders it. If the host stores that Markdown, this becomes stored XSS.
Suggested fix Escape language with md.utils.escapeHtml before interpolation and quote the language attribute. Add this payload and the raw control as regression tests with XSSPlugin enabled.
Other sources
md-editor-v3 is a Markdown editor for Vue 3 developed in JSX and TypeScript. Prior to 6.5.4, MdPreview's useMarkdownIt() highlight callback in packages/MdEditor/layouts/Content/composition/useMarkdownIt.ts inserts a fenced-code language value into class and language HTML attributes without escaping or consistently quoting it. Both highlighted and non-highlighted rendering paths reach this return value, while XSSPlugin filters only existing htmlblock and htmlinline tokens before rendering and therefore cannot inspect the renderer-generated HTML. An attacker who can supply Markdown can use crafted fenced-code metadata to execute JavaScript in the application origin when a victim renders it, including as stored cross-site scripting when the host persists the Markdown. This issue is fixed in version 6.5.4
— MITRE
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
npm/md-editor-v3to a version that resolves this vulnerability.Fixed in 6.5.4 - Upgrade
Upgrade
md-editor-v3to a version that resolves this vulnerability.Fixed in 6.5.4 - Configuration
In src/main.js (per instructions), update the Markdown fenced-code language interpolation in MdPreview to escape `language` via `md.utils.escapeHtml` before interpolating it into HTML attributes, and ensure the `language` attribute is consistently quoted (e.g., `language="..."`).
MdPreview (fenced-code language rendering) escapeHtml for fenced-code language attribute = Use md.utils.escapeHtml(language) and quote the language attribute - Operational
Add the provided XSS regression test payload (fenced-code language value containing `x"><details/open/ontoggle=alert(document.domain)> SAFE </details>` as shown) and the raw control with `XSSPlugin` enabled, then run the app to confirm the fix in v6.5.4.
Event History
Frequently Asked Questions
Who is exposed to this issue?
Applications using MdPreview from npm/md-editor-v3 to render Markdown that an attacker can influence are exposed. The attacker does not need prior privileges, but a user must view the rendered Markdown for the JavaScript to execute.
Does enabling the shipped XSSPlugin prevent exploitation?
No. XSSPlugin filters existing Markdown HTML tokens, but the unsafe HTML is generated later by the fenced-code highlighting renderer, so it does not inspect or sanitize the injected language value.
Is the default highlighting configuration affected?
Yes. Although noHighlight: true makes the proof of concept deterministic, both the no-highlight and default highlighting paths reach the unsafe return that inserts the language string into HTML attributes.
What input is required to trigger the issue?
An attacker needs to supply a fenced code block whose info string, normally used as the code language, contains attribute-breaking HTML. That value is interpolated into both a class attribute and an unquoted language attribute without escaping.
What remediation information is available?
The provided references include a security advisory, a fixing commit, and the v6.5.4 release tag. If updating cannot occur immediately, do not render untrusted Markdown through the affected MdPreview fenced-code rendering path.