Keep env values off the bwrap command line
This commit is contained in:
@@ -16,6 +16,49 @@ fn printenv_inside(args: &[&str], vars: &[(&str, &str)], query: &[&str]) -> Stri
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
String::from_utf8_lossy(&output.stdout).into_owned()
|
||||
}
|
||||
#[test]
|
||||
fn user_env_value_never_reaches_the_command_line() {
|
||||
let stdout = dry_run_command_line(&["--env", "MY_SECRET=hunter2"], &[]);
|
||||
assert!(
|
||||
!stdout.contains("hunter2"),
|
||||
"user env value leaked into the bwrap command line: {stdout}"
|
||||
);
|
||||
}
|
||||
|
||||
fn dry_run_command_line(args: &[&str], vars: &[(&str, &str)]) -> String {
|
||||
let mut cmd = Sandbox::new(args);
|
||||
cmd.arg("--dry-run");
|
||||
for (k, v) in vars {
|
||||
cmd.env(k, v);
|
||||
}
|
||||
let output = cmd
|
||||
.args(["--", "true"])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
String::from_utf8_lossy(&output.stdout).into_owned()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kept_host_env_value_never_reaches_the_command_line() {
|
||||
let stdout = dry_run_command_line(&[], &[("TERM", "xterm-canary")]);
|
||||
assert!(
|
||||
!stdout.contains("xterm-canary"),
|
||||
"kept host env value leaked into the bwrap command line: {stdout}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passed_through_env_value_never_reaches_the_command_line() {
|
||||
let stdout = dry_run_command_line(
|
||||
&["--env", "PASSED_THROUGH"],
|
||||
&[("PASSED_THROUGH", "from-host-canary")],
|
||||
);
|
||||
assert!(
|
||||
!stdout.contains("from-host-canary"),
|
||||
"passed-through env value leaked into the bwrap command line: {stdout}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn whitelist_keeps_identity_and_terminal_vars() {
|
||||
let stdout = printenv_inside(
|
||||
|
||||
+41
-23
@@ -1,37 +1,55 @@
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn keepenv_emits_setenv_for_present_key() {
|
||||
let parent = vec![("XDG_RUNTIME_DIR".into(), "/run/user/1000".into())];
|
||||
let args = keepenv_args(&["XDG_RUNTIME_DIR".into()], &parent);
|
||||
assert_eq!(args, vec!["--setenv", "XDG_RUNTIME_DIR", "/run/user/1000"]);
|
||||
fn parent(pairs: &[(&str, &str)]) -> Vec<(String, String)> {
|
||||
pairs
|
||||
.iter()
|
||||
.map(|(k, v)| (k.to_string(), v.to_string()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keepenv_skips_absent_keys() {
|
||||
let parent = vec![("HOME".into(), "/home/me".into())];
|
||||
let args = keepenv_args(&["XDG_RUNTIME_DIR".into()], &parent);
|
||||
assert!(args.is_empty());
|
||||
fn copy_parent_env_retains_every_var() {
|
||||
let sandbox_env = copy_parent_env(&parent(&[("GH_TOKEN", "secret"), ("XDG_SEAT", "seat0")]));
|
||||
assert_eq!(sandbox_env.get("GH_TOKEN").unwrap(), "secret");
|
||||
assert_eq!(sandbox_env.get("XDG_SEAT").unwrap(), "seat0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keepenv_preserves_caller_key_order() {
|
||||
let parent = vec![
|
||||
("B".into(), "2".into()),
|
||||
("A".into(), "1".into()),
|
||||
("C".into(), "3".into()),
|
||||
];
|
||||
let args = keepenv_args(&["A".into(), "B".into(), "C".into()], &parent);
|
||||
fn whitelist_retains_allowed_var() {
|
||||
let sandbox_env = apply_whitelist(&parent(&[("TERM", "xterm")]));
|
||||
assert_eq!(sandbox_env.get("TERM").unwrap(), "xterm");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn whitelist_removes_unlisted_var() {
|
||||
let sandbox_env = apply_whitelist(&parent(&[("SOME_RANDOM_NOISE_VAR", "leak")]));
|
||||
assert!(sandbox_env.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blacklist_removes_secret_var() {
|
||||
let sandbox_env = apply_blacklist(&parent(&[("GH_TOKEN", "secret"), ("MY_NICE_VAR", "hello")]));
|
||||
assert!(!sandbox_env.contains_key("GH_TOKEN"));
|
||||
assert_eq!(sandbox_env.get("MY_NICE_VAR").unwrap(), "hello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blacklist_carves_out_vendor_prefix() {
|
||||
let sandbox_env = apply_blacklist(&parent(&[("ANTHROPIC_API_KEY", "key")]));
|
||||
assert_eq!(sandbox_env.get("ANTHROPIC_API_KEY").unwrap(), "key");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn find_parent_value_returns_present_value() {
|
||||
let parent_env = parent(&[("XDG_RUNTIME_DIR", "/run/user/1000")]);
|
||||
assert_eq!(
|
||||
args,
|
||||
vec![
|
||||
"--setenv", "A", "1", "--setenv", "B", "2", "--setenv", "C", "3"
|
||||
]
|
||||
find_parent_value(&parent_env, "XDG_RUNTIME_DIR").unwrap(),
|
||||
"/run/user/1000"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keepenv_empty_keys_yields_nothing() {
|
||||
let parent = vec![("A".into(), "1".into())];
|
||||
assert!(keepenv_args(&[], &parent).is_empty());
|
||||
fn find_parent_value_returns_none_for_absent_key() {
|
||||
let parent_env = parent(&[("HOME", "/home/me")]);
|
||||
assert!(find_parent_value(&parent_env, "XDG_RUNTIME_DIR").is_none());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user