Skip to content

Windows: shell-language ctx_execute/ctx_batch_execute scripts run with stripped PATH (/usr/bin:/bin only), breaking gh/other Windows-installed CLIs and git credential-helper #1217

Description

@fl4pj4ck

Summary

On Windows, shell-language ctx_execute/ctx_batch_execute calls run their command in a Git Bash (MSYS) process whose PATH is stripped down to /usr/bin:/bin — it does not carry the parent/inherited PATH. This breaks resolution of any Windows-installed CLI that isn't already inside MSYS's own /usr/bin (e.g. GitHub CLI gh, and by extension any git credential helper configured to shell out to gh auth git-credential, which additionally fails because the sandboxed process also has no /dev/tty).

Root cause

build/executor.js:

/** Pure helper — exported for unit testing. Restores parent PATH after shell startup. */
export function buildShellScriptContent(code, inheritedPath, platform) {
    if (platform === "win32" || !inheritedPath)
        return code;
    return `export PATH=${quoteForPosixShell(inheritedPath)}\n${code}`;
}

and its call site:

writeFileSync(fp, buildShellScriptContent(shellCode, process.env.PATH, process.platform), { encoding: "utf-8", mode: 0o700 });

This function explicitly restores the parent-inherited PATH into the generated script only on non-Windows (if (platform === "win32" ...) return code — the win32 branch is a no-op passthrough). #buildSafeEnv() does correctly propagate the real process.env.PATH into the child process's env, and does prepend Git's own usr/bin/bin, but the spawned Git Bash process does not appear to actually use that inherited PATH for command resolution inside the script — it starts with MSYS's own minimal default (/usr/bin:/bin), same shape as a fresh login shell that hasn't sourced /etc/profile. Because the Windows branch of buildShellScriptContent never injects an export PATH=... line the way the POSIX branch does, there is nothing to override that MSYS default inside the script itself.

Reproduction

ctx_batch_execute(commands: [
  { label: "shell identity", command: "echo PATH=$PATH" },
  { label: "gh direct", command: "'/c/Program Files/GitHub CLI/gh' --version" },
  { label: "gh via PATH", command: "which gh; gh --version" }
])

Result:462

PATH=/usr/bin:/bin
gh version 2.101.0 ...      # works via absolute path
which: no gh in (/usr/bin:/bin)
... gh: command not found

Yet in a normal (non-sandboxed) Git Bash / the host's own Bash tool, gh resolves fine because the real Windows PATH (which includes C:\Program Files\GitHub CLI) is on it.

Secondary, compounding failure: any git command that needs to shell out to a credential helper (e.g. credential.https://github.com.helper=!gh auth git-credential) additionally fails inside the sandbox with:

error: failed to execute prompt script (exit code 1)
fatal: could not read Password for '...': No such file or directory

because the sandbox process also has no /dev/tty for an interactive credential prompt fallback — so even once gh is resolvable, a bare git pull/push that needs credential-helper mediation still cannot succeed non-interactively inside the sandbox the way it does in a normal shell session.

Impact

  • Every Windows user relying on gh (or any other Windows-installed, non-MSYS CLI) from inside ctx_execute/ctx_batch_execute shell scripts hits a misleading command not found, despite the same binary resolving fine via which/direct invocation in a normal shell, and despite #buildSafeEnv() clearly intending to pass the real PATH through.
  • This is silently inconsistent with the POSIX behavior, where the same class of tool (anything installed outside the sandboxed shell's own minimal PATH) resolves correctly because buildShellScriptContent restores it.

Suggested fix

Make the Windows branch of buildShellScriptContent do the POSIX-equivalent restoration — i.e. emit a PATH="<inheritedPath>" (or Windows-appropriate export PATH=... after MSYS path conversion) line at the top of the generated script instead of returning code unchanged, so Git Bash's own default MSYS PATH doesn't shadow the inherited environment. At minimum, document the current behavior (and the /dev/tty absence for git credential-helper prompts) as a known limitation, since right now it presents as a plain "command not found"/"could not read Password" with no pointer to the real cause.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions