Every CVE Search/ Get API key

CVE-2026-59887

linkify-it: Quadratic-complexity DoS via the `mailto:` validator scan-loop on attacker text

7.5 HighFix available

linkify-it is a links recognition library with full Unicode support. Prior to 5.0.2, the mailto: schema validator used by .test() and .match() can be invoked at every mailto: occurrence and scan the remaining input through src_email_name in lib/re.mjs, causing O(n^2) CPU consumption on crafted user text. This issue is fixed in version 5.0.2.

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.47%Up 0.1 points since Jul 9, 2026
90%50%10%1%

What each source reports

CVE List Score used 7.5

linkify-it: Quadratic-complexity DoS via the `mailto:` validator scan-loop on attacker text

linkify-it is a links recognition library with full Unicode support. Prior to 5.0.2, the mailto: schema validator used by .test() and .match() can be invoked at every mailto: occurrence and scan the remaining input through src_email_name in lib/re.mjs, causing O(n^2) CPU consumption on crafted user text. This issue is fixed in version 5.0.2.

Record
CVE-2026-59887
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-407
Updated
2026-11-02
EUVDEUVD-2026-42321 7.5

linkify-it is a links recognition library with full Unicode support. Prior to 5.0.2, the mailto: schema validator used by .test() and .match() can be invoked at every mailto: occurrence and scan the remaining input through src_email_name in lib/re.mjs, causing O(n^2) CPU consumption on crafted user text. This issue is fixed in version 5.0.2.

Record
EUVD-2026-42321
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-v245-v573-v5vm 7.5

linkify-it: Quadratic-complexity DoS via the `mailto:` validator scan-loop on attacker text

Summary

linkify-it's schema-scan loop (.test() / .match(), the documented public API) invokes the mailto: schema validator at every mailto: occurrence in the input text. For each occurrence the validator does text.slice(pos) (an O(n) copy) and runs an email regex whose local-part class src_email_name greedily scans the entire remaining tail (O(n)) before failing. With N mailto: occurrences that is N × O(n) = O(n²). Because linkify-it runs on arbitrary user text (markdown-it feeds it whole documents when linkify:true), an unauthenticated attacker can block the single-threaded event loop for many seconds with a small input. No length bound (unlike an HTTP header).

Root cause — index.mjs + lib/re.mjs

// index.mjs (mailto validator) — runs at every "mailto:" hit
'mailto:': { validate: function (text, pos, self) {
  const tail = text.slice(pos)                                  // O(n) copy per hit
  if (!self.re.mailto) self.re.mailto = new RegExp('^' + self.re.src_email_name + '@' + self.re.src_host_strict, 'i')
  if (self.re.mailto.test(tail)) { ... }                        // scans the whole O(n) tail
  return 0
}}
// lib/re.mjs:91-93 — every char of "mailto:" (incl. ':','-',';') is in this class:
re.src_email_name = '[\\-;:&=\\+\\$,\\.a-zA-Z0-9_][\\-;:&=\\+\\$,\\"\\.a-zA-Z0-9_]*'

The while ((m = re.exec(text)) !== null) { …testSchemaAt… } scan loop calls the validator at each mailto: hit; src_email_name greedily consumes the whole tail (all chars are in its class) then fails for lack of @. http:/https: do NOT blow up — their validator requires the tail to start with //, failing in O(1) per hit.

Proof of Concept (confirmed, linkify-it 5.0.1, Node v24)

const LinkifyIt = require('linkify-it');
const lf = new LinkifyIt();
lf.match('mailto:'.repeat(48000));   // ~336 KB of "mailto:mailto:…" -> seconds of blocked event loop

| input (same bytes) | 56 KB | 112 KB | 224 KB | 336 KB | |---|---:|---:|---:|---:| | mailto: contiguous | 97 ms | 357 ms | 1438 ms | 3272 ms | | mailto: space-separated | 2 ms | 3 ms | 5 ms | 8 ms | | http:// contiguous | 12 ms | 17 ms | 33 ms | 49 ms |

×~4 per 2× input ⇒ O(n²); equal-byte controls stay flat ⇒ algorithmic, not a GC/allocation artifact. Real-world via markdown-it 14.x ({linkify:true}), md.render('mailto:'.repeat(n)): 219 KB ≈ ~5 s. <img width="737" height="161" alt="image" src="https://github.com/user-attachments/assets/b5d390f3-68d0-4861-9c47-ad8aff0203d5" />

Impact

Reachable on arbitrary user text via the documented .test()/.match() API and through markdown-it's linkifier — comment systems, chat, forums, wikis, note apps that render user markdown with linkify enabled. A ~220 KB post hangs the event loop ~5 s; a few hundred KB → tens of seconds. Availability only.

Suggested remediation

Bound the email local-part per RFC 5321 (≤64) so per-hit work is O(1), and avoid the full-tail slice:

