CVE-2026-110910
LowCVSS 3.7Exploitation Probability (EPSS)
Low risk21th percentile - higher than 21% of all known CVEs
Summary
A vulnerability in the qs library causes the key of an empty array to be written without encoding when allowEmptyArrays: true is used, even though keys are encoded by default. A key containing &, =, or # can therefore inject parameters into the serialized query string or truncate it. Affected versions are >=6.12.0 <6.12.6, >=6.13.0 <6.13.4, >=6.14.0 <6.14.3, >=6.15.0 <6.15.4, and >=6.16.0 <6.16.2.
Risk Assessment
An application that passes an object with untrusted key names to qs.stringify with allowEmptyArrays: true may allow an attacker to inject parameters into the resulting query string, potentially overriding or bypassing trusted parameters such as apiKey. The option is off by default, and only keys whose value is an empty array are affected.
Recommendation
Update the qs library to version 6.12.6, 6.13.4, 6.14.3, 6.15.4, 6.16.2, or 6.17.0 and later. If updating is not possible, avoid passing untrusted key names to qs.stringify with allowEmptyArrays: true.
Other vulnerabilities in qs
See all- CVE-2026-110816Medium
A vulnerability in the qs library affects qs.stringify with arrayFormat: 'comma', which throws a TypeError when an array element cannot be coerced to a string (e.g., an object with an own non-callable toString or a null-prototype object). The issue exists in versions >=6.7.0 <6.16.1 and is fixed in v6.16.1. Input can come from untrusted sources, including JSON.parse or qs.parse with allowPrototypes: true.
- CVE-2026-82562Low
A vulnerability in the qs library allows bypassing the arrayLimit for comma-separated values when comma: true and throwOnLimitExceeded: true are used. This affects bracket-push keys (a[]=), where the value is split into an array without checking the limit, enabling allocation of arbitrarily large arrays. The issue affects versions >=6.14.2 <6.16.0 and is fixed in 6.16.0.
- CVE-2026-82417Medium
The `qs.stringify` function in the qs library throws a `TypeError` when serializing an object whose own `constructor` property has a truthy, non-callable `isBuffer` member. The duck-typing check in `utils.isBuffer` invokes `obj.constructor.isBuffer(obj)` without verifying it is a function, causing the error. Affected versions are from 2.2.5 to 6.15.3, fixed in 6.16.0.
- CVE-2026-8723Medium
The qs library versions 6.11.1 through 6.15.2 contain a bug where qs.stringify throws a synchronous TypeError when called with arrayFormat: 'comma' and encodeValuesOnly: true on an array containing null or undefined. The skipNulls and strictNullHandling options are not honored because the error occurs before they are checked. The vulnerability is fixed in version 6.15.2.
- CVE-2025-15284Low
A vulnerability in the qs library (parse module) involves improper input validation — the arrayLimit option was not enforced for bracket notation (a[]=1&a[]=2), only for indexed notation (a[0]=1). This allows arrays larger than the defined limit to be created. The default parameterLimit (1000) effectively mitigates the practical impact.
Original NVD description (English source)
### 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 `#` therefore 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 ```js 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, and backported to v6.16.2, v6.15.4, v6.14.3, v6.13.4, and v6.12.6: 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.12.6`, fixed in v6.12.6 - `>=6.13.0 <6.13.4`, fixed in v6.13.4 - `>=6.14.0 <6.14.3`, fixed in v6.14.3 - `>=6.15.0 <6.15.4`, fixed in v6.15.4 - `>=6.16.0 <6.16.2`, fixed in v6.16.2 v6.17.0 and later are not affected. `allowEmptyArrays` was introduced in f22b2bc and first shipped in v6.12.0, with this early return. 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.
Vulnerability data from NVD (NIST) · CISA KEV · EPSS

