Skip to content

fix: resolve executables against PATH not cwd on Windows; reject %VAR% paths - #1905

Open
iankhou wants to merge 3 commits into
mainfrom
iankhou-windows-exec-resolution
Open

fix: resolve executables against PATH not cwd on Windows; reject %VAR% paths#1905
iankhou wants to merge 3 commits into
mainfrom
iankhou-windows-exec-resolution

Conversation

@iankhou

@iankhou iankhou commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Windows integ test verification run: https://github.com/aws/aws-cdk-cli/actions/runs/33107303906

Follow-up to #1763 / #1849. Two Windows-specific hardening fixes to the shared subprocess module, and a test that exercises the .cmd path.

Summary

  1. resolveExecutable() — on Windows a bare program name spawned without a shell is searched for in the working directory before PATH, so a file planted in a handed-over cloud assembly (e.g. docker.bat) could shadow the real binary. run()/runSync() now resolve the executable to an absolute PATH hit up front and refuse a name that is not on PATH rather than let the cwd satisfy it. POSIX is unchanged (execvp already searches PATH only); an explicit path (absolute or containing a separator) is honored verbatim.

  2. quoteShellPart() (toolkit-lib) — cmd.exe expands %VAR% even inside double quotes, and a cmd /c command line cannot reliably escape a percent (doubling only works in batch files). A discovered path carrying a %...% reference is now refused loudly instead of being silently rewritten (an env var spliced into the path).

  3. A Windows-only test that runs a .cmd shim with hostile arguments — the one path where cross-spawn's cmd.exe escaping is exercised (a plain .exe, as in every existing test, never is).

Testing

  • Unit tests for resolveExecutable
  • The .cmd hostile-args test run on Windows integ workflow.
  • Test for %VAR% in environment.test.ts

Checklist

  • Unit tests added/updated
  • Integration tests — Windows integ run required (pending)
  • No manual edits to generated files

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license.

@github-actions

Copy link
Copy Markdown
Contributor

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

…s; reject %VAR% paths

Two Windows-specific hardening fixes to the shared subprocess module, plus a
test that actually exercises the .cmd path.

1. resolveExecutable(): on Windows a bare program name spawned without a shell
   is searched for in the cwd *before* PATH, so a file planted in a handed-over
   cloud assembly (e.g. docker.bat) could shadow the real binary. run()/runSync()
   now resolve the executable to an absolute PATH hit up front and refuse a name
   that is not on PATH rather than let the cwd satisfy it. POSIX is unchanged
   (execvp already searches PATH only); explicit paths are honored verbatim.

2. quoteShellPart() (toolkit-lib): cmd.exe expands %VAR% even inside double
   quotes and a `cmd /c` line cannot reliably escape a percent, so a discovered
   path carrying a %...% reference is now refused loudly instead of being
   silently rewritten.

3. Adds a Windows-only test that runs a real .cmd shim with hostile arguments —
   the one path where cross-spawn's cmd.exe escaping is exercised (a plain .exe
   never is).

NOTE: items 1 and 3 change Windows spawn behavior and must be validated by the
Windows integ tests; the resolveExecutable logic is unit-tested cross-platform
via a `platform` parameter.
@iankhou
iankhou force-pushed the iankhou-windows-exec-resolution branch from b334c08 to 9f440cb Compare August 26, 2026 20:27
@iankhou
iankhou deployed to no-approval August 26, 2026 20:28 — with GitHub Actions Active
@iankhou iankhou changed the title fix(subprocess): resolve executables against PATH not cwd on Windows; reject %VAR% paths fix(subprocess): resolve executables against PATH not cwd on Windows; reject bad paths Aug 28, 2026
@iankhou
iankhou marked this pull request as ready for review August 28, 2026 15:19
@iankhou iankhou changed the title fix(subprocess): resolve executables against PATH not cwd on Windows; reject bad paths fix: resolve executables against PATH not cwd on Windows; reject %VAR% paths Aug 28, 2026
@iankhou
iankhou deployed to no-approval August 28, 2026 15:21 — with GitHub Actions Active

Copilot AI left a comment

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.

Pull request overview

Hardens Windows subprocess execution against executable shadowing and unsafe %VAR% path expansion.

Changes:

  • Resolves bare Windows executables through PATH before spawning.
  • Rejects shell-bound Windows paths containing %...%.
  • Adds Windows subprocess and unit coverage.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
packages/@aws-cdk/private-tools/lib/subprocess/index.ts Adds executable resolution and ENOENT handling.
packages/@aws-cdk/private-tools/test/subprocess/subprocess.test.ts Tests PATH resolution and .cmd escaping.
packages/@aws-cdk/toolkit-lib/lib/api/cloud-assembly/environment.ts Rejects unsafe percent-containing paths.
packages/@aws-cdk/toolkit-lib/test/api/cloud-assembly/environment.test.ts Tests percent-path rejection.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

.map((e) => e.trim())
.filter(Boolean);
const lower = command.toLowerCase();
return configured.some((e) => lower.endsWith(e.toLowerCase())) ? [''] : configured;

for (const dir of dirs) {
for (const ext of exts) {
const candidate = path.join(dir, command + ext);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants