{"id":"CVE-2026-59887","aliases":["CLSA-2026-1784682996","CLSA-2026-1784856031","CLSA-2026-1790520316","CLSA-2026-1792489196","CLSA-2026-1792489445","EUVD-2026-42321","GHSA-v245-v573-v5vm","MGASA-2026-0377","ROOT-APP-NPM-CVE-2026-59887"],"status":"published","title":"linkify-it: Quadratic-complexity DoS via the `mailto:` validator scan-loop on attacker text","description":"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.","severity":{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-12","score":0.00467,"percentile":0.40905,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-407"],"published":"2026-07-08T15:58:12Z","modified":"2026-10-28T02:31:12Z","fixes":[{"ecosystem":"npm","package":"linkify-it","fixed_in":["5.0.2"],"sources":["ghsa","osv"]},{"ecosystem":"TuxCare:npm","package":"linkify-it","fixed_in":["1.2.4-tuxcare.1","4.0.1-tuxcare.1","2.2.0-tuxcare.1","1.0.1-tuxcare.1","3.0.3-tuxcare.1"],"sources":["osv"]},{"ecosystem":"Root:npm","package":"linkify-it","fixed_in":["5.0.0-aikido.1","5.0.1-aikido.1","2.2.0-aikido.2","4.0.1-aikido.1"],"sources":["osv"]},{"ecosystem":"Root:npm","package":"@rootio/linkify-it","fixed_in":["5.0.0-root.io.1","5.0.1-root.io.1","2.2.0-root.io.2","4.0.1-root.io.1"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"npm","package":"linkify-it","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"5.0.1"},{"fixed":"5.0.2"}]}]},{"source":"osv","ecosystem":"TuxCare:npm","package":"linkify-it","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.2.4-tuxcare.0"},{"fixed":"1.2.4-tuxcare.1"}]}],"versions":["1.2.4"]},{"source":"osv","ecosystem":"TuxCare:npm","package":"linkify-it","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"4.0.1-tuxcare.0"},{"fixed":"4.0.1-tuxcare.1"}]}],"versions":["4.0.1"]},{"source":"osv","ecosystem":"TuxCare:npm","package":"linkify-it","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"2.2.0-tuxcare.0"},{"fixed":"2.2.0-tuxcare.1"}]}],"versions":["2.2.0"]},{"source":"osv","ecosystem":"TuxCare:npm","package":"linkify-it","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.0.1-tuxcare.0"},{"fixed":"1.0.1-tuxcare.1"}]}],"versions":["1.0.1"]},{"source":"osv","ecosystem":"TuxCare:npm","package":"linkify-it","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"3.0.3-tuxcare.0"},{"fixed":"3.0.3-tuxcare.1"}]}],"versions":["3.0.3"]},{"source":"osv","ecosystem":"npm","package":"linkify-it","ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"5.0.2"}]}]},{"source":"osv","ecosystem":"Mageia:10","package":"python-linkify-it-py","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.1.1-1.mga10"}]}],"bundled":2},{"source":"osv","ecosystem":"Root:npm","package":"linkify-it","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"5.0.0-aikido.1"},{"fixed":"5.0.1-aikido.1"},{"fixed":"2.2.0-aikido.2"},{"fixed":"4.0.1-aikido.1"}]}]},{"source":"osv","ecosystem":"Root:npm","package":"@rootio/linkify-it","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"5.0.0-root.io.1"},{"fixed":"5.0.1-root.io.1"},{"fixed":"2.2.0-root.io.2"},{"fixed":"4.0.1-root.io.1"}]}]}],"products":[{"vendor":"markdown-it","product":"linkify-it"}],"references":[{"url":"https://github.com/markdown-it/linkify-it/security/advisories/GHSA-v245-v573-v5vm","tags":["x_refsource_CONFIRM","exploit","Exploit","Mitigation","Vendor Advisory","advisory","web"]},{"url":"https://github.com/markdown-it/linkify-it/commit/105e5d77f7d119871d2b2d86ed208568eb3e7ffe","tags":["x_refsource_MISC","Patch","fix","web"]},{"url":"https://github.com/markdown-it/linkify-it/releases/tag/5.0.2","tags":["x_refsource_MISC","Release Notes","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-59887","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-v245-v573-v5vm"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/59xxx/CVE-2026-59887.json","tags":["advisory"]},{"url":"https://github.com/markdown-it/linkify-it","tags":["package"]},{"url":"https://advisories.mageia.org/MGASA-2026-0377.html","tags":["advisory"]},{"url":"https://bugs.mageia.org/show_bug.cgi?id=36247","tags":["report"]},{"url":"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/DZROSWVUL5NSOK5V2O56ZMD6SRN2KV7V/","tags":["web"]}],"sources":{"cve":[{"id":"CVE-2026-59887","title":"linkify-it: Quadratic-complexity DoS via the `mailto:` validator scan-loop on attacker text","description":"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.","status":"published","published":"2026-07-08T15:58:12.114Z","modified":"2026-07-09T14:41:02.113Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high","assessor":"cna"}],"cwes":["CWE-407"],"updated_at":"2026-11-02T14:19:58.560602Z"}],"euvd":[{"id":"EUVD-2026-42321","description":"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.","status":"published","published":"2026-07-08T15:58:12Z","modified":"2026-07-09T14:41:02Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"updated_at":"2026-11-02T18:20:04.126011Z"}],"ghsa":[{"id":"GHSA-v245-v573-v5vm","title":"linkify-it: Quadratic-complexity DoS via the `mailto:` validator scan-loop on attacker text","description":"### Summary\n`linkify-it`'s schema-scan loop (`.test()` / `.match()`, the documented public API) invokes the `mailto:`\nschema validator at **every** `mailto:` occurrence in the input text. For each occurrence the validator does\n`text.slice(pos)` (an O(n) copy) and runs an email regex whose local-part class `src_email_name` greedily\nscans the **entire remaining tail** (O(n)) before failing. With N `mailto:` occurrences that is\n**N × O(n) = O(n²)**. Because linkify-it runs on arbitrary user text (markdown-it feeds it whole documents\nwhen `linkify:true`), an unauthenticated attacker can block the single-threaded event loop for many seconds\nwith a small input. No length bound (unlike an HTTP header).\n\n### Root cause — `index.mjs` + `lib/re.mjs`\n```js\n// index.mjs (mailto validator) — runs at every \"mailto:\" hit\n'mailto:': { validate: function (text, pos, self) {\n  const tail = text.slice(pos)                                  // O(n) copy per hit\n  if (!self.re.mailto) self.re.mailto = new RegExp('^' + self.re.src_email_name + '@' + self.re.src_host_strict, 'i')\n  if (self.re.mailto.test(tail)) { ... }                        // scans the whole O(n) tail\n  return 0\n}}\n// lib/re.mjs:91-93 — every char of \"mailto:\" (incl. ':','-',';') is in this class:\nre.src_email_name = '[\\\\-;:\u0026=\\\\+\\\\$,\\\\.a-zA-Z0-9_][\\\\-;:\u0026=\\\\+\\\\$,\\\\\"\\\\.a-zA-Z0-9_]*'\n```\nThe `while ((m = re.exec(text)) !== null) { …testSchemaAt… }` scan loop calls the validator at each\n`mailto:` hit; `src_email_name` greedily consumes the whole tail (all chars are in its class) then fails for\nlack of `@`. `http:`/`https:` do NOT blow up — their validator requires the tail to start with `//`, failing\nin O(1) per hit.\n\n### Proof of Concept (confirmed, linkify-it 5.0.1, Node v24)\n```js\nconst LinkifyIt = require('linkify-it');\nconst lf = new LinkifyIt();\nlf.match('mailto:'.repeat(48000));   // ~336 KB of \"mailto:mailto:…\" -\u003e seconds of blocked event loop\n```\n| input (same bytes) | 56 KB | 112 KB | 224 KB | 336 KB |\n|---|---:|---:|---:|---:|\n| **`mailto:` contiguous** | 97 ms | 357 ms | 1438 ms | 3272 ms |\n| `mailto:` space-separated | 2 ms | 3 ms | 5 ms | 8 ms |\n| `http://` contiguous | 12 ms | 17 ms | 33 ms | 49 ms |\n\n×~4 per 2× input ⇒ O(n²); equal-byte controls stay flat ⇒ algorithmic, not a GC/allocation artifact.\nReal-world via markdown-it 14.x (`{linkify:true}`), `md.render('mailto:'.repeat(n))`: 219 KB ≈ ~5 s.\n\u003cimg width=\"737\" height=\"161\" alt=\"image\" src=\"https://github.com/user-attachments/assets/b5d390f3-68d0-4861-9c47-ad8aff0203d5\" /\u003e\n\n### Impact\nReachable on arbitrary user text via the documented `.test()`/`.match()` API and through markdown-it's\nlinkifier — comment systems, chat, forums, wikis, note apps that render user markdown with linkify enabled.\nA ~220 KB post hangs the event loop ~5 s; a few hundred KB → tens of seconds. Availability only.\n\n### Suggested remediation\nBound the email local-part per RFC 5321 (≤64) so per-hit work is O(1), and avoid the full-tail slice:\n```js\n// lib/re.mjs — cap the greedy run:\nre.src_email_name = '[\\\\-;:\u0026=\\\\+\\\\$,\\\\.a-zA-Z0-9_][\\\\-;:\u0026=\\\\+\\\\$,\\\\\"\\\\.a-zA-Z0-9_]{0,63}'\n// index.mjs — prefer a sticky regex anchored at `pos` over text.slice(pos).\n```\n\n### Affected / disclosure\nAll versions through 5.0.1 (latest); same code on `master`. cve-mcp/OSV report no known vulnerability for\nlinkify-it. Distinct from markdown-it's own `*`-run ReDoS (CVE-2026-2327, different package/path) and the\nrecent markdown-it DoS. Reported privately; happy to test a patch against the PoC.","status":"published","published":"2026-07-21T19:06:09Z","modified":"2026-07-21T19:06:10Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"cwes":["CWE-407"],"updated_at":"2026-11-02T14:40:50.444681Z"}],"nvd":[{"id":"CVE-2026-59887","description":"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.","status":"published","published":"2026-07-08T17:17:26.883Z","modified":"2026-10-25T17:10:00.197Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high","assessor":"secondary"}],"cwes":["CWE-407"],"updated_at":"2026-11-02T14:12:28.040501Z"}],"osv":[{"id":"CVE-2026-59887","title":"linkify-it: Quadratic-complexity DoS via the `mailto:` validator scan-loop on attacker text","description":"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.","status":"published","published":"2026-07-08T15:58:12.114Z","modified":"2026-10-28T02:31:12.671587626Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"cwes":["CWE-407"],"updated_at":"2026-11-02T15:45:12.496548Z"},{"id":"GHSA-v245-v573-v5vm","title":"linkify-it: Quadratic-complexity DoS via the `mailto:` validator scan-loop on attacker text","description":"### Summary\n`linkify-it`'s schema-scan loop (`.test()` / `.match()`, the documented public API) invokes the `mailto:`\nschema validator at **every** `mailto:` occurrence in the input text. For each occurrence the validator does\n`text.slice(pos)` (an O(n) copy) and runs an email regex whose local-part class `src_email_name` greedily\nscans the **entire remaining tail** (O(n)) before failing. With N `mailto:` occurrences that is\n**N × O(n) = O(n²)**. Because linkify-it runs on arbitrary user text (markdown-it feeds it whole documents\nwhen `linkify:true`), an unauthenticated attacker can block the single-threaded event loop for many seconds\nwith a small input. No length bound (unlike an HTTP header).\n\n### Root cause — `index.mjs` + `lib/re.mjs`\n```js\n// index.mjs (mailto validator) — runs at every \"mailto:\" hit\n'mailto:': { validate: function (text, pos, self) {\n  const tail = text.slice(pos)                                  // O(n) copy per hit\n  if (!self.re.mailto) self.re.mailto = new RegExp('^' + self.re.src_email_name + '@' + self.re.src_host_strict, 'i')\n  if (self.re.mailto.test(tail)) { ... }                        // scans the whole O(n) tail\n  return 0\n}}\n// lib/re.mjs:91-93 — every char of \"mailto:\" (incl. ':','-',';') is in this class:\nre.src_email_name = '[\\\\-;:\u0026=\\\\+\\\\$,\\\\.a-zA-Z0-9_][\\\\-;:\u0026=\\\\+\\\\$,\\\\\"\\\\.a-zA-Z0-9_]*'\n```\nThe `while ((m = re.exec(text)) !== null) { …testSchemaAt… }` scan loop calls the validator at each\n`mailto:` hit; `src_email_name` greedily consumes the whole tail (all chars are in its class) then fails for\nlack of `@`. `http:`/`https:` do NOT blow up — their validator requires the tail to start with `//`, failing\nin O(1) per hit.\n\n### Proof of Concept (confirmed, linkify-it 5.0.1, Node v24)\n```js\nconst LinkifyIt = require('linkify-it');\nconst lf = new LinkifyIt();\nlf.match('mailto:'.repeat(48000));   // ~336 KB of \"mailto:mailto:…\" -\u003e seconds of blocked event loop\n```\n| input (same bytes) | 56 KB | 112 KB | 224 KB | 336 KB |\n|---|---:|---:|---:|---:|\n| **`mailto:` contiguous** | 97 ms | 357 ms | 1438 ms | 3272 ms |\n| `mailto:` space-separated | 2 ms | 3 ms | 5 ms | 8 ms |\n| `http://` contiguous | 12 ms | 17 ms | 33 ms | 49 ms |\n\n×~4 per 2× input ⇒ O(n²); equal-byte controls stay flat ⇒ algorithmic, not a GC/allocation artifact.\nReal-world via markdown-it 14.x (`{linkify:true}`), `md.render('mailto:'.repeat(n))`: 219 KB ≈ ~5 s.\n\u003cimg width=\"737\" height=\"161\" alt=\"image\" src=\"https://github.com/user-attachments/assets/b5d390f3-68d0-4861-9c47-ad8aff0203d5\" /\u003e\n\n### Impact\nReachable on arbitrary user text via the documented `.test()`/`.match()` API and through markdown-it's\nlinkifier — comment systems, chat, forums, wikis, note apps that render user markdown with linkify enabled.\nA ~220 KB post hangs the event loop ~5 s; a few hundred KB → tens of seconds. Availability only.\n\n### Suggested remediation\nBound the email local-part per RFC 5321 (≤64) so per-hit work is O(1), and avoid the full-tail slice:\n```js\n// lib/re.mjs — cap the greedy run:\nre.src_email_name = '[\\\\-;:\u0026=\\\\+\\\\$,\\\\.a-zA-Z0-9_][\\\\-;:\u0026=\\\\+\\\\$,\\\\\"\\\\.a-zA-Z0-9_]{0,63}'\n// index.mjs — prefer a sticky regex anchored at `pos` over text.slice(pos).\n```\n\n### Affected / disclosure\nAll versions through 5.0.1 (latest); same code on `master`. cve-mcp/OSV report no known vulnerability for\nlinkify-it. Distinct from markdown-it's own `*`-run ReDoS (CVE-2026-2327, different package/path) and the\nrecent markdown-it DoS. Reported privately; happy to test a patch against the PoC.","status":"published","published":"2026-07-21T19:06:09Z","modified":"2026-09-10T03:51:12.054179363Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"cwes":["CWE-407"],"updated_at":"2026-11-02T15:46:56.639206Z"},{"id":"CLSA-2026-1784682996","title":"TuxCare security update for linkify-it (1 CVE)","description":"TuxCare rebuilt linkify-it to address 1 CVE. Fixed in 1.2.4-tuxcare.1.","status":"published","published":"2026-08-04T14:52:46Z","modified":"2026-10-15T19:13:11.092492585Z","updated_at":"2026-11-02T15:43:46.482572Z"},{"id":"CLSA-2026-1784856031","title":"TuxCare security update for linkify-it (1 CVE)","description":"TuxCare rebuilt linkify-it to address 1 CVE. Fixed in 4.0.1-tuxcare.1.","status":"published","published":"2026-08-04T14:52:48Z","modified":"2026-10-15T19:13:04.645413597Z","updated_at":"2026-11-02T15:43:46.482572Z"},{"id":"CLSA-2026-1790520316","title":"TuxCare security update for linkify-it (1 CVE)","description":"TuxCare rebuilt linkify-it to address 1 CVE. Fixed in 2.2.0-tuxcare.1.","status":"published","published":"2026-09-27T14:45:16Z","modified":"2026-10-15T09:45:09.996149631Z","updated_at":"2026-11-02T15:43:48.65893Z"},{"id":"CLSA-2026-1792489196","title":"TuxCare security update for linkify-it (1 CVE)","description":"TuxCare rebuilt linkify-it to address 1 CVE. Fixed in 1.0.1-tuxcare.1.","status":"published","published":"2026-10-20T09:39:56Z","modified":"2026-10-20T13:45:12.459722558Z","updated_at":"2026-11-02T15:43:54.378946Z"},{"id":"CLSA-2026-1792489445","title":"TuxCare security update for linkify-it (1 CVE)","description":"TuxCare rebuilt linkify-it to address 1 CVE. Fixed in 3.0.3-tuxcare.1.","status":"published","published":"2026-10-20T09:44:05Z","modified":"2026-10-20T13:45:12.466717496Z","updated_at":"2026-11-02T15:43:54.378946Z"},{"id":"MGASA-2026-0377","title":"Updated python-linkify-it-py package fixes security vulnerabilities","description":"LinkifyIt.match() was quadratic on untrusted input when linkify is\nenabled (GHSA-8m2q-wq3r-6hq8).\nFix quadratic complexity in LinkifyIt.match(), port of linkify-it 5.0.1\nand 5.0.2 (upstream CVE-2026-48801, CVE-2026-59887) (#82)\nMatch validators at a position instead of slicing the tail, needed\nbecause Python 3.10 does not optimize the ^ anchor (#82)\nAllow ; in the email name, matching linkify-it. Behavior change:\na;b@example.com is now linkified (#82)\n","status":"published","published":"2026-09-05T04:35:57Z","modified":"2026-09-05T04:45:03.246249689Z","updated_at":"2026-11-02T15:49:43.795591Z"},{"id":"ROOT-APP-NPM-CVE-2026-59887","title":"CVE-2026-59887 in linkify-it - Patched by Root","description":"Root has patched CVE-2026-59887 in the linkify-it package for Root:npm. Multiple fixed versions available.","status":"published","published":"2026-10-20T06:42:43Z","modified":"2026-10-20T07:15:12.161458489Z","updated_at":"2026-11-02T15:53:27.649662Z"}]}}
