CVE-2026-110910: qs.stringify with allowEmptyArrays does not encode the keys of empty arrays, allowing query string parameter injection

Published Oct 27, 2026
·
Updated

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

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: 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.

Affected Software

1 affected component
npm/qs>=6.12.0<6.17.0

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade qs to a version that resolves this vulnerability.

    Fixed in 6.17.0

Event History

Oct 27, 2026
CVE Published
via MITRE·06:42 AM
Data Sourced
via MITRE·06:42 AM
RemedyDescriptionSeverityWeakness

Frequently Asked Questions

1

Which applications are exposed to this issue?

Applications using npm/qs are exposed only when they call qs.stringify with allowEmptyArrays: true and allow untrusted input to control object key names. The issue can occur for empty arrays at any nesting depth.

2

What does an attacker need to exploit it?

An attacker needs to supply a key name containing query-string syntax such as &, the configured delimiter, =, or #, and cause that key to be serialized with an empty array value. This can inject additional query parameters or truncate the resulting query string.

3

Are configurations that disable key encoding affected?

No. Configurations using encode: false or encodeValuesOnly: true intentionally leave keys unencoded on all serialization paths and are not affected by this inconsistent empty-array behavior.

4

Does a custom encoder prevent the issue?

No. For empty arrays with allowEmptyArrays enabled, the affected early-return path does not call the custom encoder for the key.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203