Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain
-
AccessSupply malicious object key with empty-array value
-
ExploitApp serializes via qs.stringify with allowEmptyArrays:true
-
ExecutionUnencoded key's &/=/# breaks query-string syntax
-
ImpactInjected or truncated parameters reach upstream service
Vulnerability Assessment
| Weakness |
CWE
OWASP Top 10:2025
A05:2025 - Injection
ASVS 5.0
V1 - Encoding and Sanitization Output encoding for downstream
|
| Exploitation Conditions | Exploitation requires ALL of: (1) the application calls qs.stringify with the non-default option allowEmptyArrays:true; (2) object key names are derived from untrusted input (e.g. forwarding a parsed JSON body or user-selected fields to an upstream service); (3) at least one such untrusted key has an empty array as its value after any filter - only empty-array keys hit the vulnerable early return; and (4) default key encoding is in effect. Limiting factors: allowEmptyArrays is OFF by default, so most deployments are unaffected; configurations using encode:false or encodeValuesOnly:true leave keys unencoded on every path and are explicitly NOT affected; and impact is confined to query-string parameter injection/truncation against the downstream consumer, with the winner of duplicate parameters depending on how the receiving side handles them. |
| Impact | This is a genuine but conditional, moderate-severity issue rather than an emergency. The vendor CVSS 4.0 vector (AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N, base 6.3) shows a network-reachable, unauthenticated, integrity-only weakness whose AT:P correctly flags that a specific attack requirement must hold - here, the non-default allowEmptyArrays:true configuration plus untrusted key names. Every signal points the same way rather than conflicting: EPSS is low (0.23%, 14th percentile), there is no CISA KEV listing, and the vuln.today Decision label is 'this_month' (not act_now/emergency), all consistent with a flaw that is real but not broadly or opportunistically exploitable. A working proof-of-concept is published in the GHSA advisory and the CVE description (runnable qs.stringify/qs.parse snippets), so exploitability is demonstrated; this is distinct from confirmed active exploitation, which is NOT indicated here. The main tempering factor is the narrow precondition set: allowEmptyArrays is off by default and only keys whose value is an empty array are affected, so most qs deployments are not exposed even though qs itself is extremely widely used. Net: prioritize patching on a normal cycle (this month) for applications that actually enable allowEmptyArrays and forward untrusted keys; treat it as lower priority elsewhere. |
| Exploit ScenarioDerived | An application enables qs.stringify with allowEmptyArrays:true and forwards user-controlled fields (e.g. a parsed JSON body or user-selected field names) to an upstream service as a query string. An attacker supplies a field name like 'x&apiKey=evil&y' whose value is an empty array; because the empty-array key is emitted unencoded, the '&' and '=' become query-string syntax and inject an attacker-controlled apiKey parameter into the upstream request. A published PoC in the vendor advisory demonstrates both parameter injection and truncation via a '#' in the key; consistent with CVSS AV:N/AC:L the request itself is simple, with the AT:P requirement being the non-default allowEmptyArrays configuration. |
Get an e-mail when this CVE is exploited, patched or re-scored. Free account.
Alert meAnalysisAIv1 – Oct 28, 2026
Query string parameter injection in the npm qs library: when an application calls qs.stringify with the non-default option allowEmptyArrays: true, the key of an empty array is emitted without encoding, so an attacker-influenced key containing &, =,
or the configured delimiter becomes query string syntax rather than data - letting the caller add parameters to the serialized query string or truncate it at a fragment. All releases from 6.12.0 through 6.16.1 are affected (allowEmptyArrays did not exist before 6.12.0); the issue only bites when untrusted key names reach stringify and at least one of them maps to an empty array, and deployments that leave the option at its default are unaffected. The vendor has released patches in 6.12.6, 6.13.4, 6.14.3, 6.15.4, 6.16.2 and 6.17.0; EPSS is low at 0.23% (14th percentile), there is no CISA KEV entry, and the advisory includes a public PoC reference that is not verified as working exploit code. The assessed vector (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N) rates impact as integrity-only and modest.
RemediationAI
Upgrade to a patched release - 6.17.0, or the fixed version on your current branch: 6.12.6, 6.13.4, 6.14.3, 6.15.4 or 6.16.2 (fix commit https://github.com/ljharb/qs/commit/234d5051f5071efaa025a7ca9e4d34a56a460a1b, PR https://github.com/ljharb/qs/pull/561, advisory https://github.com/ljharb/qs/security/advisories/GHSA-w4cr-vjpx-p82q). Be aware that the fix changes output for some inputs: the brackets of nested keys are now percent-encoded (a[b][] becomes a%5Bb%5D[], which qs.parse reads identically, but consumers that compare or sign raw query strings byte-for-byte may see differences), and with encodeDotInKeys a dot now encodes as %252E instead of %2E. Where upgrading is not immediately possible, the exposure is confined to call sites that set allowEmptyArrays: true - leaving it at its default (off) avoids the vulnerable early return entirely at the cost of not supporting empty-array syntax - and configurations using encode: false or encodeValuesOnly: true are not affected but leave every key unencoded, including any untrusted ones. Call sites that must keep allowEmptyArrays enabled should not pass object key names derived from untrusted input (e.g. forwarded JSON body fields) into qs.stringify, or should reject/escape keys containing &, =,
or the configured delimiter before serialization; note that on the receiving side the outcome of a duplicate parameter depends on how that downstream service resolves it, so parameter de-duplication or allowlisting at the upstream service limits the impact.
Affected VersionsENISA
Affected Packages
Source: OSV (Open Source Vulnerabilities)
Affects your stack
Affects 1 package in euvd
Checking this CVE against your own stacks and servers is part of the Basic plan and up.
Technical ContextAI · show details
qs is an npm library for serializing JavaScript objects into query strings and parsing them back. The bug is an output-encoding defect (CWE-116, improper encoding/escaping of output) in lib/stringify.js: the allowEmptyArrays branch returns adjustedPrefix + '[]' early, bypassing the encoder and formatter that every other key passes through. As a result { 'x&k=v': ['1'] } serializes correctly as x%26k%3Dv%5B0%5D=1, while { 'x&k=v': [] } serializes as x&k=v[], which re-parses into extra parameters. The early return applies at any nesting depth ({ a: { 'b&k=v': [] } } yields a[b&k=v][]), under every arrayFormat, with allowDots, and even with a custom encoder, which is never invoked for that key. The explicitly unaffected configurations are encode: false and encodeValuesOnly: true, where keys are intentionally left unencoded on every code path and the missing encoding is therefore consistent rather than an anomaly. Environments are described by CPE cpe:2.3:a:ljharb:qs. The fix (commit 234d505, PR #561) routes the empty-array key through encoder and formatter exactly as a normal key, producing x%26apiKey%3Devil%26y[]. Two intentional serialization changes come with it: brackets of nested keys are now percent-encoded (a[b][] becomes a%5Bb%5D[], which qs.parse reads identically), and with encodeDotInKeys a dot is encoded as %252E rather than %2E.
Know when Qs gets its next vulnerability
- Watch products and get an email when something new is published
- Check this CVE against your own stack and servers
- Exploit and PoC references, threat intelligence from 22 sources
No credit card · 7-day full trial
Severity by source
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N
Network-reachable and unauthenticated at the data layer (PR:N), but the non-default allowEmptyArrays config plus untrusted empty-array keys make it AC:H; integrity-only parameter injection gives I:L with no C or A impact.
Primary rating from Vendor (harborist).
CVSS VectorVendor: harborist
Lifecycle Timeline
3DescriptionCVE.org
Summary
qs.stringify with allowEmptyArrays: true writes the key of an empty array without encoding it, even though keys are encoded by default. A key that contains & (or the configured delimiter), =, or # ∴ becomes query string syntax instead of data, so a caller-supplied key name can add parameters to the serialized query string, or cut it short.
Details
In lib/stringify.js, the allowEmptyArrays branch returns adjustedPrefix + '[]' early, before the code that passes every other key through encoder and formatter. So { "x&k=v": ["1"] } serializes as x%26k%3Dv%5B0%5D=1, but { "x&k=v": [] } serializes as x&k=v[]. The early return applies at any depth ({ "a": { "b&k=v": [] } } gives a[b&k=v][]), under every arrayFormat, with allowDots, and with a custom encoder, which is never called for that key. With encode: false or encodeValuesOnly: true, keys are intentionally left unencoded on every path; those configurations are not affected.
PoC
var qs = require("qs");
var query = qs.stringify({ q: "hi", "x&apiKey=evil&y": [] }, { allowEmptyArrays: true });
// "q=hi&x&apiKey=evil&y[]"
qs.parse(query);
// { q: "hi", x: "", apiKey: "evil", y: [""] }
qs.stringify({ "x#": [], z: "1" }, { allowEmptyArrays: true });
// "x#[]&z=1": in a URL, everything after "#" becomes the fragment
Fix
Applied in 234d505 on main and released as v6.17.0: the key of an empty array goes through encoder and formatter exactly as the key of any other value does, so { "x&apiKey=evil&y": [] } now serializes as x%26apiKey%3Devil%26y[]. Keys that need no encoding serialize as before, with two exceptions that match every other path: the brackets of a nested key are now encoded (a[b][] becomes a%5Bb%5D[], which qs.parse reads identically), and with encodeDotInKeys, a dot in the key becomes %252E instead of %2E (which decodeDotInKeys previously misread as nesting).
Affected versions
>=6.12.0 <6.17.0, fixed in v6.17.0.
allowEmptyArrays was introduced in f22b2bc and first shipped in v6.12.0, with this early return. Every release from v6.12.0 through v6.16.1 is affected. Earlier versions have no allowEmptyArrays option and are not affected.
Impact
An application that passes an object whose key names come from untrusted input to qs.stringify with allowEmptyArrays: true (for example, to forward a parsed JSON body or user-selected fields to an upstream service) can have parameters injected into the resulting query string. The injected parameters are not keys of the object, so deleting or overwriting a key on the object (such as setting a trusted apiKey after copying the user's fields) does not remove them; whether the attacker's value or the application's wins depends on how the receiving side handles a duplicate parameter. A # in such a key ends the query string early when the result is placed in a URL. allowEmptyArrays is off by default, and only keys whose value (after any filter) is an empty array are affected.
Threat Intelligence Overview
How this decision was reached
The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.
What every action level means
SSVC AssessmentENISA
Priority Score
Attack Technique
Code injection occurs when an application accepts user input and passes it directly into a language interpreter or evaluator without proper sanitization.
qs before 6.10.3, as used in Express before 4.17.3 and other products, allows attackers to cause a Node process hang for
Memory and availability pressure in the npm package qs (versions before 6.14.1) stems from an input-validation gap where
Comma-split value parsing in the qs npm library (versions 6.14.2 through 6.15.3) bypasses the caller-configured `arrayLi
### Summary The `arrayLimit` option in qs does not enforce limits for comma-separated values when `comma: true` is enabl
the web framework using ljharb's qs module older than v6.3.2, v6.2.3, v6.1.2, and v6.0.4 is vulnerable to a DoS. Rated h
EUVD-2026-110630
NVD
CVE.org
EPSS
GitHub
Exploit-DB