From af9b51925421ce98e960a0ebd5d5105f02fc4d94 Mon Sep 17 00:00:00 2001 From: Agung Subastian Date: Thu, 14 May 2026 19:40:08 +0700 Subject: [PATCH] security: isolate agent shell commands from parent environment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prevent environment variable leakage to agent-spawned processes. Previously, run_command() inherited all env vars from the desktop app process (API keys, HOME, credentials). Now env_clear() wipes the slate and only PATH, LANG, and LC_ALL are passed through — enough for shell tools to find binaries and handle encoding correctly. Also adds test_run_command_env_isolation to assert that secrets set in the parent process are not visible to agent commands. --- src-tauri/src/tools/executor.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src-tauri/src/tools/executor.rs b/src-tauri/src/tools/executor.rs index 10cae51..b8eba4a 100644 --- a/src-tauri/src/tools/executor.rs +++ b/src-tauri/src/tools/executor.rs @@ -301,8 +301,15 @@ impl ToolExecutor { sh_process }; + let safe_path = std::env::var("PATH") + .unwrap_or_else(|_| "/usr/local/bin:/usr/bin:/bin".to_string()); + command .current_dir(&self.sandbox) + .env_clear() + .env("PATH", safe_path) + .env("LANG", std::env::var("LANG").unwrap_or_else(|_| "en_US.UTF-8".to_string())) + .env("LC_ALL", std::env::var("LC_ALL").unwrap_or_else(|_| "en_US.UTF-8".to_string())) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .kill_on_drop(true); @@ -794,6 +801,30 @@ mod tests { cleanup("run_cmd_invalid"); } + #[tokio::test] + async fn test_run_command_env_isolation() { + let sandbox_path = with_sandbox("run_cmd_env_isolation"); + + std::env::set_var("ENOWX_SECRET_TEST_VAR", "super_secret_value"); + + let executor = ToolExecutor::new(sandbox_path); + + let call = ToolCall { + tool: ToolName::RunCommand, + input: serde_json::json!({ "command": "echo ${ENOWX_SECRET_TEST_VAR:-empty}" }), + }; + let result = executor.execute(call).await; + assert!(!result.is_error, "command should succeed: {}", result.output); + assert!( + !result.output.contains("super_secret_value"), + "parent env vars must not leak into agent commands, got: {}", + result.output + ); + + std::env::remove_var("ENOWX_SECRET_TEST_VAR"); + cleanup("run_cmd_env_isolation"); + } + #[tokio::test] async fn test_run_command_timeout() { let sandbox_path = with_sandbox("run_cmd_timeout");