{"id":39630,"date":"2026-10-16T04:33:28","date_gmt":"2026-10-15T20:33:28","guid":{"rendered":"https:\/\/www.54iter.cn\/?vuln=cve-2026-110816"},"modified":"2026-10-16T04:33:28","modified_gmt":"2026-10-15T20:33:28","slug":"cve-2026-110816","status":"publish","type":"vuln","link":"https:\/\/www.54iter.cn\/?vuln=cve-2026-110816","title":{"rendered":"CVE-2026-110816 [MEDIUM] &#8211; ### Summary\n\n\n\n`qs.stringify` with `arrayFormat: &#8216;comma&#8217;` throws `TypeError: Cannot conver"},"content":{"rendered":"<p>### Summary<\/p>\n<p>`qs.stringify` with `arrayFormat: &#8216;comma&#8217;` 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` (`{ &#8220;toString&#8221;: &#8220;x&#8221; }`) 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.<\/p>\n<p>### Details<\/p>\n<p>In `lib\/stringify.js`, the comma branch collapses the array with `obj.join(&#8216;,&#8217;)`. 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 (`{ &#8220;a&#8221;: [[{ &#8220;toString&#8221;: &#8220;x&#8221; }]] }`) 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.<\/p>\n<p>Such elements can come from untrusted input. `JSON.parse` produces `{ &#8220;a&#8221;: [{ &#8220;toString&#8221;: &#8220;x&#8221; }] }` with no qs option involved. `qs.parse(&#8220;a[0][toString]=x&#8221;, { allowPrototypes: true })` keeps the `toString` key, and `qs.parse(&#8220;a[][b]=c&#8221;, { plainObjects: true })` produces a null-prototype element; the default parse options drop `toString` and produce ordinary objects, which do not throw. Express 4&#8217;s default `query parser`, Express 5&#8217;s `extended` query parser, and body-parser&#8217;s `urlencoded({ extended: true })` all call `qs.parse` with `allowPrototypes: true`.<\/p>\n<p>#### PoC<\/p>\n<p>&#8220;`js<\/p>\n<p>var qs = require(&#8220;qs&#8221;);<\/p>\n<p>qs.stringify(JSON.parse(&#8220;{&#8220;a&#8221;:[{&#8220;toString&#8221;:&#8221;x&#8221;}]}&#8221;), { arrayFormat: &#8220;comma&#8221; });<\/p>\n<p>qs.stringify(qs.parse(&#8220;a[0][toString]=x&#8221;, { allowPrototypes: true }), { arrayFormat: &#8220;comma&#8221; });<\/p>\n<p>qs.stringify(qs.parse(&#8220;a[][b]=c&#8221;, { plainObjects: true }), { arrayFormat: &#8220;comma&#8221;, encodeValuesOnly: true });<\/p>\n<p>\/\/ TypeError: Cannot convert object to primitive value<\/p>\n<p>&#8220;`<\/p>\n<p>#### Fix<\/p>\n<p>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.<\/p>\n<p>### Affected versions<\/p>\n<p>`&gt;=6.7.0 &lt;6.16.1`, fixed in v6.16.1.<\/p>\n<p>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: &#039;comma&#039;` 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.<\/p>\n<p>### Impact<\/p>\n<p>An unauthenticated request can make any code path that re-serializes attacker-influenced data with `qs.stringify` and `arrayFormat: &#039;comma&#039;` (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.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>### Summary<\/p>\n<p>`qs.stringify` with `arrayFormat: &#8216;comma&#8217;` throws `TypeError: Cannot convert object to primitive value` when an array element cannot be coerced to a string, such as an object with an ow<\/p>\n","protected":false},"template":"","meta":[],"categories":[],"tags":[],"class_list":["post-39630","vuln","type-vuln","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=\/wp\/v2\/vuln\/39630","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=\/wp\/v2\/vuln"}],"about":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=\/wp\/v2\/types\/vuln"}],"wp:attachment":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=39630"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=39630"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=39630"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}