Filter environment variables in both sandbox modes

Whitelist mode now clears the parent env and re-adds a small allowlist
(identity, terminal, locale, proxy, non-GUI XDG, vendor prefixes).
Blacklist mode strips cloud credentials, backup passphrases, dangling
socket pointers, and anything matching *_TOKEN, *_SECRET, *_PASSWORD,
*_PASSPHRASE, *_API_KEY, *_PRIVATE_KEY, *_CLIENT_SECRET; vendor prefix
carve-outs keep ANTHROPIC_API_KEY and friends.

Users can override via --setenv KEY=VALUE and --unsetenv KEY (and the
corresponding TOML keys), or opt out of the built-in policy entirely
with --no-env-filter.
This commit is contained in:
2026-04-08 09:22:11 +02:00
parent 12644ae31e
commit 25f0037aab
8 changed files with 638 additions and 5 deletions
+25
View File
@@ -3,6 +3,7 @@ use std::process::Command;
use crate::agents;
use crate::blacklist;
use crate::env;
use crate::seccomp;
use crate::{SandboxConfig, SandboxError, SandboxMode};
@@ -35,6 +36,9 @@ pub fn build_command(config: &SandboxConfig) -> Result<Command, SandboxError> {
add_ro_bind(&mut cmd, path)?;
}
add_env_policy(&mut cmd, config);
add_user_env_overrides(&mut cmd, config);
cmd.args(["--remount-ro", "/"]);
cmd.arg("--new-session");
cmd.arg("--die-with-parent");
@@ -55,6 +59,27 @@ pub fn build_command(config: &SandboxConfig) -> Result<Command, SandboxError> {
Ok(cmd)
}
fn add_env_policy(cmd: &mut Command, config: &SandboxConfig) {
if !config.env_filter {
return;
}
let parent_env: Vec<(String, String)> = std::env::vars().collect();
let args = match config.mode {
SandboxMode::Blacklist => env::blacklist_env_args(&parent_env),
SandboxMode::Whitelist => env::whitelist_env_args(&parent_env),
};
cmd.args(args);
}
fn add_user_env_overrides(cmd: &mut Command, config: &SandboxConfig) {
for (key, value) in &config.setenv {
cmd.arg("--setenv").arg(key).arg(value);
}
for key in &config.unsetenv {
cmd.arg("--unsetenv").arg(key);
}
}
fn apply_masks(cmd: &mut Command, masks: &[PathBuf]) {
for path in masks {
if path.is_file() {