Every CVE Search/ Get API key

CVE-2026-111758

markdown-it: Quadratic-time rendering of a single multi-line link reference definition with default options (regression since 14.0)

7.5 HighFix available

markdown-it is a Markdown parser. From versions 14.1.0 to before 14.3.3, and 15.0.0 to before 15.0.3, a link reference definition whose label or title is never closed is O(n²) with default options. Continuation lines are pulled lazily with str += lineContent in src/rules_block/reference.ts, and the subsequent charCodeAt and slice calls flatten the V8 cons-string, copying everything collected so far; this runs before the rule decides the definition is invalid. No options are required to trigger it. This issue has been patched in versions 14.3.3 and 15.0.3.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
None
User interaction
None

What an attacker can affect

Confidentiality
None
Integrity
None
Availability
High

Exploit likelihood

0.55%Up 0.1 points since Oct 21, 2026
90%50%10%1%

What each source reports

CVE List Score used 7.5

markdown-it: Quadratic-time rendering of a single multi-line link reference definition with default options (regression since 14.0)

markdown-it is a Markdown parser. From versions 14.1.0 to before 14.3.3, and 15.0.0 to before 15.0.3, a link reference definition whose label or title is never closed is O(n²) with default options. Continuation lines are pulled lazily with str += lineContent in src/rules_block/reference.ts, and the subsequent charCodeAt and slice calls flatten the V8 cons-string, copying everything collected so far; this runs before the rule decides the definition is invalid. No options are required to trigger it. This issue has been patched in versions 14.3.3 and 15.0.3.

Record
CVE-2026-111758
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Weakness
CWE-400, CWE-407
Updated
2026-11-02
EUVDEUVD-2026-105358 7.5

markdown-it is a Markdown parser. From versions 14.1.0 to before 14.3.3, and 15.0.0 to before 15.0.3, a link reference definition whose label or title is never closed is O(n²) with default options. Continuation lines are pulled lazily with str += lineContent in src/rules_block/reference.ts, and the subsequent charCodeAt and slice calls flatten the V8 cons-string, copying everything collected so far; this runs before the rule decides the definition is invalid. No options are required to trigger it. This issue has been patched in versions 14.3.3 and 15.0.3.

Record
EUVD-2026-105358
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Updated
2026-11-02
GitHub AdvisoriesGHSA-gfr2-rv44-r23c 7.5

markdown-it: Quadratic-time rendering of a single multi-line link reference definition with default options (regression since 14.0)

Summary

A link reference definition whose label or title is never closed is O(n²) in markdown-it 15.0.2 with default options. Continuation lines are pulled lazily with str += lineContent in src/rules_block/reference.ts, and the subsequent charCodeAt and slice calls flatten the V8 cons-string, copying everything collected so far; this runs before the rule decides the definition is invalid. It is a regression introduced by PR #1004 in 14.0.0: 13.0.2 processed a 313 KB input in 125 ms, while every version since takes about 11 seconds on the same input. At the sizes below, 200 KB takes 3.6 seconds, 400 KB takes 18.1 seconds, and 800 KB takes 74.0 seconds, which is roughly 4–5× per doubling. No options are required to trigger it.

Details

