Cloudanix VulnDB

CVE-2026-110816

Medium · 5.3

CVSS

5.3

EPSS

0.4%

Published

Oct 14, 2026

CISA due date

—

Cloudanix analysis

Schedule a fix SSVC: Track

Certain unusual data sent to a web application can cause it to crash when the software tries to rebuild a request string, disrupting service until the library is updated.

Exploitation
Remote Unauthenticated
Attacker needs
None
Complexity
Low
Impact
Denial Of Service
Component
Library
Class
Improper Input Validation
Remediation
Upgrade
Effort
Low
Patch risk
None

What it actually does

qs.stringify with arrayFormat: 'comma' (and encodeValuesOnly) throws TypeError: Cannot convert object to primitive value on array elements that fail string coercion, such as objects with non-callable toString or null-prototype objects, because join and String() calls lack guards; other arrayFormat options are unaffected.

Why it matters to the business

Unauthenticated requests can trigger synchronous crashes or 500 responses in applications that re-serialize parsed data using the vulnerable qs path, leading to denial of service; recent publication and required upgrades in Grafana 13.1/13.2 packages increase urgency for affected Red Hat deployments.

How an attack unfolds

An attacker supplies input (via JSON.parse, qs.parse with allowPrototypes:true or plainObjects:true) that produces arrays containing uncoercible objects; when the application later calls qs.stringify on that data with arrayFormat:'comma', the library throws, terminating the request or process depending on surrounding error handling.

How to fix it

Upgrade qs to 6.16.1 or later; for the listed Grafana packages apply the exact fixed versions grafana13-1-main (redhat) 13.1.8-0.3.hum1 and grafana13-2-main (redhat) 13.2.3-0.3.hum1 per RHSA-2026:87077; wrap stringify calls in try/catch and avoid arrayFormat:comma on untrusted data until patched.

How to confirm the fix worked

Re-run the published PoC inputs after upgrade and confirm they no longer throw; verify the library version in package.json/lockfile and test that existing comma-format serialization of ordinary arrays continues to produce identical output.

Typical attack path

  1. Supply crafted query string or JSON body that qs.parse converts into an array containing an uncoercible object
  2. Trigger application code path that rebuilds a query string or serializes the data using qs.stringify with arrayFormat set to comma
  3. Force the join/String coercion that throws TypeError, resulting in 500 response or unhandled rejection

How to detect it

Search application and web-server logs for the exact TypeError message in request-handling threads; correlate with recent qs.parse calls using allowPrototypes or plainObjects options and subsequent stringify operations.

  • TypeError: Cannot convert object to primitive value
  • arrayFormat.*comma

What your security team should do

  • Immediately upgrade qs to 6.16.1 and apply the listed Grafana Red Hat patches
  • Audit all call sites that invoke qs.stringify with arrayFormat:comma
  • Add defensive error handling around serialization of request-derived data
  • Monitor for the TypeError in application logs as a temporary detection measure

The record

### Summary `qs.stringify` with `arrayFormat: 'comma'` throws `TypeError: Cannot convert object to primitive value` when an array element cannot be coerced to a string, such as an object with an own non-callable `toString` (`{ "toString": "x" }`) or a null-prototype object. The comma format joins array elements with `Array.prototype.join` and, under `encodeValuesOnly`, first passes each element through `String()` in `utils.encode`; neither guards against an element whose string coercion fails. ### Details In `lib/stringify.js`, the comma branch collapses the array with `obj.join(',')`. For an element whose own `toString` is not callable, `ToPrimitive` skips it, falls through to `Object.prototype.valueOf` (which returns the object itself), and throws. Under `encodeValuesOnly: true`, the same element first reaches `String(str)` in `utils.encode` (`lib/utils.js`) and throws there. Nested arrays (`{ "a": [[{ "toString": "x" }]] }`) throw the same way, through `Array.prototype.toString`. The `indices`, `brackets`, and `repeat` formats walk into object elements instead of coercing them, and are not affected. Such elements can come from untrusted input. `JSON.parse` produces `{ "a": [{ "toString": "x" }] }` with no qs option involved. `qs.parse("a[0][toString]=x", { allowPrototypes: true })` keeps the `toString` key, and `qs.parse("a[][b]=c", { plainObjects: true })` produces a null-prototype element; the default parse options drop `toString` and produce ordinary objects, which do not throw. Express 4's default `query parser`, Express 5's `extended` query parser, and body-parser's `urlencoded({ extended: true })` all call `qs.parse` with `allowPrototypes: true`. #### PoC ```js var qs = require("qs"); qs.stringify(JSON.parse("{\"a\":[{\"toString\":\"x\"}]}"), { arrayFormat: "comma" }); qs.stringify(qs.parse("a[0][toString]=x", { allowPrototypes: true }), { arrayFormat: "comma" }); qs.stringify(qs.parse("a[][b]=c", { plainObjects: true }), { arrayFormat: "comma", encodeValuesOnly: true }); // TypeError: Cannot convert object to primitive value ``` #### Fix Applied in a952e75 on `main` and released as v6.16.1: an element whose string coercion would fail is coerced to its primitive `valueOf`, if any, or else to `Object.prototype.toString`, so it renders as `[object Object]`, the same as any other object element already does under the comma format. Nested arrays are walked the way `join` walks them. Every input that did not previously throw serializes exactly as before, and a `toString` or `valueOf` that itself throws still propagates. ### Affected versions `>=6.7.0 <6.16.1`, fixed in v6.16.1. The `join` path was introduced with the comma format in b84acba and first shipped in v6.7.0; v6.6.x and earlier ignore `arrayFormat: 'comma'` and are not affected. The `encodeValuesOnly` `String()` site was introduced in 4c4b23d (v6.11.1); before that, the same input throws at `join`. Every release from v6.7.0 through v6.16.0 is affected. ### Impact An unauthenticated request can make any code path that re-serializes attacker-influenced data with `qs.stringify` and `arrayFormat: 'comma'` (for example, rebuilding a query string from `req.query` for a redirect or an upstream request, or serializing a parsed JSON body) throw synchronously. In a typical Node.js HTTP framework the throw is caught by the framework error boundary and the affected request returns a 500; the process survives and other requests are unaffected. Where the call runs outside an error boundary, such as an `async` Express 4 handler (where the throw becomes an unhandled promise rejection) or a bare `http` request handler, the process exits, so the impact in that case depends on the application error handling rather than on qs. The default `arrayFormat` (`indices`) is not affected.

Affected packages

Ecosystem Package Introduced Fixed

Related vulnerabilities