Jul 25, 2026·7 min read·14 visits
Shescape is vulnerable to an O(N^2) algorithmic complexity Denial of Service due to nested array copying and string joining inside its default flag-protection processing loop.
A high-severity algorithmic-complexity vulnerability in the Node.js library Shescape leads to a Quadratic-Time Denial of Service (DoS) when flag protection is enabled. By supplying inputs containing repetitive control characters and hyphens, remote attackers can trigger an inefficient nested loop in Shescape's argument-composition logic, blocking the Node.js single-threaded event loop and causing total application Denial of Service.
Shescape is an open-source command-argument escaping library designed to defend Node.js applications against command injection attacks. It is widely deployed to safely parse and escape untrusted user inputs before passing them to operating system shell processors. The default operating configuration of Shescape includes an active security control known as flag protection. This control is designed to identify and process arguments that begin with a hyphen, preventing attackers from injecting arbitrary flags into system utilities.
The default implementation of this flag-protection subsystem contains a critical algorithmic vulnerability. When processing inputs that contain repeated sequences of null characters and hyphens, the underlying argument-composition loop degrades into quadratic time complexity. This vulnerability is classified under CWE-407 (Inefficient Algorithmic Complexity) and CWE-400 (Uncontrolled Resource Consumption).
For applications executing in the single-threaded Node.js runtime, this algorithmic degradation represents an immediate Denial of Service vector. Because the CPU-intensive evaluation occurs entirely within the main execution thread, the event loop becomes completely blocked. Remote unauthenticated attackers can exploit this behavior by submitting small, specially crafted request parameters, exhausting server CPU resources and halting all concurrent operations.
The root cause of this vulnerability lies within the compose function located in the source file src/internal/compose.js. This function wraps the core escaping routines in a protective layer that iteratively strips and validates flags. The pre-patched code parses arguments into a structural array using a flag-extraction utility. It then enters a while loop that continues execution as long as there are remaining array elements and the parsed leading element escapes to an empty string.
During each iteration of this loop, the application performs two operations that scale linearly with the size of the remaining input. First, it reconstructs the working argument string by executing rest.join(""). Second, it performs an array destructuring assignment using the rest operator: [preFlag, , ...rest] = rest. This destructuring pattern forces the JavaScript engine to allocate a new array structure and execute a shallow copy of all remaining elements.
Because these linear O(N) operations are nested directly inside a loop that iterates up to N times, the overall execution performance degrades quadratically (O(N^2)). If the input argument consists of N repetitive sequences of characters that resolve to empty escaped strings, the engine must perform N*(N-1)/2 copy and join steps. An input containing 32,000 such segments forces the loop to run 32,000 times, performing intensive allocation and string construction on each pass, which completely blocks the CPU.
The vulnerable implementation of the flag-protection loop in src/internal/compose.js is structured as follows:
export function compose({ escapeFn, flagFn, quoteFn }) {
return (arg) => {
// flagFn splits the input argument into fragments
let [preFlag, , ...rest] = flagFn(arg);
// The while loop iterates if the escaped prefix is empty
while (rest.length > 0 && escapeFn(preFlag) === "") {
// LINEAR OPERATION: Reconstructs string by joining remaining elements
arg = rest.join("");
// LINEAR OPERATION: Destructuring creates a shallow copy of the array slice
[preFlag, , ...rest] = rest;
}
return escape(arg);
};
}The security patch introduced in commit 43d70b59d09bbe5c3fd02ef08b3a123e977ed9de resolves this computational bottleneck. Rather than modifying and copying the array during each iteration, the patched version implements an index-based linear scanning loop:
export function compose({ escapeFn, flagFn, quoteFn }) {
return (arg) => {
const fragments = flagFn(arg);
let idx = 0;
// CONSTANT TIME LOOP: Increments index counter to traverse elements
for (; idx < fragments.length - 2; idx += 2) {
const escapedFragment = escapeFn(fragments[idx]);
if (escapedFragment !== "") {
break;
}
}
// SINGLE LINEAR OPERATION: Performed exactly once after loop termination
arg = fragments.slice(idx).join("");
return escape(arg);
};
}This refactored logic replaces the nested O(N) copying operations with simple index incrementation. The loop body executes in O(1) constant time, and the expensive slice and join operations are deferred until the traversal has completed. This ensures that the overall time complexity remains strictly linear (O(N)) relative to the size of the array fragments.
Exploitation of this vulnerability requires the attacker to submit a payload that repeatedly satisfies the loop's continuation condition. The condition requires that the parsed fragment preFlag evaluates to an empty string under the configured escapeFn. The null byte character (\u0000) meets this criterion because Shescape's escape functions sanitize or strip null bytes, resulting in an empty string representation "".
To maintain the structure expected by the flag parser, each null byte must be accompanied by a hyphen delimiter. The resulting payload pattern is a continuous sequence of \u0000-. When the flag parser processes this input, it splits the payload into thousands of small fragments. On each iteration of the loop, the engine encounters a null byte, evaluates its escaped value to "", and proceeds to execute the nested O(N) string reconstruction and array copying operations.
A proof-of-concept script executing this attack demonstrates that a payload containing 32,000 repetitions of \u0000- is sufficient to freeze the Node.js event loop for approximately 20 seconds. This block duration scales exponentially as the size of the payload increases. Because the execution is synchronous, no other concurrent network requests or asynchronous events can be processed by the server during this window.
The security impact of this vulnerability is classified as high, receiving a CVSS v4.0 score of 8.7. The primary impact is on application availability. Because Node.js is designed with a single-threaded event loop, any synchronous operation that blocks execution halts the processing of all incoming HTTP requests. An attacker can exploit this to cause service unavailability with minimal network bandwidth.
Unlike memory corruption vulnerabilities that might lead to remote code execution or data leakage, this flaw does not compromise data confidentiality or integrity. The attack requirements are extremely low: no special privileges or user interactions are required to trigger the vulnerable code path. Any exposed input vector—such as form fields, query parameters, or file upload names—that routes unsanitized input to Shescape is a potential vector.
Furthermore, because Shescape is typically positioned on sensitive input-validation boundaries immediately before command execution, this vulnerability is often present in core security pipelines. If a monitoring system or load balancer attempts to check the health of an affected node while it is processing a malicious payload, the health check will fail, potentially causing container orchestrators to aggressively restart the instance and amplify the Denial of Service state across the infrastructure.
Remediation of GHSA-gm3r-q2wp-hw87 requires upgrading the Shescape library to a patched release. For applications utilizing the 3.x release line, the package must be updated to version 3.0.1 or higher. For legacy environments running the 2.x line, the package must be updated to the backported security release 2.1.14.
If an immediate package upgrade is not feasible, several defensive workarounds can be implemented at the application layer. The most effective temporary control is to enforce strict input validation on the length of all string arguments passed to Shescape. Limiting the maximum character length to a reasonable threshold, such as 256 characters, ensures that the quadratic complexity loop cannot scale to a point where it significantly degrades CPU performance.
Alternatively, applications can explicitly disable the flag-protection feature by initializing the library with the flagProtection option set to false. This bypasses the vulnerable code pathway entirely. However, this workaround must be applied with caution; disabling flag protection removes the defensive controls that prevent command-line flag injection, potentially exposing the application to argument-injection vulnerabilities if the processed inputs are subsequently used in sensitive shell execution contexts.
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
shescape Eric Cornelissen | >= 2.1.11, < 2.1.14 | 2.1.14 |
shescape Eric Cornelissen | >= 3.0.0, < 3.0.1 | 3.0.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-407 / CWE-400 |
| Attack Vector | Network |
| CVSS v4.0 Score | 8.7 (High) |
| Exploit Status | PoC Available |
| Remediation | Upgrade to v2.1.14 or v3.0.1 |
| Impact | Application Denial of Service (CPU Exhaustion) |
The product uses an algorithm with an inefficient worst-case time complexity, such as quadratic or exponential, allowing attackers to cause resource exhaustion.
CVE-2026-10032 is a DOM-based Cross-Site Scripting (XSS) vulnerability in Google's @a2ui/web_core Node.js library. The vulnerability is located within the openUrl utility function, which processes and opens dynamic URLs defined in layout configurations. Because the function fails to sanitize or validate the target URL scheme before passing it to the window.open browser sink, an attacker can specify a javascript: pseudo-protocol to execute arbitrary client-side script in the context of the host origin.
CVE-2026-59944 is a path traversal and link-following vulnerability in Composer, the PHP dependency manager. This flaw allows malicious or compromised packages to bypass previous path-hardening protections and perform arbitrary filesystem operations outside of their designated installation directory, leading to unauthorized permission modifications or execution proxy creations.
A critical Broken Object Level Authorization (BOLA) vulnerability was identified in Trigger.dev before version v4.5.2. An authenticated attacker could trigger a run replay and supply an arbitrary target environmentId belonging to a completely different tenant. Because the server failed to validate whether the target environment belonged to the same project or organization as the source run, it would execute the task within the victim's environment, resulting in unauthorized cross-tenant write operations and remote task execution.
A critical Server-Side Request Forgery (SSRF) vulnerability in Trigger.dev prior to version 4.5.2 allows authenticated organization members to configure webhook alert channels with unvalidated target URLs. This can lead to internal network scanning and cloud metadata extraction.
A Server-Side Request Forgery (SSRF) vulnerability exists in the rmcp OAuth client, which is part of the Model Context Protocol (MCP) Rust SDK. The vulnerability arises from insecure processing of the resource_metadata parameter in WWW-Authenticate headers returned by a malicious or compromised MCP server. The client parses and fetches absolute URLs from this header without validation of scheme, origin, or network routing, allowing remote attackers to initiate HTTP GET requests to local network interfaces, RFC 1918 private subnets, or cloud metadata endpoints.
A module allowlist bypass vulnerability (CVE-2026-92945 / GHSA-7q3f-wx44-378m) was identified in the vm2 sandboxing library prior to version 3.11.7. This flaw permits unauthenticated or untrusted code running within the sandbox environment to bypass explicit module restrictions. When the transitive resolution option is disabled, the system fails to validate file system path boundaries, allowing prefix-sharing sibling directories to be resolved and loaded, thereby escaping intended sandbox restrictions.