Jun 9, 2026·6 min read·178 visits
An OS command injection vulnerability in shell-quote < 1.8.4 allows arbitrary command execution. The quote() function fails to escape line terminators within object-tokens due to a regular expression omission, enabling attackers to inject newlines that act as command separators in POSIX shells.
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.
The NPM package shell-quote is designed to provide utility functions for parsing and quoting shell commands in Node.js environments. Its core functionality allows developers to safely serialize arrays of parameters into sanitized shell commands or parse complex terminal input strings into structured representation arrays. Because the library is frequently utilized prior to invoking shell executors such as child_process.exec or child_process.spawn, any failure to securely escape metacharacters can lead to command execution vulnerabilities.
CVE-2026-9277 (tracked as GHSA-w7jw-789q-3m8p) represents a critical command injection flaw identified within the quote() function. When serializing an array containing object-tokens (representing operators, globs, or comments), the library attempts to dynamically escape special characters. However, a regular expression matching defect allows line terminators to pass through unescaped.
The vulnerability exposes a broad attack surface in applications that pass structured or partially structured inputs to the command formatter. If an attacker can manipulate elements within the array passed to quote(), or inject values parsed during environment variable expansion in parse(), they can achieve arbitrary command execution under the context of the running application process.
The technical root cause of CVE-2026-9277 lies in the character-by-character backslash-escaping logic applied to the .op property of object-tokens. Within the quote() function, the library iterates over input array elements. If an element is identified as an object containing an .op field, the library attempts to escape each character of the operator string using the JavaScript regular expression arg.op.replace(/(.)/g, '\\$1').
In ECMAScript engines, the dot (.) character class matches any single character except line terminators. Because the dot-all (/s) modifier is absent in this pattern, the regular expression ignores the Carriage Return (\r, U+000D), Line Feed (\n, U+000A), Line Separator (U+2028), and Paragraph Separator (U+2029). As a result, the replacement expression \\$1 is never executed for these line terminators.
When the serialized output is constructed, these line terminators are written into the final string with zero escaping or sanitization. In POSIX-compliant shells (such as sh, bash, dash, or zsh), a literal newline is treated as an implicit command separator. When the shell interpreter parses the final string, it executes the commands sequentially, completely separating the original command context from the injected suffix commands.
The vulnerable code block inside quote() processes the token's operator as shown below:
// Vulnerable logic in shell-quote <= 1.8.3
if (arg && typeof arg === 'object') {
if (arg.op === 'glob') {
return arg.pattern;
}
// Character-by-character escape attempts using dot class
// This regex does not match line terminators (\n, \r)
return arg.op.replace(/(.)/g, '\\$1');
}An analysis of the fix implemented in commit 1518179 shows that the maintainers chose to eliminate the dynamic character-by-character escaping strategy entirely. Instead, they transitioned to strict schema and type validation for all object-tokens.
// Patched logic in shell-quote 1.8.4
// Rather than trying to escape free-form operators, shape validation is applied
if (arg && typeof arg === 'object') {
if (arg.op === 'glob') {
if (typeof arg.pattern !== 'string' || /[\r\n]/.test(arg.pattern)) {
throw new TypeError('glob pattern must be a string and cannot contain line terminators');
}
return arg.pattern;
}
if (typeof arg.op === 'string' && controlOperators.indexOf(arg.op) !== -1) {
return arg.op;
}
throw new TypeError('Invalid operator: ' + arg.op);
}The patch evaluates the object properties against structural limits. If an object is configured as a glob, the program checks for line terminators. If the object-token uses an operator, it must match one of the items inside controlOperators array (e.g. &&, ||, ;). Any input that deviates from this strict configuration raises a TypeError and halts the process.
Exploitation requires that an application exposes either the inputs of quote() or the custom environment variable expansion callback of parse() to attacker-controlled data. In a direct object injection scenario, the target application accepts JSON payloads and passes them to quote() without validation.
The diagram below models the sequence of execution leading to arbitrary command execution.
In secondary attack vectors, the application leverages parse(cmd, envFn). The envFn is a developer-supplied callback designed to resolve shell variables. If the callback resolves a variable to an object-token whose operator is sourced from external input, an injection occurs. For instance, if an HTTP header determines the value returned by envFn as { op: "&&\nid" }, the returned token is subsequently passed back to quote(), which fails to escape the newline.
POSIX shell engines interpret the unescaped newline as a command separator. While other characters within the payload such as i and d are escaped as \i\d, the shell strips these redundant backslashes, yielding the executable instruction id.
The impact of CVE-2026-9277 is categorized as high, with an assigned CVSS v3.1 base score of 8.1. An exploit allows an unauthenticated remote attacker to execute arbitrary OS commands within the context of the underlying Node.js application process. This can lead to complete compromise of the host system, horizontal privilege escalation, and lateral movement within the network.
Because the injected shell command executes with the permissions of the Node.js process, applications running as a high-privilege user or root are highly vulnerable. Attackers can leverage this access to extract environmental variables, read application source code, download malicious binaries, or establish persistent reverse shell connections.
The risk remains elevated for cloud-native configurations where environment variables often store database credentials, API keys, and cloud provider IAM metadata tokens. The lack of complex prerequisites other than exposing input serialization vectors makes applications processing multi-tenant structured configuration particularly susceptible.
The primary remediation is upgrading the shell-quote package dependency to version 1.8.4 or later. This can be accomplished by updating the dependency declaration within the project's package.json file and executing a package manager update cycle.
npm install shell-quote@1.8.4In environments where immediate dependency upgrades are blocked by legacy system constraints, teams must deploy input validation layers. Applications should enforce schema validation on all inputs parsed by, or passed to, the library. Ensuring that any array processed by quote() contains strictly string elements, or validating that any object-token lacks line terminators, effectively mitigates the vulnerability vector.
Additionally, developers should analyze any implementation of custom environment callbacks in parse(). Ensuring that the envFn only returns sanitized string values and never passes raw, unvalidated object-tokens prevents nested serialization exploitation paths.
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
shell-quote ljharb | >= 1.1.0, < 1.8.4 | 1.8.4 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-78 / CWE-77 |
| Attack Vector | Network (AV:N) |
| CVSS Severity | 8.1 (High) |
| EPSS Score | 0.00068 |
| Exploit Status | Proof of Concept |
| CISA KEV Status | Not Listed |
The software constructs an OS command using externally-influenced input, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component.
A critical Server-Side Request Forgery (SSRF) bypass vulnerability exists in CodeWhale before version 0.8.64 (and version 0.8.41 in the 0.8.x branch) due to a Time-of-Check to Time-of-Use (TOCTOU) bug in its DNS pre-flight validation mechanism. By returning a temporary resolution failure during validation and subsequently resolving to restricted IPs during HTTP execution, attackers can bypass security rules.
An argument injection vulnerability in CodeWhale (CVE-2026-75912 / GHSA-c6mw-8xh8-gpq6) allows unauthenticated remote attackers to execute arbitrary option commands on the git binary. By passing malicious command-line flags inside git_blame and git_show tool helper functions, an attacker can bypass typical access controls to read arbitrary local system files via the underlying git process.
SurrealDB prior to version 3.2.0 is vulnerable to an authorization bypass where authenticated users can invoke custom API endpoints belonging to other tenants. This cross-tenant data access occurs because the system fails to validate authorization scope boundaries against request-supplied namespace and database identifiers before executing scripts with elevated definer's rights.
An incorrect authorization vulnerability (CWE-863) in SurrealDB allows authenticated, low-privileged users to execute unauthorized state-modifying queries. This occurs because the database disabled permissions during evaluation of custom PERMISSIONS WHERE predicates to prevent infinite recursion.
SiYuan before v3.7.4 fails to enforce publish-access filters on five filetree path-resolution endpoints, allowing unauthenticated attackers to reconstruct private directory layouts and map document structures.
Prior to version v3.7.4, the SiYuan personal knowledge management system contained a critical logical authorization vulnerability within its database view rendering component. The flaws allowed unauthenticated remote attackers to bypass publish-access filters on databases, exposing sensitive Relation and Rollup cell contents belonging to private or password-protected repositories.