diff --git a/e2e/sandbox/test_sandbox_task b/e2e/sandbox/test_sandbox_task index 4574b5f940..3435ce4514 100644 --- a/e2e/sandbox/test_sandbox_task +++ b/e2e/sandbox/test_sandbox_task @@ -6,9 +6,17 @@ cat >mise.toml <<'TOML' [tasks.env_test] run = "echo ${SANDBOX_TASK_SECRET:-empty}" deny_env = true + +[tasks.path_test] +run = 'echo "PATH=${PATH:-unset} HOME=${HOME:-unset}"' +deny_env = true TOML export SANDBOX_TASK_SECRET="should_not_see" # Task with deny_env should not see the var assert "mise run env_test" "empty" + +# deny_env should still let essential vars (PATH, HOME, ...) through +assert_not_contains "mise run path_test" "PATH=unset" +assert_not_contains "mise run path_test" "HOME=unset" diff --git a/src/cmd.rs b/src/cmd.rs index 257ed400b0..dcbe59d754 100644 --- a/src/cmd.rs +++ b/src/cmd.rs @@ -622,9 +622,18 @@ impl<'a> CmdLineRunner<'a> { #[cfg(target_os = "linux")] { - // On Linux, clear inherited env before pre_exec so child only sees filtered vars + // On Linux, clear inherited env before pre_exec so child only sees filtered vars. + // env_clear() also wipes envs explicitly set via .envs(), so save and restore them. if sandbox.effective_deny_env() { + let saved: Vec<(std::ffi::OsString, std::ffi::OsString)> = self + .cmd + .get_envs() + .filter_map(|(k, v)| v.map(|v| (k.to_os_string(), v.to_os_string()))) + .collect(); self.cmd.env_clear(); + for (k, v) in saved { + self.cmd.env(k, v); + } } // Use pre_exec to apply Landlock/seccomp in the child process // before it execs the target program. This avoids restricting the mise process.