CVE-2026-8723

Severity CVSS v4.0:
MEDIUM
Type:
CWE-476 NULL Pointer Dereference
Publication date:
17/05/2026
Last modified:
17/05/2026

Description

### Summary<br /> <br /> <br /> <br /> `qs.stringify` throws `TypeError` when called with `arrayFormat: &amp;#39;comma&amp;#39;` and `encodeValuesOnly: true` on an array containing `null` or `undefined`. The throw is synchronous and not handled by any of qs&amp;#39;s null-related options (`skipNulls`, `strictNullHandling`).<br /> <br /> <br /> <br /> ### Details<br /> <br /> <br /> <br /> In the comma + `encodeValuesOnly` branch, `lib/stringify.js:145` mapped the array through the raw encoder before joining:<br /> <br /> <br /> <br /> ```js<br /> <br /> <br /> <br /> obj = utils.maybeMap(obj, encoder);<br /> <br /> <br /> <br /> ```<br /> <br /> <br /> <br /> `utils.encode` (`lib/utils.js:195`) reads `str.length` with no null guard, so a `null` or `undefined` element throws `TypeError`. `skipNulls` and `strictNullHandling` are both checked in the per-element loop below this line and never get a chance to run.<br /> <br /> <br /> <br /> Same class of bug as the filter-array path fixed in 0c180a4. The vulnerable shape of the comma + `encodeValuesOnly` branch was introduced in 4c4b23d ("encode comma values more consistently", PR #463, 2023-01-19), first released in v6.11.1.<br /> <br /> <br /> <br /> #### PoC<br /> <br /> <br /> <br /> ```js<br /> <br /> <br /> <br /> const qs = require(&amp;#39;qs&amp;#39;);<br /> <br /> <br /> <br /> qs.stringify({ a: [null, &amp;#39;b&amp;#39;] }, { arrayFormat: &amp;#39;comma&amp;#39;, encodeValuesOnly: true });<br /> <br /> <br /> <br /> qs.stringify({ a: [undefined, &amp;#39;b&amp;#39;] }, { arrayFormat: &amp;#39;comma&amp;#39;, encodeValuesOnly: true });<br /> <br /> <br /> <br /> qs.stringify({ a: [null] }, { arrayFormat: &amp;#39;comma&amp;#39;, encodeValuesOnly: true });<br /> <br /> <br /> <br /> // TypeError: Cannot read properties of null (reading &amp;#39;length&amp;#39;)<br /> <br /> <br /> <br /> // at encode (lib/utils.js:195:13)<br /> <br /> <br /> <br /> // at Object.maybeMap (lib/utils.js:322:37)<br /> <br /> <br /> <br /> // at stringify (lib/stringify.js:145:25)<br /> <br /> <br /> <br /> ```<br /> <br /> <br /> <br /> #### Fix<br /> <br /> <br /> <br /> `lib/stringify.js:145`, applied in 21f80b3 on `main` and released as v6.15.2:<br /> <br /> <br /> <br /> ```diff<br /> <br /> <br /> <br /> - obj = utils.maybeMap(obj, encoder);<br /> <br /> <br /> <br /> + obj = utils.maybeMap(obj, function (v) {<br /> <br /> <br /> <br /> + return v == null ? v : encoder(v);<br /> <br /> <br /> <br /> + });<br /> <br /> <br /> <br /> ```<br /> <br /> <br /> <br /> `null` and `undefined` now pass through `maybeMap` unchanged and reach the `join(&amp;#39;,&amp;#39;)` step as-is. For `{ a: [null, &amp;#39;b&amp;#39;] }` this produces `a=,b`, matching the non-`encodeValuesOnly` comma path (which already joins before encoding and produces `a=%2Cb` for the same input). Single-element `[null]` arrays still collapse via the existing `obj.join(&amp;#39;,&amp;#39;) || null` and remain subject to `skipNulls` / `strictNullHandling` in the main loop.<br /> <br /> <br /> <br /> ### Affected versions<br /> <br /> <br /> <br /> `&gt;=6.11.1