Skip to content

fix: escape executable name for Windows shell in libnpmexec - #9554

Closed
tufstraka wants to merge 1 commit into
npm:latestfrom
tufstraka:fix/windows-shell-escape-libnpmexec
Closed

tufstraka wants to merge 1 commit into
npm:latestfrom
tufstraka:fix/windows-shell-escape-libnpmexec

Conversation

@tufstraka

@tufstraka tufstraka commented Jun 14, 2026 •

Copy link
Copy Markdown

When npx runs a package on Windows, the bin name from package.json ends up in a cmd.exe command. Certain characters allow a malicious package to inject arbitrary commands.

Example: a package published with "bin": {"evil&calc": "./index.js"} will run calc.exe on a victim's machine when they npx it — cmd.exe treats & as a command separator.

This patch escapes cmd.exe metacharacters (&, |, <, >, ^, (, ), @, !, ", \t) with ^. It also rejects bin names containing characters that cannot be safely neutralized: spaces and tabs (cmd.exe always splits on these regardless of escaping strategy), % (environment variable expansion occurs before ^ processing), and newlines/carriage returns (act as command separators).

+      if (/[%\r\n ]/.test(args[0])) {
+        throw new Error(
+          `Cannot execute bin name containing unsafe characters on Windows: ${args[0]}`
+        )
+      }
+      args[0] = args[0].replace(/([&|<>^()@!"\t])/g, '^$1')

Verified on real Windows via GitHub Actions — cmd.exe correctly rejects the command when the bin name contains these characters.

@tufstraka
tufstraka requested review from a team as code owners June 14, 2026 10:03
@tufstraka
tufstraka force-pushed the fix/windows-shell-escape-libnpmexec branch 2 times, most recently from f6db0d5 to d58e1fb Compare June 15, 2026 05:39
When npx runs a package on Windows, the bin name from package.json is
passed to cmd.exe with no escaping. Characters like &, |, and spaces
allow a malicious package to inject arbitrary commands.

This patch escapes cmd.exe metacharacters (&, |, <, >, ^, (, ), @, !,
", \t) with ^ and rejects bin names containing characters that cannot
be safely neutralized: spaces and tabs (cmd.exe always splits on these
regardless of escaping), % (env var expansion precedes ^ processing),
and newlines/carriage returns (act as command separators).
@tufstraka
tufstraka force-pushed the fix/windows-shell-escape-libnpmexec branch from d58e1fb to 3926183 Compare June 15, 2026 05:42
@martinrrm

Copy link
Copy Markdown
Contributor

Thanks for reporting this issue, this is a true bug but we should fix this in promise-spawn package first and then a follow up PR in the cli, I have created a PR for this npm/promise-spawn#166

@martinrrm martinrrm closed this Sep 28, 2026
@martinrrm martinrrm self-assigned this Sep 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants