CVE-2026-59887
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.
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
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 High
CVSS: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 High
CVSS: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 High
CVSS: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 High
CVSS: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 High
CVSS: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 High
CVSS: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
| Package | Affected | Fixed |
|---|---|---|
linkify-itnpm |
through 5.0.1 | 5.0.2 |
linkify-itTuxCare: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-itTuxCare: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-itTuxCare: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-itTuxCare: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-itTuxCare: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-itnpm |
before 5.0.2 | 5.0.2 |
python-linkify-it-pyMageia:10 From an advisory covering 2 CVEs |
before 2.1.1-1.mga10 | 2.1.1-1.mga10 |
linkify-itRoot: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-itRoot: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
| Recorded | Changed | Sources |
|---|---|---|
| affected, facts | ||
| 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-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
github.com/markdown-it/linkify-it/security/advisories/GHSA-v245-v573-v5vm
github.com/markdown-it/linkify-it/commit/105e5d77f7d119871d2b2d86ed208568eb3e7ffe
github.com/markdown-it/linkify-it/releases/tag/5.0.2
nvd.nist.gov/vuln/detail/CVE-2026-59887
github.com/advisories/GHSA-v245-v573-v5vm
github.com/CVEProject/cvelistV5/tree/main/cves/2026/59xxx/CVE-2026-59887.json