// lib/re.mjs — cap the greedy run:
re.src_email_name = '[\\-;:&=\\+\\$,\\.a-zA-Z0-9_][\\-;:&=\\+\\$,\\"\\.a-zA-Z0-9_]{0,63}'
// index.mjs — prefer a sticky regex anchored at `pos` over text.slice(pos).

Affected / disclosure

All versions through 5.0.1 (latest); same code on master. cve-mcp/OSV report no known vulnerability for linkify-it. Distinct from markdown-it's own *-run ReDoS (CVE-2026-2327, different package/path) and the recent markdown-it DoS. Reported privately; happy to test a patch against the PoC.

Record
GHSA-v245-v573-v5vm
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-407
Updated
2026-11-02
NVD 7.5

linkify-it is a links recognition library with full Unicode support. Prior to 5.0.2, the mailto: schema validator used by .test() and .match() can be invoked at every mailto: occurrence and scan the remaining input through src_email_name in lib/re.mjs, causing O(n^2) CPU consumption on crafted user text. This issue is fixed in version 5.0.2.

Record
CVE-2026-59887
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-407
Updated
2026-11-02
OSV 7.5

linkify-it: Quadratic-complexity DoS via the `mailto:` validator scan-loop on attacker text

linkify-it is a links recognition library with full Unicode support. Prior to 5.0.2, the mailto: schema validator used by .test() and .match() can be invoked at every mailto: occurrence and scan the remaining input through src_email_name in lib/re.mjs, causing O(n^2) CPU consumption on crafted user text. This issue is fixed in version 5.0.2.

Record
CVE-2026-59887
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-407
Updated
2026-11-02
OSVGHSA-v245-v573-v5vm 7.5

linkify-it: Quadratic-complexity DoS via the `mailto:` validator scan-loop on attacker text

Summary

linkify-it's schema-scan loop (.test() / .match(), the documented public API) invokes the mailto: schema validator at every mailto: occurrence in the input text. For each occurrence the validator does text.slice(pos) (an O(n) copy) and runs an email regex whose local-part class src_email_name greedily scans the entire remaining tail (O(n)) before failing. With N mailto: occurrences that is N × O(n) = O(n²). Because linkify-it runs on arbitrary user text (markdown-it feeds it whole documents when linkify:true), an unauthenticated attacker can block the single-threaded event loop for many seconds with a small input. No length bound (unlike an HTTP header).

Root cause — index.mjs + lib/re.mjs

// index.mjs (mailto validator) — runs at every "mailto:" hit
'mailto:': { validate: function (text, pos, self) {
  const tail = text.slice(pos)                                  // O(n) copy per hit
  if (!self.re.mailto) self.re.mailto = new RegExp('^' + self.re.src_email_name + '@' + self.re.src_host_strict, 'i')
  if (self.re.mailto.test(tail)) { ... }                        // scans the whole O(n) tail
  return 0
}}
// lib/re.mjs:91-93 — every char of "mailto:" (incl. ':','-',';') is in this class:
re.src_email_name = '[\\-;:&=\\+\\$,\\.a-zA-Z0-9_][\\-;:&=\\+\\$,\\"\\.a-zA-Z0-9_]*'

The while ((m = re.exec(text)) !== null) { …testSchemaAt… } scan loop calls the validator at each mailto: hit; src_email_name greedily consumes the whole tail (all chars are in its class) then fails for lack of @. http:/https: do NOT blow up — their validator requires the tail to start with //, failing in O(1) per hit.

Proof of Concept (confirmed, linkify-it 5.0.1, Node v24)

const LinkifyIt = require('linkify-it');
const lf = new LinkifyIt();
lf.match('mailto:'.repeat(48000));   // ~336 KB of "mailto:mailto:…" -> seconds of blocked event loop

| input (same bytes) | 56 KB | 112 KB | 224 KB | 336 KB | |---|---:|---:|---:|---:| | mailto: contiguous | 97 ms | 357 ms | 1438 ms | 3272 ms | | mailto: space-separated | 2 ms | 3 ms | 5 ms | 8 ms | | http:// contiguous | 12 ms | 17 ms | 33 ms | 49 ms |

×~4 per 2× input ⇒ O(n²); equal-byte controls stay flat ⇒ algorithmic, not a GC/allocation artifact. Real-world via markdown-it 14.x ({linkify:true}), md.render('mailto:'.repeat(n)): 219 KB ≈ ~5 s. <img width="737" height="161" alt="image" src="https://github.com/user-attachments/assets/b5d390f3-68d0-4861-9c47-ad8aff0203d5" />

Impact

Reachable on arbitrary user text via the documented .test()/.match() API and through markdown-it's linkifier — comment systems, chat, forums, wikis, note apps that render user markdown with linkify enabled. A ~220 KB post hangs the event loop ~5 s; a few hundred KB → tens of seconds. Availability only.

Suggested remediation

Bound the email local-part per RFC 5321 (≤64) so per-hit work is O(1), and avoid the full-tail slice:

