Unencoded keys in qs.stringify allow query parameter injection
Key facts
- Attack vector
- Network
- Privileges required
- None
- User interaction
- None
- Attack complexity
- Low
- Attack requirements
- Present
- Impact type
- —
- Confidentiality · integrity · availability
- None · Low · None
- Fix status
- Partial Red Hat: 1 fixed, 35 without a fix
Attack profile from CVSS 4.0 · CNA. Fix status from CNA and Red Hat.
Summary
Versions of qs by ljharb from 6.12.0 before 6.12.6, from 6.13.0 before 6.13.4, from 6.14.0 before 6.14.3, from 6.15.0 before 6.15.4, and from 6.16.0 before 6.16.2 are affected. With allowEmptyArrays enabled, qs.stringify does not encode the key of an empty array, so & or = in that key becomes query string syntax (CWE-116). A remote attacker can inject parameters into the query string when an application builds keys from untrusted input.
Written with Claude Haiku 5.5 from the sources below · 2026-11-04
Description from the CNA
### 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.
Why it matters now
SSVC decision points
CISA ADP · SSVC 2.0.3
- Exploitation
- None
- Automatable
- No
- Technical impact
- Partial
The decision (Track, Attend, Act) also depends on your mission and well-being impact; see CISA's SSVC guide.
Affected products
| Vendor | Product | Affected versions | Fixed in | Status | Source |
|---|---|---|---|---|---|
| ljharb | qs | 6.12.0 to before 6.12.6; 6.13.0 to before 6.13.4; 6.14.0 to before 6.14.3; 6.15.0 to before 6.15.4; 6.16.0 to before 6.16.2 | 6.12.6, 6.13.4, 6.14.3, 6.15.4, 6.16.2 | Patch available | CNA |
- qs Vendorljharb Affected 6.12.0 to before 6.12.6; 6.13.0 to before 6.13.4; 6.14.0 to before 6.14.3; 6.15.0 to before 6.15.4; 6.16.0 to before 6.16.2 Fixed in 6.12.6, 6.13.4, 6.14.3, 6.15.4, 6.16.2 Patch available CNA
Remediation
Upgrade qs to 6.12.6, 6.13.4, 6.14.3, 6.15.4, 6.16.2, or 6.17.0 and later, matching your release line. Red Hat Hardened Images is fixed by RHSA-2026:94794. Red Hat's plan is none available for Ansible Automation Orchestrator 2026, Cost Management On Premise, Cryostat 4, Migration Toolkit for Applications 8, Migration Toolkit for Containers, and other products.
Written with Claude Haiku 5.5 from the sources below · 2026-11-04
- Fix · CNA qs (ljharb) Fixed in 6.12.6, 6.13.4, 6.14.3, 6.15.4, 6.16.2
Red Hat 1 advisory
- RHSA-2026:94794 Released 2026-11-03 · Red Hat rating ImportantRed Hat Hardened Images RPMs Security Update
- Red Hat Hardened Images grafana13.2-13.2.4-0.6.hum1@aarch64, grafana13.2-13.2.4-0.6.hum1@s390x, grafana13.2-13.2.4-0.6.hum1@src +1
Vendor assessments
Red Hat
Red Hat rating: Low. Red Hat's page for this CVE →
| Product | Status | Plan or fix |
|---|---|---|
| Ansible Automation Orchestrator 2026 | Affected | No fix yet: Fix deferred |
| Cost Management On Premise | Affected | No fix yet: Fix deferred |
| Cryostat 4 | Affected | No fix yet: Fix deferred |
| Migration Toolkit for Applications 8 | Affected | No fix yet: Fix deferred |
| Migration Toolkit for Containers | Affected | No fix yet: Fix deferred |
| Multicluster Engine for Kubernetes | Affected | No fix yet: Fix deferred |
| Node HealthCheck Operator | Affected | No fix yet: Fix deferred |
| OpenShift Lightspeed | Affected | No fix yet: Fix deferred |
| OpenShift Pipelines | Affected | No fix yet: Fix deferred |
| OpenShift Serverless | Affected | No fix yet: Fix deferred |
| OpenShift Service Mesh 3 | Affected | No fix yet: Fix deferred |
| Red Hat Advanced Cluster Management for Kubernetes 2 | Affected | No fix yet: Fix deferred |
| Red Hat Advanced Cluster Security 4 | Affected | No fix yet: Fix deferred |
| Red Hat Ansible Automation Platform 2 | Affected | No fix yet: Fix deferred |
| Red Hat Build of Podman Desktop | Affected | No fix yet: Fix deferred |
| Red Hat Connectivity Link 1 | Affected | No fix yet: Fix deferred |
| Red Hat Data Grid 8 | Affected | No fix yet: Fix deferred |
| Red Hat Developer Hub 1 | Affected | No fix yet: Fix deferred |
| Red Hat Discovery 2 | Affected | No fix yet: Fix deferred |
| Red Hat Enterprise Linux 10 | Affected | No fix yet: Fix deferred |
| Red Hat Enterprise Linux 9 | Affected | No fix yet: Fix deferred |
| Red Hat Enterprise Linux AI (RHEL AI) 3 | Affected | No fix yet: Fix deferred |
| Red Hat OpenShift AI (RHOAI) | Affected | No fix yet: Fix deferred |
| Red Hat OpenShift Container Platform 4 | Affected | No fix yet: Fix deferred |
| Red Hat OpenShift Container Platform 5 | Affected | No fix yet: Fix deferred |
| Red Hat OpenShift Dev Spaces | Affected | No fix yet: Fix deferred |
| Red Hat OpenShift GitOps | Affected | No fix yet: Fix deferred |
| Red Hat Openshift Data Foundation 4 | Affected | No fix yet: Fix deferred |
| Red Hat Quay 3 | Affected | No fix yet: Fix deferred |
| Red Hat Satellite 6 | Affected | No fix yet: Fix deferred |
| Red Hat Trusted Profile Analyzer | Affected | No fix yet: Fix deferred |
| Red Hat build of Apache Camel - HawtIO 4 | Affected | No fix yet: Fix deferred |
| Red Hat build of Apache Camel for Spring Boot 4 | Affected | No fix yet: Fix deferred |
| Secrets Management Console for Red Hat OpenShift | Affected | No fix yet: Fix deferred |
| Self-service automation portal 2 | Affected | No fix yet: Fix deferred |
| Red Hat Hardened Images | Fixed | RHSA-2026:94794 |
Not affected: 15 products
- Node HealthCheck Operator
- Red Hat 3scale API Management Platform 2
- Red Hat AMQ Broker 7
- Red Hat Advanced Cluster Management for Kubernetes 2
- Red Hat Ansible Automation Platform 2
- Red Hat Ceph Storage 4
- Red Hat Enterprise Linux 7
- Red Hat Enterprise Linux 8
- Red Hat Enterprise Linux 9
- Red Hat Fuse 7
- Red Hat Hardened Images
- Red Hat JBoss Enterprise Application Platform 7
- Red Hat OpenShift AI (RHOAI)
- Red Hat OpenStack Platform 17.1
- Red Hat Single Sign-On 7
Timeline
- 2026-10-27 Threatist record created from CVE-2026-110910
- 2026-10-27 Fix available: [email protected], [email protected], [email protected] +2 · CNA
- 2026-10-27 5 new references · CNA
- 2026-10-27 Priority changed: MEDIUM → LOW
- 2026-10-29 Affected versions changed · Red Hat
- 2026-10-29 CVSS score added · Red Hat
- 2026-10-30 Affected versions changed · Red Hat
- 2026-11-04 Fix available: RHSA-2026:94794 · Red Hat
- 2026-11-04 Fix available: Red Hat Hardened Images · Red Hat
- 2026-11-04 CISA SSVC decision points changed
References
Patch 7
- https://github.com/ljharb/qs/commit/234d5051f5071efaa025a7ca9e4d34a56a460a1b
- https://github.com/ljharb/qs/commit/6b8bf1a53d7b0c5e7e434eaae8e2c0cffd5c2492
- https://github.com/ljharb/qs/commit/033526fc82f2e80a3d896df2bce9137e34cb5dc5
- https://github.com/ljharb/qs/commit/f9dd4830cb922363822cdfdca2cceb3ed9792224
- https://github.com/ljharb/qs/commit/ad445aa23e4c3a97394c3b9a5f9d7a7159eb1f17
- https://github.com/ljharb/qs/commit/5d3b1b9ec6afd0cfc760c8e12862873789a32fc8
- https://github.com/ljharb/qs/pull/561
More ljharb vulnerabilities
- TI-2026-74516 qs stringify throws TypeError on uncoercible array elements in comma format CVSS 4.0MEDIUM 6.3
- TI-2026-56824 qs.stringify throws TypeError on objects with a non-callable constructor.isBuffer property CVSS 4.0MEDIUM 6.3
- TI-2026-07665 qs.stringify crashes on null/undefined entries in comma-format arrays under encodeValuesOnly CVSS 4.0MEDIUM 6.3
- TI-2026-56916 Resource allocation without limits in ljharb qs CVSS 4.0MEDIUM 6.3
- TI-2026-69286 Prototype pollution in traverse set() modifies built-in prototypes CVSS 4.0MEDIUM 6.9
Sources
- CVE-2026-110910 · CVE Program
- CVE-2026-110910 · NVD
- FIRST EPSS
- CVE-2026-110910 · Red Hat security data · CC BY 4.0
This product uses the NVD API but is not endorsed or certified by the NVD. Sources and licences