From fe90f6fed96feaa3534963d81c6519d7287778d7 Mon Sep 17 00:00:00 2001 From: Viet Tran Date: Sun, 19 Apr 2026 14:23:26 +0700 Subject: [PATCH] fix(tools): clean up Windows exec merge leftovers + expand credentialed env (#963) - shell.go: remove commented-out git conflict markers; restore load-bearing rationale for plain exec.Command vs CommandContext (process-group kill). - credentialed_exec.go: pass through SYSTEMROOT, TEMP, APPDATA and other Windows runtime vars that native CLIs (gh, az, aws, npm) require. Missing SYSTEMROOT breaks networking/registry APIs in most Win32 programs. Switch to USERNAME (Windows) vs USER (POSIX) per platform convention. --- internal/tools/credentialed_exec.go | 42 ++++++++++++++++++++--------- internal/tools/shell.go | 12 +++------ 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/internal/tools/credentialed_exec.go b/internal/tools/credentialed_exec.go index 6d28eb9b..55fe295c 100644 --- a/internal/tools/credentialed_exec.go +++ b/internal/tools/credentialed_exec.go @@ -496,25 +496,41 @@ func (t *ExecTool) executeCredentialedSandbox(ctx context.Context, absPath strin // buildCredentialedEnv creates a minimal environment with injected credentials. // Inherits PATH and HOME from parent process, adds credential env vars. +// On Windows, also passes through SYSTEMROOT / TEMP / APPDATA / etc. — these +// are not secrets and many native CLIs (gh, az, aws, npm) require them to run. func buildCredentialedEnv(envMap map[string]string) []string { - var pathDefault string - var homeDefault string + var env []string if runtime.GOOS == "windows" { - pathDefault = "C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem" - homeDefault = os.Getenv("USERPROFILE") + pathDefault := "C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem" + homeDefault := os.Getenv("USERPROFILE") if homeDefault == "" { homeDefault = "C:\\Users\\Default" } + env = []string{ + "PATH=" + getenvDefault("PATH", pathDefault), + "HOME=" + getenvDefault("HOME", homeDefault), + "LANG=" + getenvDefault("LANG", "en_US.UTF-8"), + "USERNAME=" + getenvDefault("USERNAME", "goclaw"), + } + // Pass through Windows runtime vars that native tools expect. + // Missing SYSTEMROOT breaks networking/registry in most Win32 programs. + for _, k := range []string{ + "SYSTEMROOT", "SYSTEMDRIVE", "WINDIR", "COMSPEC", "PATHEXT", + "TEMP", "TMP", "USERPROFILE", "APPDATA", "LOCALAPPDATA", + "PROGRAMFILES", "PROGRAMFILES(X86)", "PROGRAMDATA", + "HOMEDRIVE", "HOMEPATH", "COMPUTERNAME", + } { + if v := os.Getenv(k); v != "" { + env = append(env, k+"="+v) + } + } } else { - pathDefault = "/usr/local/bin:/usr/bin:/bin" - homeDefault = "/tmp" - } - - env := []string{ - "PATH=" + getenvDefault("PATH", pathDefault), - "HOME=" + getenvDefault("HOME", homeDefault), - "LANG=" + getenvDefault("LANG", "en_US.UTF-8"), - "USER=" + getenvDefault("USER", "goclaw"), + env = []string{ + "PATH=" + getenvDefault("PATH", "/usr/local/bin:/usr/bin:/bin"), + "HOME=" + getenvDefault("HOME", "/tmp"), + "LANG=" + getenvDefault("LANG", "en_US.UTF-8"), + "USER=" + getenvDefault("USER", "goclaw"), + } } for k, v := range envMap { env = append(env, k+"="+v) diff --git a/internal/tools/shell.go b/internal/tools/shell.go index 78e0b526..563661ca 100644 --- a/internal/tools/shell.go +++ b/internal/tools/shell.go @@ -361,19 +361,15 @@ func (t *ExecTool) executeOnHost(ctx context.Context, command, cwd string) *Resu ctx, cancel := context.WithTimeout(ctx, t.timeout) defer cancel() - //<<<<<<< HEAD + // Use plain exec.Command (not CommandContext) so we control the kill sequence. + // CommandContext would SIGKILL only the direct child, leaving forked grandchildren alive. + // Route through the platform shell: cmd.exe on Windows, sh on POSIX. var cmd *exec.Cmd if runtime.GOOS == "windows" { - cmd = exec.Command("cmd", "/c", command) + cmd = exec.Command("cmd", "/C", command) } else { cmd = exec.Command("sh", "-c", command) } - - //======= - // Use plain exec.Command (not CommandContext) so we control the kill sequence. - // CommandContext would SIGKILL only the direct child, leaving forked grandchildren alive. - // cmd := exec.Command("sh", "-c", command) - //>>>>>>> dev cmd.Dir = cwd // Scrub credential env vars so fall-through exec cannot exfiltrate