criticalJul 24, 2026

shescape: Shell Injection via Unescaped Parentheses on Windows CMD

Rohit Hatagale
AI Security Researcher, SecureLayer7

A shell injection flaw in the shescape npm library lets an attacker break out of CMD command context by supplying parentheses in user input, because those characters were never escaped before being…

Packageshescape
Ecosystemnpm
Affected< 2.1.14
Fixed in2.1.14
shescape: Shell Injection via Unescaped Parentheses on Windows CMD

The problem

shescape versions before 2.1.14 do not escape parentheses when building arguments for cmd.exe on Windows. Callers using the escape or escapeAll APIs with shell set to cmd.exe (or shell: true on a system where CMD is the default) are affected.

In CMD, parentheses are grouping and conditional-branching metacharacters. Leaving them unescaped means attacker-supplied input can close an open conditional group and inject a new command branch, achieving shell injection even inside what looks like a safely escaped argument.

Proof of concept

A working proof-of-concept for this issue in shescape, with the exact payload below.

javascript
// Attacker-controlled input
const payload = "x) else if a==a (echo y";

// Vulnerable usage
import * as cp from "node:child_process";
import { Shescape } from "shescape";

const shescape = new Shescape({ shell: "cmd.exe" });
const escapedPayload = shescape.escape(payload);

// Resulting command executed by CMD:
//   if defined FALSY (echo x) else if a==a (echo y)
//
// Expected output: "" (FALSY is undefined, so nothing echoes)
// Actual output:   "y"  (injected branch executes)
const result = cp.execSync(`if defined FALSY (echo ${escapedPayload})`, { shell: "cmd.exe" });
console.log(result.toString()); // prints "y"

The root cause is CWE-78 / CWE-150: the CMD-specific escape function in shescape never treated ( and ) as special characters, so it passed them through verbatim. In CMD, parentheses delimit command groups and if/else branches, so an unescaped ) closes the surrounding group and an immediately following else or second ( opens a new one.

The patch commits (b4b34c3 for v2, 43d70b5 for v3) add caret-escaping for both characters, replacing ( with ^( and ) with ^) in the CMD escape path. Because the fix is purely additive to the character replacement table, working backwards from it confirms exactly which characters were missing and why the payload above survives unmodified in all versions before 2.1.14.

The fix

Upgrade shescape to v2.1.14 (or v3.0.1 if already on v3). No configuration changes are needed after upgrading. As a temporary workaround, strip all ( and ) from untrusted input before passing it to shescape.

Reported by Eric Cornelissen.

References: [1][2][3][4][5][6]

Related research