// lib/re.mjs — cap the greedy run:
re.src_email_name = '[\\-;:&=\\+\\$,\\.a-zA-Z0-9_][\\-;:&=\\+\\$,\\"\\.a-zA-Z0-9_]{0,63}'
// index.mjs — prefer a sticky regex anchored at `pos` over text.slice(pos).

Affected / disclosure

All versions through 5.0.1 (latest); same code on master. cve-mcp/OSV report no known vulnerability for linkify-it. Distinct from markdown-it's own *-run ReDoS (CVE-2026-2327, different package/path) and the recent markdown-it DoS. Reported privately; happy to test a patch against the PoC.

Record
GHSA-v245-v573-v5vm
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-407
Updated
2026-11-02
Show 7 moreShow less
OSVCLSA-2026-1784682996 No score

TuxCare security update for linkify-it (1 CVE)

TuxCare rebuilt linkify-it to address 1 CVE. Fixed in 1.2.4-tuxcare.1.

Record
CLSA-2026-1784682996
Updated
2026-11-02
OSVCLSA-2026-1784856031 No score

TuxCare security update for linkify-it (1 CVE)

TuxCare rebuilt linkify-it to address 1 CVE. Fixed in 4.0.1-tuxcare.1.

Record
CLSA-2026-1784856031
Updated
2026-11-02
OSVCLSA-2026-1790520316 No score

TuxCare security update for linkify-it (1 CVE)

TuxCare rebuilt linkify-it to address 1 CVE. Fixed in 2.2.0-tuxcare.1.

Record
CLSA-2026-1790520316
Updated
2026-11-02
OSVCLSA-2026-1792489196 No score

TuxCare security update for linkify-it (1 CVE)

TuxCare rebuilt linkify-it to address 1 CVE. Fixed in 1.0.1-tuxcare.1.

Record
CLSA-2026-1792489196
Updated
2026-11-02
OSVCLSA-2026-1792489445 No score

TuxCare security update for linkify-it (1 CVE)

TuxCare rebuilt linkify-it to address 1 CVE. Fixed in 3.0.3-tuxcare.1.

Record
CLSA-2026-1792489445
Updated
2026-11-02
OSVMGASA-2026-0377 No score

Updated python-linkify-it-py package fixes security vulnerabilities

LinkifyIt.match() was quadratic on untrusted input when linkify is enabled (GHSA-8m2q-wq3r-6hq8). Fix quadratic complexity in LinkifyIt.match(), port of linkify-it 5.0.1 and 5.0.2 (upstream CVE-2026-48801, CVE-2026-59887) (#82) Match validators at a position instead of slicing the tail, needed because Python 3.10 does not optimize the ^ anchor (#82) Allow ; in the email name, matching linkify-it. Behavior change: a;[email protected] is now linkified (#82)

Record
MGASA-2026-0377
Updated
2026-11-02
OSVROOT-APP-NPM-CVE-2026-59887 No score

CVE-2026-59887 in linkify-it - Patched by Root

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

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

Affected products

Affected packages

Affected packages
PackageAffectedFixed
linkify-it
npm
through 5.0.1
5.0.2
linkify-it
TuxCare:npm
1.2.4-tuxcare.0 up to 1.2.4-tuxcare.1, plus listed versions
1.2.4
1.2.4-tuxcare.1
linkify-it
TuxCare:npm
4.0.1-tuxcare.0 up to 4.0.1-tuxcare.1, plus listed versions
4.0.1
4.0.1-tuxcare.1
linkify-it
TuxCare:npm
2.2.0-tuxcare.0 up to 2.2.0-tuxcare.1, plus listed versions
2.2.0
2.2.0-tuxcare.1
linkify-it
TuxCare:npm
1.0.1-tuxcare.0 up to 1.0.1-tuxcare.1, plus listed versions
1.0.1
1.0.1-tuxcare.1
linkify-it
TuxCare:npm
3.0.3-tuxcare.0 up to 3.0.3-tuxcare.1, plus listed versions
3.0.3
3.0.3-tuxcare.1
linkify-it
npm
before 5.0.2
5.0.2
python-linkify-it-py
Mageia:10
From an advisory covering 2 CVEs
before 2.1.1-1.mga10
2.1.1-1.mga10
linkify-it
Root:npm
before 5.0.0-aikido.1
5.0.0-aikido.15.0.1-aikido.12.2.0-aikido.24.0.1-aikido.1
@rootio/linkify-it
Root:npm
before 5.0.0-root.io.1
5.0.0-root.io.15.0.1-root.io.12.2.0-root.io.24.0.1-root.io.1

History

History
RecordedChangedSources
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-59887 is a high vulnerability (CVSS 7.5) in Markdown-it Linkify-it, published July 8, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.47%, the 40th percentile. Fixed in linkify-it 5.0.2.

References

Show 4 moreShow less
MoveEnter Open