CVE-2026-2391 Affecting json-server package, versions <0.17.4-r5


Severity

Recommended
0.0
high
0
10

Snyk's Security Team recommends NVD's CVSS assessment. Learn more

Threat Intelligence

EPSS
0.04% (12th percentile)

Do your applications use this vulnerable package?

In a few clicks we can analyze your entire application and see what components are vulnerable in your application, and suggest you quick fixes.

Test your applications
  • Snyk IDSNYK-CHAINGUARDLATEST-JSONSERVER-15338253
  • published24 Feb 2026
  • disclosed12 Feb 2026

Introduced: 12 Feb 2026

NewCVE-2026-2391  (opens in a new tab)

How to fix?

Upgrade Chainguard json-server to version 0.17.4-r5 or higher.

NVD Description

Note: Versions mentioned in the description apply only to the upstream json-server package and not the json-server package as distributed by Chainguard. See How to fix? for Chainguard relevant fixed versions and status.

Summary

The arrayLimit option in qs does not enforce limits for comma-separated values when comma: true is enabled, allowing attackers to cause denial-of-service via memory exhaustion. This is a bypass of the array limit enforcement, similar to the bracket notation bypass addressed in GHSA-6rw7-vpxm-498p (CVE-2025-15284).

Details

When the `comma` option is set to `true` (not the default, but configurable in applications), qs allows parsing comma-separated strings as arrays (e.g., `?param=a,b,c` becomes `['a', 'b', 'c']`). However, the limit check for `arrayLimit` (default: 20) and the optional throwOnLimitExceeded occur after the comma-handling logic in `parseArrayValue`, enabling a bypass. This permits creation of arbitrarily large arrays from a single parameter, leading to excessive memory allocation.

Vulnerable code (lib/parse.js: lines ~40-50):

if (val &amp;&amp; typeof val === &#39;string&#39; &amp;&amp; options.comma &amp;&amp; val.indexOf(&#39;,&#39;) &gt; -1) {
    return val.split(&#39;,&#39;);
}

if (options.throwOnLimitExceeded &amp;&amp; currentArrayLength &gt;= options.arrayLimit) {     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;); }

return val;

The split(&#39;,&#39;) returns the array immediately, skipping the subsequent limit check. Downstream merging via utils.combine does not prevent allocation, even if it marks overflows for sparse arrays.This discrepancy allows attackers to send a single parameter with millions of commas (e.g., ?param=,,,,,,,,...), allocating massive arrays in memory without triggering limits. It bypasses the intent of arrayLimit, which is enforced correctly for indexed (a[0]=) and bracket (a[]=) notations (the latter fixed in v6.14.1 per GHSA-6rw7-vpxm-498p).

PoC

**Test 1 - Basic bypass:** ``` npm install qs ```
const qs = require(&#39;qs&#39;);

const payload = &#39;a=&#39; + &#39;,&#39;.repeat(25);  // 26 elements after split (bypasses arrayLimit: 5)
const options = { comma: true, arrayLimit: 5, throwOnLimitExceeded: true };

try {
  const result = qs.parse(payload, options);
  console.log(result.a.length);  // Outputs: 26 (bypass successful)
} catch (e) {
  console.log(&#39;Limit enforced:&#39;, e.message);  // Not thrown
}

Configuration:

  • comma: true
  • arrayLimit: 5
  • throwOnLimitExceeded: true

Expected: Throws "Array limit exceeded" error. Actual: Parses successfully, creating an array of length 26.

Impact

Denial of Service (DoS) via memory exhaustion.

CVSS Base Scores

version 3.1