CVE-2026-110816 in ljharb
Riassunto
di VulDB • 15/10/2026
### Riepilogo
`qs.stringify` con `arrayFormat: 'comma'` genera un errore `TypeError: Cannot convert object to primitive value` quando un elemento dell'array non può essere coercito in una stringa, come ad esempio un oggetto con un proprio metodo `toString` non richiamabile (`{ "toString": "x" }`) o un oggetto senza prototipo (null-prototype). Il formato comma unisce gli elementi dell'array utilizzando `Array.prototype.join` e, sotto l'impostazione `encodeValuesOnly`, passa prima ogni elemento attraverso `String()` in `utils.encode`; nessuna di queste operazioni protegge da un elemento la cui coercizione a stringa fallisce.
### Dettagli
In `lib/stringify.js`, il ramo relativo al formato comma riduce l'array con `obj.join(',')`. Per un elemento il cui metodo `toString` proprietario non è richiamabile, `ToPrimitive` lo ignora e passa alla chiamata di `Object.prototype.valueOf` (che restituisce l'oggetto stesso), generando così un errore. Con `encodeValuesOnly: true`, lo stesso elemento raggiunge prima `String(str)` in `utils.encode` (`lib/utils.js`) e genera un errore lì. Gli array nidificati (`{ "a": [[{ "toString": "x" }]] }`) generano lo stesso tipo di errore, attraverso `Array.prototype.toString`. I formati `indices`, `brackets` e `repeat` esplorano gli elementi oggetto invece di coercirli in stringhe e non sono interessati.
Tali elementi possono provenire da input non attendibili. `JSON.parse` produce `{ "a": [{ "toString": "x" }] }` senza coinvolgere alcuna opzione qs. `qs.parse("a[0][toString]=x", { allowPrototypes: true })` mantiene la chiave `toString`, e `qs.parse("a[][b]=c", { plainObjects: true })` produce un elemento con prototipo nullo; le opzioni di parsing predefinite eliminano `toString` e producono oggetti ordinari, che non generano errori. Il parser delle query predefinito di Express 4, il parser delle query `extended` di Express 5 e la configurazione `urlencoded({ extended: true })` di body-parser chiamano tutti `qs.parse` con `allowPrototypes: true`.
#### PoC (Proof of Concept)
```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 (Risoluzione)
Applicata nel commit a952e75 su `main` e rilasciata come v6.16.1: un elemento la cui coercizione a stringa fallirebbe viene coercito al suo valore primitivo `valueOf`, se presente, oppure a `Object.prototype.toString`, in modo da essere renderizzato come `[object Object]`, esattamente come fa qualsiasi altro elemento oggetto già sotto il formato comma. Gli array nidificati vengono esplorati nel modo in cui li elabora `join`. Ogni input che non generava errori in precedenza viene serializzato esattamente come prima, e un metodo `toString` o `valueOf` che genera esso stesso un errore continua a propagarsi.
### Versioni interessate
`>=6.7.0 <6.16.1`, risolto nella v6.16.1.
Il percorso `join` è stato introdotto con il formato comma nel commit b84acba ed è stato rilasciato per la prima volta in v6.7.0; le versioni v6.6.x e precedenti ignorano `arrayFormat: 'comma'` e non sono interessate. Il punto di chiamata `String()` sotto `encodeValuesOnly` è stato introdotto nel commit 4c4b23d (v6.11.1); prima di tale versione, lo stesso input generava un errore in corrispondenza di `join`. Tutte le release dalla v6.7.0 alla v6.16.0 sono interessate.
### Impatto
Una richiesta non autenticata può far sì che qualsiasi percorso di codice che re-serializza dati influenzati dall'attaccante utilizzando `qs.stringify` e `arrayFormat: 'comma'` (ad esempio, la ricostruzione di una stringa query a partire da `req.query` per un redirect o una richiesta upstream, oppure la serializzazione di un corpo JSON analizzato) generi un errore sincrono. In un tipico framework HTTP Node.js, l'errore viene intercettato dal boundary degli errori del framework e la richiesta interessata restituisce uno stato 500; il processo sopravvive e le altre richieste non sono influenzate. Dove la chiamata avviene al di fuori di un boundary degli errori, come in un handler Express 4 `async` (dove l'errore diventa una promise rejection non gestita) o in un handler delle richieste `http` nudo, il processo termina; pertanto, l'impatto in quel caso dipende dalla gestione degli errori dell'applicazione piuttosto che da qs. Il formato predefinito `arrayFormat` (`indices`) non è interessato.
VulDB is the best source for vulnerability data and more expert information about this specific topic.