Skip to main content

qs EUVDEUVD-2026-110630

| CVE-2026-110910 MEDIUM
Improper Encoding or Escaping of Output (CWE-116)
2026-10-27 harborist
6.3
CVSS 4.0 · Vendor: harborist
View

Attack ChainAIDerived

Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain

  1. Access
    Supply malicious object key with empty-array value
  2. Exploit
    App serializes via qs.stringify with allowEmptyArrays:true
  3. Execution
    Unencoded key's &/=/# breaks query-string syntax
  4. Impact
    Injected 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.

This Month Plan within 30 days. Why: MEDIUM severity (CVSS 4.0-6.9).
Patch available 0.2% exploitation probability (EPSS)

Get an e-mail when this CVE is exploited, patched or re-scored. Free account.

Alert me

AnalysisAIv1 – 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

qs 6.12.0 <6.17.0

Affected Packages

euvd qs fix: 6.17.0

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

Vendor (harborist) PRIMARY 6.3 LOW
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
vuln.today AI 3.7 LOW

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.

3.1 AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N
4.0 AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N

Primary rating from Vendor (harborist).

CVSS VectorVendor: harborist

Attack Vector
Network
Attack Complexity
Low
Attack Requirements
Present
Privileges Required
None
User Interaction
None
Vulnerable system: Confidentiality
None
Vulnerable system: Integrity
Low
Vulnerable system: Availability
None
Subsequent system: Confidentiality
None
Subsequent system: Integrity
None
Subsequent system: Availability
None

Lifecycle Timeline

3
Severity Changed
Oct 27, 2026 - 07:22 NVD
LOW MEDIUM
CVSS changed
Oct 27, 2026 - 07:22 NVD
3.7 (LOW) 6.3 (MEDIUM)
CVE Published
Oct 27, 2026 - 06:42 cve.org
MEDIUM 6.3

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

Threat Intelligence Overview

EPSS Score
0.2%
Top 86% exploitable
Prevalence
High
qs is an extremely widely-used npm query-string library across the Node ecosystem
Social Media
1
Sightings
Data Quality
5 sources
Confidence: Medium

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.

SignalValue
Severity MEDIUM
CISA KEV No
Public POC No
EPSS 0.2%
Patch available Yes
Active threat No
Deciding rule
MEDIUM severity (CVSS 4.0-6.9)
What every action level means
Emergency Immediate action required. Criteria: CRITICAL + in CISA KEV (actively exploited) without patch, OR CRITICAL + EPSS > 30% without patch, OR CRITICAL + active threat signals without patch.
Act Now Prioritize within 24-48h. Criteria: in KEV with patch available, OR CRITICAL + public POC, OR HIGH + EPSS > 10%, OR CRITICAL + active threat, OR any CRITICAL severity.
This Week Schedule remediation this week. Criteria: HIGH severity (CVSS 7.0-8.9). Patch within normal change window.
This Month Plan within 30 days. Criteria: MEDIUM severity (CVSS 4.0-6.9), lower exploitation probability. Include in regular patch cycle.
Monitor Track and reassess. Criteria: LOW severity (CVSS 0.1-3.9), minimal exploitation risk. No immediate action needed.

SSVC AssessmentENISA

SSVC Decision· derived
Scheduled
Exploitation· derived
None
System Exposure· derived
Open
Automatable· derived
Yes
Human Impact· derived
Medium

Priority Score

32
Low Medium High Critical
KEV: 0
EPSS: 0
CVSS: +32
PoC: 0
Weaponized: 0
Threat intel: 0
Gov advisory: 0

Attack Technique

Code Injection CRITICAL

Code injection occurs when an application accepts user input and passes it directly into a language interpreter or evaluator without proper sanitization.

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy