CVE-2026-82562 (GCVE-0-2026-82562)
Vulnerability from cvelistv5
Published
2026-08-29 23:58
Modified
2026-08-31 17:04
Severity ?
3.7 (Low) - CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L
6.3 (Medium) - CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N
6.3 (Medium) - CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N
VLAI Severity ?
EPSS score ?
CWE
- CWE-770 - Allocation of Resources Without Limits or Throttling
Summary
### Summary
When `qs.parse` is called with `comma: true` and `throwOnLimitExceeded: true`, a comma-separated value under a bracket-push key (`a[]=1,2,3,4`) is split into an array without being compared against `arrayLimit`, while the same value under a flat key (`a=1,2,3,4`), an indexed key (`a[0]=`), a nested key (`a[b]=`), or a dotted key (`a.b=` with `allowDots`) throws the documented `RangeError`. A single parameter such as `a[]=1,2,2,...` therefore produces an inner array of arbitrary length even though the caller opted into the hard limit. This is the `[]=` key form that the fix for CVE-2026-2391 (qs 6.14.2) did not cover.
### Details
In `lib/parse.js`, a comma-separated value under a `[]=` key is split and then wrapped as a single nested element (`val = [val]`, so that each `a[]=x,y` group counts as one element of the outer array). The `arrayLimit` check that 6.14.2 added for comma values runs after that wrap, so for `[]=` parts it only ever saw the wrapper of length 1. 6.15.3 added a pre-split comma count so that an oversized value throws before it is allocated, but gated it on an `isFlatArrayValue` flag that `parseValues` set to `false` for any part containing `[]=`, and did not pass it for object-valued input, so the gap remained.
#### PoC
```js
var qs = require('qs');
var options = { comma: true, arrayLimit: 3, throwOnLimitExceeded: true };
qs.parse('a=1,2,3,4', options); // RangeError: Array limit exceeded. Only 3 elements allowed in an array.
qs.parse('a[]=1,2,3,4', options); // { a: [ [ '1', '2', '3', '4' ] ] } (no throw)
qs.parse('a[]=' + '1,'.repeat(1000000) + '1', { comma: true, arrayLimit: 20, throwOnLimitExceeded: true });
// no throw; a 1,000,001-element inner array is allocated
```
#### Fix
`lib/parse.js`, applied in 8859c37 on `main` and released as v6.16.0: the `isFlatArrayValue` gate is removed, so every comma-split value is counted against `arrayLimit` before splitting regardless of key form. An in-limit group under `a[]=` still counts as one element of the outer array, and the default (`throwOnLimitExceeded: false`) path is unchanged.
### Affected versions
`>=6.14.2 <6.16.0`, fixed in v6.16.0.
v6.14.2 introduced `arrayLimit` enforcement for comma values (the fix for CVE-2026-2391) but only for values not under a `[]=` key, and every release from v6.14.2 through v6.15.3 has the same gap. v6.14.0 and v6.14.1, where `throwOnLimitExceeded` exists but does not apply to any comma form, are covered by CVE-2026-2391 rather than this record. Earlier lines (6.7.x through 6.13.x) have `comma` but no `throwOnLimitExceeded`, so there is no hard cap on any comma path to bypass; releases before 6.7.0 have no `comma` option.
### Impact
An unauthenticated attacker who can reach an application that parses untrusted query strings or urlencoded bodies with both `comma: true` and `throwOnLimitExceeded: true` (both non-default) can bypass the configured limit with a single `a[]=` parameter and force the parser to allocate an array proportional to the request size. The cost is strictly linear in the attacker-supplied bytes (about 0.1 microseconds and 6 to 7 retained bytes per input byte; the same out-of-memory threshold as the documented default `throwOnLimitExceeded: false` path), so a transport-layer request or body size limit bounds it completely (and node's default maximum HTTP header size of 16 KB already bounds the request line, so multi-megabyte payloads need a body parser). The impact is that an opt-in hard limit fails open on one key spelling, not unbounded allocation from a small input.
References
{
"containers": {
"adp": [
{
"metrics": [
{
"other": {
"content": {
"id": "CVE-2026-82562",
"options": [
{
"Exploitation": "poc"
},
{
"Automatable": "no"
},
{
"Technical Impact": "partial"
}
],
"role": "CISA Coordinator",
"timestamp": "2026-08-31T17:03:54.217257Z",
"version": "2.0.3"
},
"type": "ssvc"
}
}
],
"providerMetadata": {
"dateUpdated": "2026-08-31T17:04:14.950Z",
"orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
"shortName": "CISA-ADP"
},
"references": [
{
"tags": [
"exploit"
],
"url": "https://github.com/ljharb/qs/security/advisories/GHSA-x5fp-wj9c-mxmx"
}
],
"title": "CISA ADP Vulnrichment"
}
],
"cna": {
"affected": [
{
"collectionURL": "https://npmjs.com/qs",
"defaultStatus": "unaffected",
"packageName": "qs",
"product": "qs",
"repo": "https://github.com/ljharb/qs",
"vendor": "ljharb",
"versions": [
{
"lessThan": "6.16.0",
"status": "affected",
"version": "6.14.2",
"versionType": "semver"
}
]
}
],
"credits": [
{
"lang": "en",
"type": "reporter",
"value": "Vectrain51"
},
{
"lang": "en",
"type": "reporter",
"value": "Fcmam5"
},
{
"lang": "en",
"type": "remediation developer",
"value": "ljharb"
}
],
"descriptions": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "\u003cp\u003e### Summary\u003c/p\u003e\u003cp\u003eWhen `qs.parse` is called with `comma: true` and `throwOnLimitExceeded: true`, a comma-separated value under a bracket-push key (`a[]=1,2,3,4`) is split into an array without being compared against `arrayLimit`, while the same value under a flat key (`a=1,2,3,4`), an indexed key (`a[0]=`), a nested key (`a[b]=`), or a dotted key (`a.b=` with `allowDots`) throws the documented `RangeError`. A single parameter such as `a[]=1,2,2,...` therefore produces an inner array of arbitrary length even though the caller opted into the hard limit. This is the `[]=` key form that the fix for CVE-2026-2391 (qs 6.14.2) did not cover.\u003c/p\u003e\u003cp\u003e### Details\u003c/p\u003e\u003cp\u003eIn `lib/parse.js`, a comma-separated value under a `[]=` key is split and then wrapped as a single nested element (`val = [val]`, so that each `a[]=x,y` group counts as one element of the outer array). The `arrayLimit` check that 6.14.2 added for comma values runs after that wrap, so for `[]=` parts it only ever saw the wrapper of length 1. 6.15.3 added a pre-split comma count so that an oversized value throws before it is allocated, but gated it on an `isFlatArrayValue` flag that `parseValues` set to `false` for any part containing `[]=`, and did not pass it for object-valued input, so the gap remained.\u003c/p\u003e\u003cp\u003e#### PoC\u003c/p\u003e\u003cp\u003e```js\u003c/p\u003e\u003cp\u003evar qs = require(\u0027qs\u0027);\u003c/p\u003e\u003cp\u003evar options = { comma: true, arrayLimit: 3, throwOnLimitExceeded: true };\u003c/p\u003e\u003cp\u003eqs.parse(\u0027a=1,2,3,4\u0027, options); // RangeError: Array limit exceeded. Only 3 elements allowed in an array.\u003c/p\u003e\u003cp\u003eqs.parse(\u0027a[]=1,2,3,4\u0027, options); // { a: [ [ \u00271\u0027, \u00272\u0027, \u00273\u0027, \u00274\u0027 ] ] } (no throw)\u003c/p\u003e\u003cp\u003eqs.parse(\u0027a[]=\u0027 + \u00271,\u0027.repeat(1000000) + \u00271\u0027, { comma: true, arrayLimit: 20, throwOnLimitExceeded: true });\u003c/p\u003e\u003cp\u003e// no throw; a 1,000,001-element inner array is allocated\u003c/p\u003e\u003cp\u003e```\u003c/p\u003e\u003cp\u003e#### Fix\u003c/p\u003e\u003cp\u003e`lib/parse.js`, applied in 8859c37 on `main` and released as v6.16.0: the `isFlatArrayValue` gate is removed, so every comma-split value is counted against `arrayLimit` before splitting regardless of key form. An in-limit group under `a[]=` still counts as one element of the outer array, and the default (`throwOnLimitExceeded: false`) path is unchanged.\u003c/p\u003e\u003cp\u003e### Affected versions\u003c/p\u003e\u003cp\u003e`\u0026gt;=6.14.2 \u0026lt;6.16.0`, fixed in v6.16.0.\u003c/p\u003e\u003cp\u003ev6.14.2 introduced `arrayLimit` enforcement for comma values (the fix for CVE-2026-2391) but only for values not under a `[]=` key, and every release from v6.14.2 through v6.15.3 has the same gap. v6.14.0 and v6.14.1, where `throwOnLimitExceeded` exists but does not apply to any comma form, are covered by CVE-2026-2391 rather than this record. Earlier lines (6.7.x through 6.13.x) have `comma` but no `throwOnLimitExceeded`, so there is no hard cap on any comma path to bypass; releases before 6.7.0 have no `comma` option.\u003c/p\u003e\u003cp\u003e### Impact\u003c/p\u003e\u003cp\u003eAn unauthenticated attacker who can reach an application that parses untrusted query strings or urlencoded bodies with both `comma: true` and `throwOnLimitExceeded: true` (both non-default) can bypass the configured limit with a single `a[]=` parameter and force the parser to allocate an array proportional to the request size. The cost is strictly linear in the attacker-supplied bytes (about 0.1 microseconds and 6 to 7 retained bytes per input byte; the same out-of-memory threshold as the documented default `throwOnLimitExceeded: false` path), so a transport-layer request or body size limit bounds it completely (and node\u0027s default maximum HTTP header size of 16 KB already bounds the request line, so multi-megabyte payloads need a body parser). The impact is that an opt-in hard limit fails open on one key spelling, not unbounded allocation from a small input.\u003c/p\u003e"
}
],
"value": "### Summary\n\n\n\nWhen `qs.parse` is called with `comma: true` and `throwOnLimitExceeded: true`, a comma-separated value under a bracket-push key (`a[]=1,2,3,4`) is split into an array without being compared against `arrayLimit`, while the same value under a flat key (`a=1,2,3,4`), an indexed key (`a[0]=`), a nested key (`a[b]=`), or a dotted key (`a.b=` with `allowDots`) throws the documented `RangeError`. A single parameter such as `a[]=1,2,2,...` therefore produces an inner array of arbitrary length even though the caller opted into the hard limit. This is the `[]=` key form that the fix for CVE-2026-2391 (qs 6.14.2) did not cover.\n\n\n\n### Details\n\n\n\nIn `lib/parse.js`, a comma-separated value under a `[]=` key is split and then wrapped as a single nested element (`val = [val]`, so that each `a[]=x,y` group counts as one element of the outer array). The `arrayLimit` check that 6.14.2 added for comma values runs after that wrap, so for `[]=` parts it only ever saw the wrapper of length 1. 6.15.3 added a pre-split comma count so that an oversized value throws before it is allocated, but gated it on an `isFlatArrayValue` flag that `parseValues` set to `false` for any part containing `[]=`, and did not pass it for object-valued input, so the gap remained.\n\n\n\n#### PoC\n\n\n\n```js\n\n\n\nvar qs = require(\u0027qs\u0027);\n\n\n\nvar options = { comma: true, arrayLimit: 3, throwOnLimitExceeded: true };\n\n\n\nqs.parse(\u0027a=1,2,3,4\u0027, options); // RangeError: Array limit exceeded. Only 3 elements allowed in an array.\n\n\n\nqs.parse(\u0027a[]=1,2,3,4\u0027, options); // { a: [ [ \u00271\u0027, \u00272\u0027, \u00273\u0027, \u00274\u0027 ] ] } (no throw)\n\n\n\nqs.parse(\u0027a[]=\u0027 + \u00271,\u0027.repeat(1000000) + \u00271\u0027, { comma: true, arrayLimit: 20, throwOnLimitExceeded: true });\n\n\n\n// no throw; a 1,000,001-element inner array is allocated\n\n\n\n```\n\n\n\n#### Fix\n\n\n\n`lib/parse.js`, applied in 8859c37 on `main` and released as v6.16.0: the `isFlatArrayValue` gate is removed, so every comma-split value is counted against `arrayLimit` before splitting regardless of key form. An in-limit group under `a[]=` still counts as one element of the outer array, and the default (`throwOnLimitExceeded: false`) path is unchanged.\n\n\n\n### Affected versions\n\n\n\n`\u003e=6.14.2 \u003c6.16.0`, fixed in v6.16.0.\n\n\n\nv6.14.2 introduced `arrayLimit` enforcement for comma values (the fix for CVE-2026-2391) but only for values not under a `[]=` key, and every release from v6.14.2 through v6.15.3 has the same gap. v6.14.0 and v6.14.1, where `throwOnLimitExceeded` exists but does not apply to any comma form, are covered by CVE-2026-2391 rather than this record. Earlier lines (6.7.x through 6.13.x) have `comma` but no `throwOnLimitExceeded`, so there is no hard cap on any comma path to bypass; releases before 6.7.0 have no `comma` option.\n\n\n\n### Impact\n\n\n\nAn unauthenticated attacker who can reach an application that parses untrusted query strings or urlencoded bodies with both `comma: true` and `throwOnLimitExceeded: true` (both non-default) can bypass the configured limit with a single `a[]=` parameter and force the parser to allocate an array proportional to the request size. The cost is strictly linear in the attacker-supplied bytes (about 0.1 microseconds and 6 to 7 retained bytes per input byte; the same out-of-memory threshold as the documented default `throwOnLimitExceeded: false` path), so a transport-layer request or body size limit bounds it completely (and node\u0027s default maximum HTTP header size of 16 KB already bounds the request line, so multi-megabyte payloads need a body parser). The impact is that an opt-in hard limit fails open on one key spelling, not unbounded allocation from a small input."
}
],
"metrics": [
{
"cvssV3_1": {
"attackComplexity": "HIGH",
"attackVector": "NETWORK",
"availabilityImpact": "LOW",
"baseScore": 3.7,
"baseSeverity": "LOW",
"confidentialityImpact": "NONE",
"integrityImpact": "NONE",
"privilegesRequired": "NONE",
"scope": "UNCHANGED",
"userInteraction": "NONE",
"vectorString": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L",
"version": "3.1"
},
"format": "CVSS",
"scenarios": [
{
"lang": "en",
"value": "GENERAL"
}
]
},
{
"cvssV4_0": {
"Automatable": "NOT_DEFINED",
"Recovery": "NOT_DEFINED",
"Safety": "NOT_DEFINED",
"attackComplexity": "LOW",
"attackRequirements": "PRESENT",
"attackVector": "NETWORK",
"baseScore": 6.3,
"baseSeverity": "MEDIUM",
"exploitMaturity": "NOT_DEFINED",
"privilegesRequired": "NONE",
"providerUrgency": "NOT_DEFINED",
"subAvailabilityImpact": "NONE",
"subConfidentialityImpact": "NONE",
"subIntegrityImpact": "NONE",
"userInteraction": "NONE",
"valueDensity": "NOT_DEFINED",
"vectorString": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N",
"version": "4.0",
"vulnAvailabilityImpact": "LOW",
"vulnConfidentialityImpact": "NONE",
"vulnIntegrityImpact": "NONE",
"vulnerabilityResponseEffort": "NOT_DEFINED"
},
"format": "CVSS",
"scenarios": [
{
"lang": "en",
"value": "GENERAL"
}
]
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-770",
"description": "CWE-770 Allocation of Resources Without Limits or Throttling",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2026-08-29T23:58:31.289Z",
"orgId": "7ffcee3d-2c14-4c3e-b844-86c6a321a158",
"shortName": "harborist"
},
"references": [
{
"tags": [
"vendor-advisory"
],
"url": "https://github.com/ljharb/qs/security/advisories/GHSA-x5fp-wj9c-mxmx"
},
{
"tags": [
"patch"
],
"url": "https://github.com/ljharb/qs/commit/8859c37470e11b42b547b275e4e9bd0bc8cc5464"
},
{
"tags": [
"related"
],
"url": "https://github.com/ljharb/qs/security/advisories/GHSA-w7fw-mjwx-w883"
}
],
"solutions": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "\u003cp\u003eUpgrade to qs 6.16.0 or later.\u003c/p\u003e"
}
],
"value": "Upgrade to qs 6.16.0 or later."
}
],
"source": {
"discovery": "EXTERNAL"
},
"title": "qs.parse does not enforce arrayLimit on comma groups under bracket-push keys when throwOnLimitExceeded is set (incomplete fix for CVE-2026-2391)",
"workarounds": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "\u003cp\u003eBound request and body size at the transport layer (reverse proxy, API gateway, or body parser limit), which caps the cost of this input completely; or disable `comma: true` for untrusted input.\u003c/p\u003e"
}
],
"value": "Bound request and body size at the transport layer (reverse proxy, API gateway, or body parser limit), which caps the cost of this input completely; or disable `comma: true` for untrusted input."
}
]
}
},
"cveMetadata": {
"assignerOrgId": "7ffcee3d-2c14-4c3e-b844-86c6a321a158",
"assignerShortName": "harborist",
"cveId": "CVE-2026-82562",
"datePublished": "2026-08-29T23:58:31.289Z",
"dateReserved": "2026-08-29T23:51:36.085Z",
"dateUpdated": "2026-08-31T17:04:14.950Z",
"state": "PUBLISHED"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.2",
"vulnerability-lookup:meta": {
"vulnrichment": {
"containers": "{\"adp\": [{\"title\": \"CISA ADP Vulnrichment\", \"metrics\": [{\"other\": {\"type\": \"ssvc\", \"content\": {\"id\": \"CVE-2026-82562\", \"role\": \"CISA Coordinator\", \"options\": [{\"Exploitation\": \"poc\"}, {\"Automatable\": \"no\"}, {\"Technical Impact\": \"partial\"}], \"version\": \"2.0.3\", \"timestamp\": \"2026-08-31T17:03:54.217257Z\"}}}], \"references\": [{\"url\": \"https://github.com/ljharb/qs/security/advisories/GHSA-x5fp-wj9c-mxmx\", \"tags\": [\"exploit\"]}], \"providerMetadata\": {\"orgId\": \"134c704f-9b21-4f2e-91b3-4a467353bcc0\", \"shortName\": \"CISA-ADP\", \"dateUpdated\": \"2026-08-31T17:04:11.077Z\"}}], \"cna\": {\"title\": \"qs.parse does not enforce arrayLimit on comma groups under bracket-push keys when throwOnLimitExceeded is set (incomplete fix for CVE-2026-2391)\", \"source\": {\"discovery\": \"EXTERNAL\"}, \"credits\": [{\"lang\": \"en\", \"type\": \"reporter\", \"value\": \"Vectrain51\"}, {\"lang\": \"en\", \"type\": \"reporter\", \"value\": \"Fcmam5\"}, {\"lang\": \"en\", \"type\": \"remediation developer\", \"value\": \"ljharb\"}], \"metrics\": [{\"format\": \"CVSS\", \"cvssV3_1\": {\"scope\": \"UNCHANGED\", \"version\": \"3.1\", \"baseScore\": 3.7, \"attackVector\": \"NETWORK\", \"baseSeverity\": \"LOW\", \"vectorString\": \"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L\", \"integrityImpact\": \"NONE\", \"userInteraction\": \"NONE\", \"attackComplexity\": \"HIGH\", \"availabilityImpact\": \"LOW\", \"privilegesRequired\": \"NONE\", \"confidentialityImpact\": \"NONE\"}, \"scenarios\": [{\"lang\": \"en\", \"value\": \"GENERAL\"}]}, {\"format\": \"CVSS\", \"cvssV4_0\": {\"Safety\": \"NOT_DEFINED\", \"version\": \"4.0\", \"Recovery\": \"NOT_DEFINED\", \"baseScore\": 6.3, \"Automatable\": \"NOT_DEFINED\", \"attackVector\": \"NETWORK\", \"baseSeverity\": \"MEDIUM\", \"valueDensity\": \"NOT_DEFINED\", \"vectorString\": \"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N\", \"exploitMaturity\": \"NOT_DEFINED\", \"providerUrgency\": \"NOT_DEFINED\", \"userInteraction\": \"NONE\", \"attackComplexity\": \"LOW\", \"attackRequirements\": \"PRESENT\", \"privilegesRequired\": \"NONE\", \"subIntegrityImpact\": \"NONE\", \"vulnIntegrityImpact\": \"NONE\", \"subAvailabilityImpact\": \"NONE\", \"vulnAvailabilityImpact\": \"LOW\", \"subConfidentialityImpact\": \"NONE\", \"vulnConfidentialityImpact\": \"NONE\", \"vulnerabilityResponseEffort\": \"NOT_DEFINED\"}, \"scenarios\": [{\"lang\": \"en\", \"value\": \"GENERAL\"}]}], \"affected\": [{\"repo\": \"https://github.com/ljharb/qs\", \"vendor\": \"ljharb\", \"product\": \"qs\", \"versions\": [{\"status\": \"affected\", \"version\": \"6.14.2\", \"lessThan\": \"6.16.0\", \"versionType\": \"semver\"}], \"packageName\": \"qs\", \"collectionURL\": \"https://npmjs.com/qs\", \"defaultStatus\": \"unaffected\"}], \"solutions\": [{\"lang\": \"en\", \"value\": \"Upgrade to qs 6.16.0 or later.\", \"supportingMedia\": [{\"type\": \"text/html\", \"value\": \"\u003cp\u003eUpgrade to qs 6.16.0 or later.\u003c/p\u003e\", \"base64\": false}]}], \"references\": [{\"url\": \"https://github.com/ljharb/qs/security/advisories/GHSA-x5fp-wj9c-mxmx\", \"tags\": [\"vendor-advisory\"]}, {\"url\": \"https://github.com/ljharb/qs/commit/8859c37470e11b42b547b275e4e9bd0bc8cc5464\", \"tags\": [\"patch\"]}, {\"url\": \"https://github.com/ljharb/qs/security/advisories/GHSA-w7fw-mjwx-w883\", \"tags\": [\"related\"]}], \"workarounds\": [{\"lang\": \"en\", \"value\": \"Bound request and body size at the transport layer (reverse proxy, API gateway, or body parser limit), which caps the cost of this input completely; or disable `comma: true` for untrusted input.\", \"supportingMedia\": [{\"type\": \"text/html\", \"value\": \"\u003cp\u003eBound request and body size at the transport layer (reverse proxy, API gateway, or body parser limit), which caps the cost of this input completely; or disable `comma: true` for untrusted input.\u003c/p\u003e\", \"base64\": false}]}], \"descriptions\": [{\"lang\": \"en\", \"value\": \"### Summary\\n\\n\\n\\nWhen `qs.parse` is called with `comma: true` and `throwOnLimitExceeded: true`, a comma-separated value under a bracket-push key (`a[]=1,2,3,4`) is split into an array without being compared against `arrayLimit`, while the same value under a flat key (`a=1,2,3,4`), an indexed key (`a[0]=`), a nested key (`a[b]=`), or a dotted key (`a.b=` with `allowDots`) throws the documented `RangeError`. A single parameter such as `a[]=1,2,2,...` therefore produces an inner array of arbitrary length even though the caller opted into the hard limit. This is the `[]=` key form that the fix for CVE-2026-2391 (qs 6.14.2) did not cover.\\n\\n\\n\\n### Details\\n\\n\\n\\nIn `lib/parse.js`, a comma-separated value under a `[]=` key is split and then wrapped as a single nested element (`val = [val]`, so that each `a[]=x,y` group counts as one element of the outer array). The `arrayLimit` check that 6.14.2 added for comma values runs after that wrap, so for `[]=` parts it only ever saw the wrapper of length 1. 6.15.3 added a pre-split comma count so that an oversized value throws before it is allocated, but gated it on an `isFlatArrayValue` flag that `parseValues` set to `false` for any part containing `[]=`, and did not pass it for object-valued input, so the gap remained.\\n\\n\\n\\n#### PoC\\n\\n\\n\\n```js\\n\\n\\n\\nvar qs = require(\u0027qs\u0027);\\n\\n\\n\\nvar options = { comma: true, arrayLimit: 3, throwOnLimitExceeded: true };\\n\\n\\n\\nqs.parse(\u0027a=1,2,3,4\u0027, options); // RangeError: Array limit exceeded. Only 3 elements allowed in an array.\\n\\n\\n\\nqs.parse(\u0027a[]=1,2,3,4\u0027, options); // { a: [ [ \u00271\u0027, \u00272\u0027, \u00273\u0027, \u00274\u0027 ] ] } (no throw)\\n\\n\\n\\nqs.parse(\u0027a[]=\u0027 + \u00271,\u0027.repeat(1000000) + \u00271\u0027, { comma: true, arrayLimit: 20, throwOnLimitExceeded: true });\\n\\n\\n\\n// no throw; a 1,000,001-element inner array is allocated\\n\\n\\n\\n```\\n\\n\\n\\n#### Fix\\n\\n\\n\\n`lib/parse.js`, applied in 8859c37 on `main` and released as v6.16.0: the `isFlatArrayValue` gate is removed, so every comma-split value is counted against `arrayLimit` before splitting regardless of key form. An in-limit group under `a[]=` still counts as one element of the outer array, and the default (`throwOnLimitExceeded: false`) path is unchanged.\\n\\n\\n\\n### Affected versions\\n\\n\\n\\n`\u003e=6.14.2 \u003c6.16.0`, fixed in v6.16.0.\\n\\n\\n\\nv6.14.2 introduced `arrayLimit` enforcement for comma values (the fix for CVE-2026-2391) but only for values not under a `[]=` key, and every release from v6.14.2 through v6.15.3 has the same gap. v6.14.0 and v6.14.1, where `throwOnLimitExceeded` exists but does not apply to any comma form, are covered by CVE-2026-2391 rather than this record. Earlier lines (6.7.x through 6.13.x) have `comma` but no `throwOnLimitExceeded`, so there is no hard cap on any comma path to bypass; releases before 6.7.0 have no `comma` option.\\n\\n\\n\\n### Impact\\n\\n\\n\\nAn unauthenticated attacker who can reach an application that parses untrusted query strings or urlencoded bodies with both `comma: true` and `throwOnLimitExceeded: true` (both non-default) can bypass the configured limit with a single `a[]=` parameter and force the parser to allocate an array proportional to the request size. The cost is strictly linear in the attacker-supplied bytes (about 0.1 microseconds and 6 to 7 retained bytes per input byte; the same out-of-memory threshold as the documented default `throwOnLimitExceeded: false` path), so a transport-layer request or body size limit bounds it completely (and node\u0027s default maximum HTTP header size of 16 KB already bounds the request line, so multi-megabyte payloads need a body parser). The impact is that an opt-in hard limit fails open on one key spelling, not unbounded allocation from a small input.\", \"supportingMedia\": [{\"type\": \"text/html\", \"value\": \"\u003cp\u003e### Summary\u003c/p\u003e\u003cp\u003eWhen `qs.parse` is called with `comma: true` and `throwOnLimitExceeded: true`, a comma-separated value under a bracket-push key (`a[]=1,2,3,4`) is split into an array without being compared against `arrayLimit`, while the same value under a flat key (`a=1,2,3,4`), an indexed key (`a[0]=`), a nested key (`a[b]=`), or a dotted key (`a.b=` with `allowDots`) throws the documented `RangeError`. A single parameter such as `a[]=1,2,2,...` therefore produces an inner array of arbitrary length even though the caller opted into the hard limit. This is the `[]=` key form that the fix for CVE-2026-2391 (qs 6.14.2) did not cover.\u003c/p\u003e\u003cp\u003e### Details\u003c/p\u003e\u003cp\u003eIn `lib/parse.js`, a comma-separated value under a `[]=` key is split and then wrapped as a single nested element (`val = [val]`, so that each `a[]=x,y` group counts as one element of the outer array). The `arrayLimit` check that 6.14.2 added for comma values runs after that wrap, so for `[]=` parts it only ever saw the wrapper of length 1. 6.15.3 added a pre-split comma count so that an oversized value throws before it is allocated, but gated it on an `isFlatArrayValue` flag that `parseValues` set to `false` for any part containing `[]=`, and did not pass it for object-valued input, so the gap remained.\u003c/p\u003e\u003cp\u003e#### PoC\u003c/p\u003e\u003cp\u003e```js\u003c/p\u003e\u003cp\u003evar qs = require(\u0027qs\u0027);\u003c/p\u003e\u003cp\u003evar options = { comma: true, arrayLimit: 3, throwOnLimitExceeded: true };\u003c/p\u003e\u003cp\u003eqs.parse(\u0027a=1,2,3,4\u0027, options); // RangeError: Array limit exceeded. Only 3 elements allowed in an array.\u003c/p\u003e\u003cp\u003eqs.parse(\u0027a[]=1,2,3,4\u0027, options); // { a: [ [ \u00271\u0027, \u00272\u0027, \u00273\u0027, \u00274\u0027 ] ] } (no throw)\u003c/p\u003e\u003cp\u003eqs.parse(\u0027a[]=\u0027 + \u00271,\u0027.repeat(1000000) + \u00271\u0027, { comma: true, arrayLimit: 20, throwOnLimitExceeded: true });\u003c/p\u003e\u003cp\u003e// no throw; a 1,000,001-element inner array is allocated\u003c/p\u003e\u003cp\u003e```\u003c/p\u003e\u003cp\u003e#### Fix\u003c/p\u003e\u003cp\u003e`lib/parse.js`, applied in 8859c37 on `main` and released as v6.16.0: the `isFlatArrayValue` gate is removed, so every comma-split value is counted against `arrayLimit` before splitting regardless of key form. An in-limit group under `a[]=` still counts as one element of the outer array, and the default (`throwOnLimitExceeded: false`) path is unchanged.\u003c/p\u003e\u003cp\u003e### Affected versions\u003c/p\u003e\u003cp\u003e`\u0026gt;=6.14.2 \u0026lt;6.16.0`, fixed in v6.16.0.\u003c/p\u003e\u003cp\u003ev6.14.2 introduced `arrayLimit` enforcement for comma values (the fix for CVE-2026-2391) but only for values not under a `[]=` key, and every release from v6.14.2 through v6.15.3 has the same gap. v6.14.0 and v6.14.1, where `throwOnLimitExceeded` exists but does not apply to any comma form, are covered by CVE-2026-2391 rather than this record. Earlier lines (6.7.x through 6.13.x) have `comma` but no `throwOnLimitExceeded`, so there is no hard cap on any comma path to bypass; releases before 6.7.0 have no `comma` option.\u003c/p\u003e\u003cp\u003e### Impact\u003c/p\u003e\u003cp\u003eAn unauthenticated attacker who can reach an application that parses untrusted query strings or urlencoded bodies with both `comma: true` and `throwOnLimitExceeded: true` (both non-default) can bypass the configured limit with a single `a[]=` parameter and force the parser to allocate an array proportional to the request size. The cost is strictly linear in the attacker-supplied bytes (about 0.1 microseconds and 6 to 7 retained bytes per input byte; the same out-of-memory threshold as the documented default `throwOnLimitExceeded: false` path), so a transport-layer request or body size limit bounds it completely (and node\u0027s default maximum HTTP header size of 16 KB already bounds the request line, so multi-megabyte payloads need a body parser). The impact is that an opt-in hard limit fails open on one key spelling, not unbounded allocation from a small input.\u003c/p\u003e\", \"base64\": false}]}], \"problemTypes\": [{\"descriptions\": [{\"lang\": \"en\", \"type\": \"CWE\", \"cweId\": \"CWE-770\", \"description\": \"CWE-770 Allocation of Resources Without Limits or Throttling\"}]}], \"providerMetadata\": {\"orgId\": \"7ffcee3d-2c14-4c3e-b844-86c6a321a158\", \"shortName\": \"harborist\", \"dateUpdated\": \"2026-08-29T23:58:31.289Z\"}}}",
"cveMetadata": "{\"cveId\": \"CVE-2026-82562\", \"state\": \"PUBLISHED\", \"dateUpdated\": \"2026-08-31T17:04:14.950Z\", \"dateReserved\": \"2026-08-29T23:51:36.085Z\", \"assignerOrgId\": \"7ffcee3d-2c14-4c3e-b844-86c6a321a158\", \"datePublished\": \"2026-08-29T23:58:31.289Z\", \"assignerShortName\": \"harborist\"}",
"dataType": "CVE_RECORD",
"dataVersion": "5.2"
}
}
}
Loading…
Loading…
Sightings
| Author | Source | Type | Date |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or seen somewhere by the user.
- Confirmed: The vulnerability is confirmed from an analyst perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: This vulnerability was exploited and seen by the user reporting the sighting.
- Patched: This vulnerability was successfully patched by the user reporting the sighting.
- Not exploited: This vulnerability was not exploited or seen by the user reporting the sighting.
- Not confirmed: The user expresses doubt about the veracity of the vulnerability.
- Not patched: This vulnerability was not successfully patched by the user reporting the sighting.
Loading…
Loading…