Vulnerabilidad en `arrayLimit` en qs (CVE-2026-2391)
Gravedad CVSS v4.0:
MEDIA
Tipo:
CWE-20
Validación incorrecta de entrada
Fecha de publicación:
12/02/2026
Última modificación:
12/02/2026
Descripción
### Resumen<br />
La opción `arrayLimit` en qs no aplica límites para valores separados por comas cuando `comma: true` está habilitado, permitiendo a los atacantes causar denegación de servicio a través del agotamiento de memoria. Esto es un bypass de la aplicación del límite de array, similar al bypass de notación de corchetes abordado en GHSA-6rw7-vpxm-498p (CVE-2025-15284).<br />
<br />
### Detalles<br />
Cuando la opción `comma` se establece en `true` (no es el valor predeterminado, pero es configurable en las aplicaciones), qs permite analizar cadenas separadas por comas como arrays (por ejemplo, `?param=a,b,c` se convierte en `[&#39;a&#39;, &#39;b&#39;, &#39;c&#39;]`). Sin embargo, la verificación de límite para `arrayLimit` (predeterminado: 20) y la opción throwOnLimitExceeded ocurren después de la lógica de manejo de comas en `parseArrayValue`, lo que permite un bypass. Esto permite la creación de arrays arbitrariamente grandes a partir de un solo parámetro, lo que lleva a una asignación excesiva de memoria.<br />
<br />
Código vulnerable (lib/parse.js: líneas ~40-50):<br />
```js<br />
if (val &amp;&amp; typeof val === &#39;string&#39; &amp;&amp; options.comma &amp;&amp; val.indexOf(&#39;,&#39;) &gt; -1) {<br />
return val.split(&#39;,&#39;);<br />
}<br />
<br />
if (options.throwOnLimitExceeded &amp;&amp; currentArrayLength &gt;= options.arrayLimit) {<br />
throw new RangeError(&#39;Array limit exceeded. Only &#39; + options.arrayLimit + &#39; element&#39; + (options.arrayLimit === 1 ? &#39;&#39; : &#39;s&#39;) + &#39; allowed in an array.&#39;);<br />
}<br />
<br />
return val;<br />
```<br />
El `split(&#39;,&#39;)` devuelve el array inmediatamente, omitiendo la verificación de límite subsiguiente. La fusión posterior a través de `utils.combine` no evita la asignación, incluso si marca desbordamientos para arrays dispersos. Esta discrepancia permite a los atacantes enviar un solo parámetro con millones de comas (por ejemplo, `?param=,,,,,,,,...`), asignando arrays masivos en memoria sin activar los límites. Bypassea la intención de `arrayLimit`, que se aplica correctamente para las notaciones indexadas (`a[0]=`) y de corchetes (`a[]=`) (esta última corregida en v6.14.1 según GHSA-6rw7-vpxm-498p).<br />
<br />
### PoC<br />
Prueba 1 - Bypass básico:<br />
```<br />
npm install qs<br />
```<br />
<br />
```js<br />
const qs = require(&#39;qs&#39;);<br />
<br />
const payload = &#39;a=&#39; + &#39;,&#39;.repeat(25); // 26 elements after split (bypasses arrayLimit: 5)<br />
const options = { comma: true, arrayLimit: 5, throwOnLimitExceeded: true };<br />
<br />
try {<br />
const result = qs.parse(payload, options);<br />
console.log(result.a.length); // Outputs: 26 (bypass successful)<br />
} catch (e) {<br />
console.log(&#39;Limit enforced:&#39;, e.message); // Not thrown<br />
}<br />
```<br />
Configuración:<br />
- `comma: true`<br />
- `arrayLimit: 5`<br />
- `throwOnLimitExceeded: true`<br />
<br />
Esperado: Lanza el error &#39;Array limit exceeded&#39;.<br />
Real: Analiza con éxito, creando un array de longitud 26.<br />
<br />
### Impacto<br />
Denegación de Servicio (DoS) a través del agotamiento de memoria.
Impacto
Puntuación base 4.0
6.30
Gravedad 4.0
MEDIA
Puntuación base 3.x
3.70
Gravedad 3.x
BAJA



