May 22, 2026·7 min read·47 visits
A configuration-dependent vulnerability in qs.stringify allows attackers to crash the hosting Node.js process by supplying arrays with null or undefined elements when comma formatting and encodeValuesOnly are enabled.
The qs query string parsing and serialization library for Node.js is vulnerable to a synchronous Denial of Service (DoS) attack. The vulnerability manifests as a process-terminating TypeError when processing arrays with null or undefined elements under specific configuration parameters.
The qs package is a fundamental utility in the Node.js ecosystem used for parsing and stringifying URL query strings. It provides extensive configuration options for handling complex data structures, including nested objects and arrays. Applications frequently use this library to serialize data before constructing HTTP requests or to parse incoming request parameters. The vulnerability resides in the serialization logic of the qs.stringify function, specifically within the array formatting module.
This flaw is classified under CWE-476 (NULL Pointer Dereference) as it involves an unsafe property access on a null or undefined object type in JavaScript. The condition is triggered exclusively when two specific options are provided to qs.stringify: arrayFormat: 'comma' and encodeValuesOnly: true. When these options are active, the library attempts to process array elements before standard null-handling checks are evaluated.
The immediate consequence of this vulnerability is a synchronous TypeError. In a standard Node.js environment, unhandled synchronous exceptions terminate the worker process immediately. If an application routes attacker-controlled data into qs.stringify using the affected configuration without a surrounding try-catch block, remote attackers can achieve a persistent Denial of Service (DoS) state by continuously sending malformed inputs.
The vulnerability stems from an architectural flaw in how the qs library sequences array formatting operations and data encoding. In lib/stringify.js, when the arrayFormat: 'comma' option is specified, the execution flow enters a dedicated branch designed to collapse the array into a single comma-separated string early in the processing loop. This early evaluation path bypasses the standard null-validation mechanisms that occur later in the object traversal logic.
Within this branch, if the encodeValuesOnly option is active, the code executes obj = utils.maybeMap(obj, encoder);. This operation applies the selected encoding function to every element in the array prior to the string joining operation. The default encoder utilized by the library, located at lib/utils.js:195, expects a string-like object as its input parameter. It immediately attempts to access the .length property of the provided value to determine encoding requirements.
When an array contains a null or undefined element, the utils.maybeMap function passes these specific types directly to the default encoder. The JavaScript runtime cannot access properties on null or undefined, causing the encoder to throw a TypeError: Cannot read properties of null (reading 'length'). Because this mapping operation occurs before the main execution loop evaluates options like skipNulls and strictNullHandling, those protective configurations are rendered entirely ineffective against this crash.
A detailed review of the vulnerable code path demonstrates the unsafe delegation to the encoder function. In versions >=6.11.1 <6.15.2, the implementation in lib/stringify.js at line 145 passes array elements directly to the encoder without verifying their type. This blind delegation assumes that all array elements are primitives that possess a .length property or can be safely coerced by the encoder.
The vulnerable snippet executes the following logic:
if (generateArrayPrefix === 'comma' && isArray(obj)) {
if (encodeValuesOnly && encoder) {
obj = utils.maybeMap(obj, encoder);
}
objKeys = [{ value: obj.length > 0 ? obj.join(',') || null : void undefined }];
}The patch introduced in commit 21f80b33e5c8b3f7eba1034fff0da4a4a37a1d41 remediates this by introducing an inline validation wrapper around the encoder function. The modification ensures that null or undefined values are returned verbatim, preventing them from reaching the vulnerable property access within the encoder.
--- a/lib/stringify.js
+++ b/lib/stringify.js
@@ -142,7 +142,9 @@ var stringify = function stringify(
if (generateArrayPrefix === 'comma' && isArray(obj)) {
// we need to join elements in
if (encodeValuesOnly && encoder) {
- obj = utils.maybeMap(obj, encoder);
+ obj = utils.maybeMap(obj, function (v) {
+ return v == null ? v : encoder(v);
+ });
}
objKeys = [{ value: obj.length > 0 ? obj.join(',') || null : void undefined }];This fix successfully averts the exception by utilizing the loose equality operator (v == null), which correctly identifies both null and undefined primitives. These values bypass the encoder and proceed to the .join(',') operation, which standard JavaScript arrays inherently handle by coercing them to empty strings.
Exploiting CVE-2026-8723 requires the attacker to identify an application endpoint that invokes qs.stringify using the exact combination of arrayFormat: 'comma' and encodeValuesOnly: true. Furthermore, the attacker must have a mechanism to supply an array containing at least one null or undefined element into the data structure processed by this function. This data injection typically occurs via JSON request bodies or URL-encoded payloads that the server reconstructs into JavaScript objects.
Once the preconditions are met, exploitation is trivial. The attacker submits the malformed payload, triggering the execution path described in the root cause analysis. Node.js processes the request synchronously until it encounters the qs.stringify call. The subsequent TypeError bubbles up the call stack immediately. If the server implementation does not wrap the vulnerable function call in a try-catch block, the exception reaches the Node.js event loop top-level handler, resulting in process termination.
The following proof-of-concept demonstrates the fundamental crash mechanics. When executed, each of these statements terminates the script execution entirely unless caught by an error handler.
const qs = require('qs');
// Demonstrating the crash with null values
try {
qs.stringify({ a: [null, 'b'] }, { arrayFormat: 'comma', encodeValuesOnly: true });
} catch (e) {
console.error('Crash verified:', e.message);
}
// Demonstrating the crash with undefined values
try {
qs.stringify({ a: [undefined, 'b'] }, { arrayFormat: 'comma', encodeValuesOnly: true });
} catch (e) {
console.error('Crash verified:', e.message);
}The primary security impact of this vulnerability is a localized Denial of Service (DoS). The CVSS v3.1 vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L accurately reflects this, highlighting that the vulnerability is network-exploitable, requires no specific privileges or user interaction, and possesses low attack complexity. The availability impact is rated as 'Low' because the crash typically affects a single worker process rather than completely halting a distributed system or cluster.
However, in environments running single-threaded Node.js applications without process managers (such as PM2) or orchestration-level health checks (such as Kubernetes readiness probes), a single payload can render the service permanently unavailable. Even in resilient environments, continuous exploitation forces constant application restarts. This degrades overall service performance, drops active connections, and heavily consumes server resources during process initialization.
The exploit maturity is currently documented as Proof of Concept (PoC). The provided EPSS score of 0.00044 (13.63 percentile) indicates a very low observed probability of exploitation in the wild. Despite this low statistical risk, the deterministic nature of the crash and the widespread usage of the qs library necessitate prompt remediation.
The definitive resolution for CVE-2026-8723 is to upgrade the qs library to version 6.15.2 or later. This version contains the patch that correctly sanitizes array elements before passing them to the encoder function. Package managers will typically resolve this issue automatically by running npm update qs or adjusting the package.json constraints to exclude the vulnerable version range >=6.11.1 <6.15.2.
If upgrading the library is not immediately feasible due to legacy dependencies, developers must implement temporary mitigations. The most straightforward approach is to modify the qs.stringify options object to remove either arrayFormat: 'comma' or encodeValuesOnly: true. Disabling either of these specific options prevents the execution flow from entering the vulnerable branch within lib/stringify.js.
Additionally, applications can implement defensive programming practices around data serialization. Wrapping all calls to qs.stringify within a try-catch block ensures that any unexpected type errors are caught and handled gracefully, preventing process termination. Implementing strict input validation to sanitize data objects and remove literal null or undefined values before serialization provides a secondary layer of defense.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L| Product | Affected Versions | Fixed Version |
|---|---|---|
qs ljharb | >= 6.11.1 < 6.15.2 | 6.15.2 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-476 |
| Attack Vector | Network |
| CVSS v3.1 | 5.3 |
| EPSS Score | 0.00044 |
| Impact | Denial of Service (Process Termination) |
| Exploit Status | poc |
| KEV Status | Not Listed |
NULL Pointer Dereference
CVE-2024-29203 identifies a cross-site scripting (XSS) vulnerability in the content ingestion and parsing mechanics of TinyMCE rich text editor. Due to a failure to enforce sandbox attributes on dynamic iframe elements and safely handle legacy embed objects, unauthenticated attackers can inject malicious elements that execute scripts within the context of the parent application session.
A technical breakdown of the OS command injection vulnerability in the shell-quote NPM package (CVE-2026-9277 / GHSA-w7jw-789q-3m8p). The bug resides in the character-by-character backslash-escaping logic applied to the .op field of object-tokens within the quote() function, which fails to match and escape line terminators due to a regex matching oversight in JavaScript. This allows unauthenticated remote attackers to execute arbitrary shell commands if they can control inputs processed by this library.
A high-severity memory corruption vulnerability exists in the V8 JavaScript engine of Google Chrome before versions 149.0.7827.102/103. The flaw arises from an incorrect bounds-check elimination during JIT compilation by the TurboFan optimizer, allowing remote attackers to achieve out-of-bounds read and write access inside the sandboxed renderer process.
An improper authentication vulnerability (CWE-287) exists in the legacy, deprecated Internet Key Exchange version 1 (IKEv1) key exchange protocol implementation in Check Point Security Gateways. The vulnerability is caused by a logic flow weakness during the certificate validation process for Remote Access VPN and Mobile Access (SSL VPN) connections. An unauthenticated remote attacker can exploit this weakness to bypass user authentication entirely, establishing a fully functional Remote Access VPN connection without a valid password.
GeoNode versions prior to 4.4.5 and 5.0.2 are vulnerable to Server-Side Request Forgery (SSRF) in the service registration endpoint. Authenticated attackers with low privileges can exploit insufficient input validation in the Web Map Service (WMS) registration module to force the application server to make outbound network queries to loopback addresses, private RFC1918 subnets, link-local scopes, and cloud metadata endpoints. This technical report details the mechanics of the vulnerability, the underlying architectural flaw, and how to effectively remediate and mitigate the associated security risks.
CVE-2022-0492 is a high-severity missing authorization vulnerability in the Linux kernel's Control Groups (cgroups) v1 implementation. The flaw resides within the cgroup_release_agent_write function in kernel/cgroup/cgroup-v1.c, where the kernel fails to validate if the process writing to the release_agent file possesses administrative capabilities in the initial user namespace. This allows a local attacker inside a container with root privileges (UID 0) to abuse user namespaces, mount a cgroups v1 directory, modify the release_agent parameter, and execute arbitrary commands on the host system as host root, effectively achieving a complete container escape.