From cccc5ed55f30c59cb41a59b5d34aaf5ae6f4c79e Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 3 Jun 2026 01:16:19 +0000 Subject: [PATCH] fix(shell): pass .NET/NuGet + Windows app-data env to exec_shell (#1857) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `exec_shell` runs with `env_clear()` plus a strict allowlist. On Windows there is no sandbox, so commands run directly — but the allowlist dropped `APPDATA`, `LOCALAPPDATA`, `ProgramData`, `ProgramFiles*`, and the `DOTNET_*` / `NUGET_*` variables that `dotnet restore` and NuGet rely on to locate their package cache, HTTP cache, and config. Restore therefore failed through the tool while working in the user's own shell, where the full environment is present. Add the .NET/NuGet and Windows app-data path variables to the shell allowlist (`DOTNET_*` via prefix, like `LC_*`). NuGet credential vars (`NuGetPackageSourceCredentials_*`) still fall outside the allowlist and are not exported. Also benefits npm/pip on Windows, which use the same paths. https://claude.ai/code/session_01MQrnh6wHfrEYN5BBdMarC1 --- crates/tui/src/child_env.rs | 53 +++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/crates/tui/src/child_env.rs b/crates/tui/src/child_env.rs index 82dc848f..21add459 100644 --- a/crates/tui/src/child_env.rs +++ b/crates/tui/src/child_env.rs @@ -169,6 +169,23 @@ fn is_allowed_parent_env_key(key: &OsStr) -> bool { | "EXTENSIONSDKDIR" | "DEVENVDIR" | "VISUALSTUDIOVERSION" + // Windows app-data + .NET/NuGet paths. `dotnet restore` (and npm, + // pip, etc.) resolve their package caches, HTTP cache, and config + // under %APPDATA% / %LOCALAPPDATA% / %ProgramData% / %ProgramFiles%. + // The sanitized child env dropped these, so restore failed through + // `exec_shell` even though it worked in the user's own shell, where + // the full environment is present (#1857). `DOTNET_*` (below) covers + // DOTNET_ROOT and the CLI flags. + | "APPDATA" + | "LOCALAPPDATA" + | "PROGRAMDATA" + | "ALLUSERSPROFILE" + | "PROGRAMFILES" + | "PROGRAMFILES(X86)" + | "PROGRAMW6432" + | "PROCESSOR_ARCHITECTURE" + | "NUGET_PACKAGES" + | "NUGET_HTTP_CACHE_PATH" // Standard proxy variables are needed by shell tasks in // corporate and WSL environments where direct internet egress is // blocked. They intentionally exclude token/API-key-shaped vars. @@ -178,6 +195,10 @@ fn is_allowed_parent_env_key(key: &OsStr) -> bool { | "ALL_PROXY" | "FTP_PROXY" ) || normalized.starts_with("LC_") + // .NET CLI / SDK configuration (DOTNET_ROOT, DOTNET_CLI_*, + // DOTNET_NOLOGO, DOTNET_CLI_TELEMETRY_OPTOUT, …). Paths and flags + // only — no secret-shaped values (#1857). + || normalized.starts_with("DOTNET_") } /// Allowlist for MCP stdio launches. Strict superset of @@ -354,6 +375,38 @@ mod tests { } } + #[test] + fn child_env_allowlist_includes_dotnet_and_windows_appdata_keys() { + // #1857: dotnet restore / NuGet need these to find caches and config. + for key in [ + "APPDATA", + "LOCALAPPDATA", + "PROGRAMDATA", + "ALLUSERSPROFILE", + "PROGRAMFILES", + "PROGRAMFILES(X86)", + "PROGRAMW6432", + "PROCESSOR_ARCHITECTURE", + "NUGET_PACKAGES", + "DOTNET_ROOT", + "DOTNET_CLI_TELEMETRY_OPTOUT", + "DOTNET_NOLOGO", + // Case-insensitive: the real Windows var is `ProgramFiles`. + "ProgramFiles", + "dotnet_root", + ] { + assert!( + is_allowed_parent_env_key(OsStr::new(key)), + "child env allowlist should include {key}" + ); + } + // Guard: NuGet credential env vars must still be dropped. + assert!( + !is_allowed_parent_env_key(OsStr::new("NuGetPackageSourceCredentials_feed")), + "NuGet credential vars must not be exported to child processes" + ); + } + #[test] fn mcp_env_allowlist_includes_python_bootstrap_keys() { for key in [