src/rules_block/reference.ts appends continuation lines with str += lineContent at lines 74, 83, 100, 133, and 150, then keeps indexing the accumulated string via str.charCodeAt(pos) in the label and whitespace loops and str.slice(...) in parseLinkTitle. V8 represents += as a cons-string, and the next charCodeAt or slice flattens it, copying everything collected so far, so one definition spanning n lines costs O(n²). Isolation confirms the rule is responsible: block-only parsing with inline disabled is still quadratic, md.disable('reference') makes the same input linear, and the same text without the leading [ is linear. The fix is to track positions in state.src instead of building str, or to collect line slices in an array and parse across them. At minimum, stop scanning the label after 999 characters (CommonMark §6.3) before pulling more lines, and cap title continuation. Add a pathological test with a 100k-line unclosed title and label. Open PR #1215 edits the same file but its label check runs after the label loop, so it does not bound either variant and the fix can land together with that PR.

The observed output from PoC was: unclosed title n=100000 200009 B 3585.5 ms unclosed title n=200000 400009 B 18107.1 ms (5.05x) unclosed title n=400000 800009 B 73954.4 ms (4.08x)

Regression comparison at 313 KB: 13.0.2 → 125 ms, 14.1.0 → 11.4 s, 15.0.0 → 10.6 s, 15.0.2 → 10.7 s.

Proof of concept

How to execute the PoC:

  1. mkdir -p markdown-it-poc && cd markdown-it-poc
  2. npm init -y
  3. npm i [email protected]
  4. node poc_markdown_it.js

PoC below is:

// poc_markdown_it.js
// Requires: npm install [email protected]
const MarkdownIt = require('markdown-it');
const md = new MarkdownIt();

function bench(label, src) {
  const t0 = process.hrtime.bigint();
  md.render(src);
  const t1 = process.hrtime.bigint();
  console.log(
    `${label.padEnd(30)} ${String(src.length).padStart(9)} B ${(Number(t1 - t0) / 1e6).toFixed(1).padStart(9)} ms`
  );
}

// Unclosed title: one reference definition spread over many lines
for (const n of [100000, 200000, 400000]) {
  bench(`unclosed title n=${n}`, '[a]: /u "' + 'x\n'.repeat(n));
}

// Unclosed label
for (const n of [5000, 10000, 20000]) {
  bench(`unclosed label n=${n}`, '[' + 'a\n'.repeat(n));
}

Impact

This is an algorithmic-complexity denial of service (CWE-407). Any server that renders user Markdown with markdown-it is affected with default options, including comments, issues, chat, wikis, docs previews, and LLM chat UIs. A ~400 KB request body blocks the Node.js event loop for about 18 seconds and an ~800 KB body for about 74 seconds. This is the same class and attacker model as GHSA-253c, but it is reachable without any opt-in option, since the affected path is a core block rule.

Record
GHSA-gfr2-rv44-r23c
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Weakness
CWE-400, CWE-407
Updated
2026-11-02
NVD 7.5

markdown-it is a Markdown parser. From versions 14.1.0 to before 14.3.3, and 15.0.0 to before 15.0.3, a link reference definition whose label or title is never closed is O(n²) with default options. Continuation lines are pulled lazily with str += lineContent in src/rules_block/reference.ts, and the subsequent charCodeAt and slice calls flatten the V8 cons-string, copying everything collected so far; this runs before the rule decides the definition is invalid. No options are required to trigger it. This issue has been patched in versions 14.3.3 and 15.0.3.

Record
CVE-2026-111758
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Weakness
CWE-400, CWE-407
Updated
2026-11-02
OSV 7.5

markdown-it: Quadratic-time rendering of a single multi-line link reference definition with default options (regression since 14.0)

markdown-it is a Markdown parser. From versions 14.1.0 to before 14.3.3, and 15.0.0 to before 15.0.3, a link reference definition whose label or title is never closed is O(n²) with default options. Continuation lines are pulled lazily with str += lineContent in src/rules_block/reference.ts, and the subsequent charCodeAt and slice calls flatten the V8 cons-string, copying everything collected so far; this runs before the rule decides the definition is invalid. No options are required to trigger it. This issue has been patched in versions 14.3.3 and 15.0.3.

Record
CVE-2026-111758
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Weakness
CWE-400, CWE-407
Updated
2026-11-02
OSVGHSA-gfr2-rv44-r23c 7.5

markdown-it: Quadratic-time rendering of a single multi-line link reference definition with default options (regression since 14.0)

Summary

A link reference definition whose label or title is never closed is O(n²) in markdown-it 15.0.2 with default options. Continuation lines are pulled lazily with str += lineContent in src/rules_block/reference.ts, and the subsequent charCodeAt and slice calls flatten the V8 cons-string, copying everything collected so far; this runs before the rule decides the definition is invalid. It is a regression introduced by PR #1004 in 14.0.0: 13.0.2 processed a 313 KB input in 125 ms, while every version since takes about 11 seconds on the same input. At the sizes below, 200 KB takes 3.6 seconds, 400 KB takes 18.1 seconds, and 800 KB takes 74.0 seconds, which is roughly 4–5× per doubling. No options are required to trigger it.

Details

src/rules_block/reference.ts appends continuation lines with str += lineContent at lines 74, 83, 100, 133, and 150, then keeps indexing the accumulated string via str.charCodeAt(pos) in the label and whitespace loops and str.slice(...) in parseLinkTitle. V8 represents += as a cons-string, and the next charCodeAt or slice flattens it, copying everything collected so far, so one definition spanning n lines costs O(n²). Isolation confirms the rule is responsible: block-only parsing with inline disabled is still quadratic, md.disable('reference') makes the same input linear, and the same text without the leading [ is linear. The fix is to track positions in state.src instead of building str, or to collect line slices in an array and parse across them. At minimum, stop scanning the label after 999 characters (CommonMark §6.3) before pulling more lines, and cap title continuation. Add a pathological test with a 100k-line unclosed title and label. Open PR #1215 edits the same file but its label check runs after the label loop, so it does not bound either variant and the fix can land together with that PR.

The observed output from PoC was: unclosed title n=100000 200009 B 3585.5 ms unclosed title n=200000 400009 B 18107.1 ms (5.05x) unclosed title n=400000 800009 B 73954.4 ms (4.08x)

Regression comparison at 313 KB: 13.0.2 → 125 ms, 14.1.0 → 11.4 s, 15.0.0 → 10.6 s, 15.0.2 → 10.7 s.

Proof of concept

How to execute the PoC:

  1. mkdir -p markdown-it-poc && cd markdown-it-poc
  2. npm init -y
  3. npm i [email protected]
  4. node poc_markdown_it.js

PoC below is:

// poc_markdown_it.js
// Requires: npm install [email protected]
const MarkdownIt = require('markdown-it');
const md = new MarkdownIt();

function bench(label, src) {
  const t0 = process.hrtime.bigint();
  md.render(src);
  const t1 = process.hrtime.bigint();
  console.log(
    `${label.padEnd(30)} ${String(src.length).padStart(9)} B ${(Number(t1 - t0) / 1e6).toFixed(1).padStart(9)} ms`
  );
}

// Unclosed title: one reference definition spread over many lines
for (const n of [100000, 200000, 400000]) {
  bench(`unclosed title n=${n}`, '[a]: /u "' + 'x\n'.repeat(n));
}

// Unclosed label
for (const n of [5000, 10000, 20000]) {
  bench(`unclosed label n=${n}`, '[' + 'a\n'.repeat(n));
}

Impact

This is an algorithmic-complexity denial of service (CWE-407). Any server that renders user Markdown with markdown-it is affected with default options, including comments, issues, chat, wikis, docs previews, and LLM chat UIs. A ~400 KB request body blocks the Node.js event loop for about 18 seconds and an ~800 KB body for about 74 seconds. This is the same class and attacker model as GHSA-253c, but it is reachable without any opt-in option, since the affected path is a core block rule.

Record
GHSA-gfr2-rv44-r23c
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Weakness
CWE-400, CWE-407
Updated
2026-11-02
Show 6 moreShow less
OSVUBUNTU-CVE-2026-111758 7.5

markdown-it is a Markdown parser. From versions 14.1.0 to before 14.3.3, and 15.0.0 to before 15.0.3, a link reference definition whose label or title is never closed is O(n²) with default options. Continuation lines are pulled lazily with str += lineContent in src/rules_block/reference.ts, and the subsequent charCodeAt and slice calls flatten the V8 cons-string, copying everything collected so far; this runs before the rule decides the definition is invalid. No options are required to trigger it. This issue has been patched in versions 14.3.3 and 15.0.3.

Record
UBUNTU-CVE-2026-111758
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
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-1792582446 No score

TuxCare security update for markdown-it (1 CVE)

TuxCare rebuilt markdown-it to address 1 CVE. Fixed in 14.3.1-tuxcare.2.

Record
CLSA-2026-1792582446
Updated
2026-11-02
OSVCLSA-2026-1792595173 No score

TuxCare security update for markdown-it (1 CVE)

TuxCare rebuilt markdown-it to address 1 CVE. Fixed in 14.3.0-tuxcare.3.

Record
CLSA-2026-1792595173
Updated
2026-11-02
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
OSVROOT-APP-NPM-CVE-2026-111758 No score

CVE-2026-111758 in markdown-it - Patched by Root

Root has patched CVE-2026-111758 in the markdown-it package for Root:npm. Multiple fixed versions available.

Record
ROOT-APP-NPM-CVE-2026-111758
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
markdown-it
npm
14.1.0 up to 14.3.3
14.3.3
markdown-it
npm
15.0.0 up to 15.0.3
15.0.3
markdown-it
TuxCare: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-it
TuxCare:npm
14.3.1-tuxcare.0 up to 14.3.1-tuxcare.2, plus listed versions
14.3.114.3.1-tuxcare.1
14.3.1-tuxcare.2
markdown-it
TuxCare:npm
14.3.0-tuxcare.0 up to 14.3.0-tuxcare.3, plus listed versions
14.3.014.3.0-tuxcare.114.3.0-tuxcare.2
14.3.0-tuxcare.3
markdown-it
TuxCare: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-it
Root:npm
before 14.3.0-aikido.1
14.3.0-aikido.114.1.0-aikido.414.3.2-aikido.114.2.0-aikido.114.2.0-aikido.2
@rootio/markdown-it
Root:npm
before 14.3.0-root.io.1
14.3.0-root.io.114.1.0-root.io.414.3.2-root.io.114.2.0-root.io.114.2.0-root.io.2
node-markdown-it
Ubuntu: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-it
Ubuntu:24.04:LTS
All versions, plus listed versions
22.2.3+dfsg+~12.2.3-2
None known
node-markdown-it
Ubuntu: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-it
Ubuntu: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

History
RecordedChangedSources
affected, aliasesOSV
affected, facts
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-111758 is a high vulnerability (CVSS 7.5) in Markdown-it, published October 20, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.55%, the 46th percentile. Fixed in markdown-it 14.3.3.

References

Show 5 moreShow less
MoveEnter Open