Skip to content

fix: add shell: true on Windows for execFileSync calls#742

Draft
evil1morty wants to merge 2 commits intocloudflare:mainfrom
evil1morty:fix/windows-execfilesync-enoent
Draft

fix: add shell: true on Windows for execFileSync calls#742
evil1morty wants to merge 2 commits intocloudflare:mainfrom
evil1morty:fix/windows-execfilesync-enoent

Conversation

@evil1morty
Copy link
Copy Markdown

@evil1morty evil1morty commented Apr 1, 2026

Summary

vinext deploy, vinext build, and vinext lint fail on Windows with spawnSync ENOENT because execFileSync cannot execute .bin/wrangler (a shell script) or package manager commands like npm/npx (.cmd wrappers) without enabling the shell.

Error: spawnSync C:\Users\...\node_modules\.bin\wrangler ENOENT
    at runWranglerDeploy (deploy.js)

Fix

Add shell: process.platform === "win32" to all execFileSync options in deploy.ts and cli.ts. This only enables the shell on Windows where it's required, preserving the no-shell-injection guarantee on other platforms.

Affected call sites:

  • runWranglerDeploy() in deploy.ts — wrangler binary
  • installDeps() in deploy.ts — package manager
  • React upgrade in deploy() — package manager
  • React upgrade in buildApp() in cli.ts — package manager
  • lint() in cli.ts — npx eslint/oxlint

Test plan

  • Verified npx wrangler deploy works as workaround on Windows 11
  • vinext deploy on Windows with this patch
  • vinext build on Windows (react upgrade path)
  • vinext lint on Windows
  • Existing behavior on macOS/Linux unaffected (shell option is false)

Environment: Windows 11, Node 22, wrangler 4.79.0

On Windows, `execFileSync` cannot execute `.bin/wrangler` (a shell
script) or package manager commands like `npm`/`npx` (which are `.cmd`
wrappers) without enabling the shell. This causes `ENOENT` errors
during `vinext deploy`, `vinext build`, and `vinext lint`.

Fix by adding `shell: process.platform === "win32"` to all
`execFileSync` options. This only enables the shell on Windows where
it's required, preserving the no-shell-injection guarantee on other
platforms.

Fixes `spawnSync ENOENT` on Windows for:
- `runWranglerDeploy` (deploy.ts)
- `installDeps` (deploy.ts)
- React upgrade in `deploy()` (deploy.ts)
- React upgrade in `buildApp()` (cli.ts)
- Lint commands in `lint()` (cli.ts)
@james-elicx
Copy link
Copy Markdown
Collaborator

/bigbonk review

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 1, 2026

Open in StackBlitz

npm i https://pkg.pr.new/vinext@742

commit: ae574e6

Copy link
Copy Markdown
Contributor

@ask-bonk ask-bonk bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good fix. The ENOENT on Windows for .cmd wrappers and shell scripts in .bin/ is a well-known Node.js footgun, and the shell: process.platform === "win32" pattern is the standard fix.

Verified:

  • All 7 execFileSync call sites in cli.ts and deploy.ts are covered
  • Arguments are still passed as arrays (not interpolated into a string), so shell injection risk on Windows is minimal — the shell is only used for .cmd shim resolution, not for building command strings
  • init.ts already uses shell: true unconditionally (line 262), so this PR is actually more conservative by limiting it to win32
  • No type issues — ExecFileSyncOptions.shell accepts boolean | string

One non-blocking nit: the existing comment in runWranglerDeploy at line 1228 says "Use execFileSync to avoid shell injection — args are passed as an array, never interpolated into a shell command string." — this is slightly misleading now that shell: true is enabled on Windows. The new comment at lines 1214-1216 explains the tradeoff well, but the original comment at 1228 could be updated to say something like: "execFileSync passes args as an array, avoiding shell injection on Unix. On Windows, shell: true is required for .cmd wrappers but the array form still prevents trivial injection."

Not a blocker — LGTM.

// On Windows, .bin/wrangler is a .cmd wrapper; execFileSync can't run
// it without a shell. Enabling shell only on win32 keeps the
// no-shell-injection guarantee on other platforms.
shell: process.platform === "win32",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice — the comment here is clear and well-scoped. Just noting that the other comment a few lines below (line 1228: "Use execFileSync to avoid shell injection") is now slightly inaccurate on Windows. Consider updating that one too to acknowledge the platform split, since both comments describe the same call site.

@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk bot commented Apr 1, 2026

Review submitted successfully. Here's a summary of what I reviewed:

PR #742: fix: add shell: true on Windows for execFileSync calls -- Approved

The fix is correct and well-scoped. On Windows, execFileSync cannot execute .cmd wrappers (like wrangler, npm, npx) without shell: true because they're shell scripts, not native executables. The PR adds shell: process.platform === "win32" to all 7 affected execFileSync call sites in cli.ts and deploy.ts.

Key observations:

  • All call sites are covered
  • The approach is more conservative than init.ts which already uses shell: true unconditionally
  • Arguments stay as arrays even with shell enabled, minimizing injection risk
  • No type issues with the boolean value
  • One non-blocking nit about updating a now-slightly-misleading comment in runWranglerDeploy

github run

@james-elicx james-elicx marked this pull request as draft April 2, 2026 10:08
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