Organize test code better
This commit is contained in:
@@ -0,0 +1,359 @@
|
||||
use crate::common::*;
|
||||
|
||||
fn printenv_inside(args: &[&str], vars: &[(&str, &str)], query: &[&str]) -> String {
|
||||
let script = query
|
||||
.iter()
|
||||
.map(|v| format!("printenv {v} || echo MISSING:{v}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join("; ");
|
||||
let mut cmd = sandbox(args);
|
||||
for (k, v) in vars {
|
||||
cmd.env(k, v);
|
||||
}
|
||||
let output = cmd
|
||||
.args(["--", "bash", "-c", &script])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
String::from_utf8_lossy(&output.stdout).into_owned()
|
||||
}
|
||||
#[test]
|
||||
fn whitelist_keeps_identity_and_terminal_vars() {
|
||||
let stdout = printenv_inside(
|
||||
&["--whitelist"],
|
||||
&[("TERM", "xterm-test"), ("LANG", "C.UTF-8")],
|
||||
&["HOME", "PATH", "TERM", "LANG"],
|
||||
);
|
||||
assert!(!stdout.contains("MISSING:HOME"), "HOME stripped: {stdout}");
|
||||
assert!(!stdout.contains("MISSING:PATH"), "PATH stripped: {stdout}");
|
||||
assert!(stdout.contains("xterm-test"), "TERM stripped: {stdout}");
|
||||
assert!(stdout.contains("C.UTF-8"), "LANG stripped: {stdout}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn whitelist_strips_arbitrary_host_var() {
|
||||
let stdout = printenv_inside(
|
||||
&["--whitelist"],
|
||||
&[("SOME_RANDOM_NOISE_VAR", "leak")],
|
||||
&["SOME_RANDOM_NOISE_VAR"],
|
||||
);
|
||||
assert!(
|
||||
stdout.contains("MISSING:SOME_RANDOM_NOISE_VAR"),
|
||||
"expected arbitrary host var to be stripped, got: {stdout}"
|
||||
);
|
||||
assert!(!stdout.contains("leak"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn whitelist_keeps_vendor_prefixes() {
|
||||
let stdout = printenv_inside(
|
||||
&["--whitelist"],
|
||||
&[
|
||||
("CLAUDE_FOO", "claude-val"),
|
||||
("ANTHROPIC_MODEL", "anthropic-val"),
|
||||
("OPENAI_API_KEY", "openai-val"),
|
||||
("CODEX_FOO", "codex-val"),
|
||||
("GEMINI_API_KEY", "gemini-val"),
|
||||
("OTEL_SERVICE_NAME", "otel-val"),
|
||||
],
|
||||
&[
|
||||
"CLAUDE_FOO",
|
||||
"ANTHROPIC_MODEL",
|
||||
"OPENAI_API_KEY",
|
||||
"CODEX_FOO",
|
||||
"GEMINI_API_KEY",
|
||||
"OTEL_SERVICE_NAME",
|
||||
],
|
||||
);
|
||||
for expected in [
|
||||
"claude-val",
|
||||
"anthropic-val",
|
||||
"openai-val",
|
||||
"codex-val",
|
||||
"gemini-val",
|
||||
"otel-val",
|
||||
] {
|
||||
assert!(
|
||||
stdout.contains(expected),
|
||||
"expected {expected} in output, got: {stdout}"
|
||||
);
|
||||
}
|
||||
assert!(!stdout.contains("MISSING:"), "unexpected strip: {stdout}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn whitelist_keeps_lc_prefix() {
|
||||
let stdout = printenv_inside(
|
||||
&["--whitelist"],
|
||||
&[("LC_TIME", "en_US.UTF-8")],
|
||||
&["LC_TIME"],
|
||||
);
|
||||
assert!(stdout.contains("en_US.UTF-8"), "LC_TIME missing: {stdout}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn whitelist_keeps_non_gui_xdg_vars() {
|
||||
let stdout = printenv_inside(
|
||||
&["--whitelist"],
|
||||
&[
|
||||
("XDG_CONFIG_HOME", "/cfg"),
|
||||
("XDG_DATA_HOME", "/data"),
|
||||
("XDG_CACHE_HOME", "/cache"),
|
||||
("XDG_STATE_HOME", "/state"),
|
||||
("XDG_CONFIG_DIRS", "/etc/xdg"),
|
||||
("XDG_DATA_DIRS", "/usr/share"),
|
||||
],
|
||||
&[
|
||||
"XDG_CONFIG_HOME",
|
||||
"XDG_DATA_HOME",
|
||||
"XDG_CACHE_HOME",
|
||||
"XDG_STATE_HOME",
|
||||
"XDG_CONFIG_DIRS",
|
||||
"XDG_DATA_DIRS",
|
||||
],
|
||||
);
|
||||
assert!(
|
||||
!stdout.contains("MISSING:"),
|
||||
"XDG non-GUI stripped: {stdout}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn whitelist_strips_gui_xdg_vars() {
|
||||
let stdout = printenv_inside(
|
||||
&["--whitelist"],
|
||||
&[
|
||||
("XDG_RUNTIME_DIR", "/run/user/1000"),
|
||||
("XDG_SESSION_ID", "1"),
|
||||
("XDG_CURRENT_DESKTOP", "KDE"),
|
||||
("XDG_SEAT", "seat0"),
|
||||
],
|
||||
&[
|
||||
"XDG_RUNTIME_DIR",
|
||||
"XDG_SESSION_ID",
|
||||
"XDG_CURRENT_DESKTOP",
|
||||
"XDG_SEAT",
|
||||
],
|
||||
);
|
||||
for var in [
|
||||
"XDG_RUNTIME_DIR",
|
||||
"XDG_SESSION_ID",
|
||||
"XDG_CURRENT_DESKTOP",
|
||||
"XDG_SEAT",
|
||||
] {
|
||||
assert!(
|
||||
stdout.contains(&format!("MISSING:{var}")),
|
||||
"expected {var} stripped, got: {stdout}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn whitelist_strips_dbus_vars() {
|
||||
let stdout = printenv_inside(
|
||||
&["--whitelist"],
|
||||
&[
|
||||
("DBUS_SESSION_BUS_ADDRESS", "unix:path=/foo"),
|
||||
("DBUS_SYSTEM_BUS_ADDRESS", "unix:path=/bar"),
|
||||
],
|
||||
&["DBUS_SESSION_BUS_ADDRESS", "DBUS_SYSTEM_BUS_ADDRESS"],
|
||||
);
|
||||
assert!(
|
||||
stdout.contains("MISSING:DBUS_SESSION_BUS_ADDRESS"),
|
||||
"expected DBUS_SESSION stripped: {stdout}"
|
||||
);
|
||||
assert!(
|
||||
stdout.contains("MISSING:DBUS_SYSTEM_BUS_ADDRESS"),
|
||||
"expected DBUS_SYSTEM stripped: {stdout}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn whitelist_env_sets_user_var() {
|
||||
let stdout = printenv_inside(
|
||||
&["--whitelist", "--env", "USER_INJECTED=forced"],
|
||||
&[],
|
||||
&["USER_INJECTED"],
|
||||
);
|
||||
assert!(stdout.contains("forced"), "env not applied: {stdout}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn whitelist_env_keep_passes_through_host_var() {
|
||||
let stdout = printenv_inside(
|
||||
&["--whitelist", "--env", "PASSED_THROUGH"],
|
||||
&[("PASSED_THROUGH", "from-host")],
|
||||
&["PASSED_THROUGH"],
|
||||
);
|
||||
assert!(
|
||||
stdout.contains("from-host"),
|
||||
"expected --env KEY to pass host value through: {stdout}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn whitelist_env_keep_absent_host_var_is_skipped() {
|
||||
let stdout = printenv_inside(
|
||||
&["--whitelist", "--env", "NEVER_SET_ON_HOST"],
|
||||
&[],
|
||||
&["NEVER_SET_ON_HOST"],
|
||||
);
|
||||
assert!(
|
||||
stdout.contains("MISSING:NEVER_SET_ON_HOST"),
|
||||
"expected absent keep-var to remain unset: {stdout}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn whitelist_unsetenv_overrides_kept_var() {
|
||||
let stdout = printenv_inside(
|
||||
&["--whitelist", "--unsetenv", "TERM"],
|
||||
&[("TERM", "xterm-test")],
|
||||
&["TERM"],
|
||||
);
|
||||
assert!(
|
||||
stdout.contains("MISSING:TERM"),
|
||||
"expected --unsetenv to strip kept var: {stdout}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blacklist_drops_token_and_secret_vars() {
|
||||
let stdout = printenv_inside(
|
||||
&[],
|
||||
&[
|
||||
("GH_TOKEN", "gh-secret"),
|
||||
("AWS_SECRET_ACCESS_KEY", "aws-secret"),
|
||||
("MY_PASSWORD", "pw"),
|
||||
("FOO_API_KEY", "fookey"),
|
||||
],
|
||||
&[
|
||||
"GH_TOKEN",
|
||||
"AWS_SECRET_ACCESS_KEY",
|
||||
"MY_PASSWORD",
|
||||
"FOO_API_KEY",
|
||||
],
|
||||
);
|
||||
for var in [
|
||||
"GH_TOKEN",
|
||||
"AWS_SECRET_ACCESS_KEY",
|
||||
"MY_PASSWORD",
|
||||
"FOO_API_KEY",
|
||||
] {
|
||||
assert!(
|
||||
stdout.contains(&format!("MISSING:{var}")),
|
||||
"expected {var} stripped in blacklist mode, got: {stdout}"
|
||||
);
|
||||
}
|
||||
for leaked in ["gh-secret", "aws-secret", "pw", "fookey"] {
|
||||
assert!(!stdout.contains(leaked), "{leaked} leaked: {stdout}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blacklist_carves_out_vendor_api_keys() {
|
||||
let stdout = printenv_inside(
|
||||
&[],
|
||||
&[
|
||||
("ANTHROPIC_API_KEY", "anthropic-key"),
|
||||
("OPENAI_API_KEY", "openai-key"),
|
||||
("GEMINI_API_KEY", "gemini-key"),
|
||||
],
|
||||
&["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GEMINI_API_KEY"],
|
||||
);
|
||||
for expected in ["anthropic-key", "openai-key", "gemini-key"] {
|
||||
assert!(
|
||||
stdout.contains(expected),
|
||||
"expected {expected} to survive carve-out, got: {stdout}"
|
||||
);
|
||||
}
|
||||
assert!(!stdout.contains("MISSING:"), "carve-out failed: {stdout}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blacklist_suffix_match_does_not_catch_substring() {
|
||||
let stdout = printenv_inside(
|
||||
&[],
|
||||
&[
|
||||
("TOKENIZER_PATH", "/opt/tok"),
|
||||
("MY_TOKEN_HOLDER", "holder"),
|
||||
],
|
||||
&["TOKENIZER_PATH", "MY_TOKEN_HOLDER"],
|
||||
);
|
||||
assert!(
|
||||
stdout.contains("/opt/tok"),
|
||||
"TOKENIZER_PATH stripped: {stdout}"
|
||||
);
|
||||
assert!(
|
||||
stdout.contains("holder"),
|
||||
"MY_TOKEN_HOLDER stripped: {stdout}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blacklist_keeps_unrelated_host_var() {
|
||||
let stdout = printenv_inside(&[], &[("MY_NICE_VAR", "hello")], &["MY_NICE_VAR"]);
|
||||
assert!(stdout.contains("hello"), "MY_NICE_VAR stripped: {stdout}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blacklist_keeps_dbus_vars() {
|
||||
let stdout = printenv_inside(
|
||||
&[],
|
||||
&[
|
||||
("DBUS_SESSION_BUS_ADDRESS", "unix:path=/tmp/fake"),
|
||||
("DBUS_SYSTEM_BUS_ADDRESS", "unix:path=/tmp/fake-system"),
|
||||
],
|
||||
&["DBUS_SESSION_BUS_ADDRESS", "DBUS_SYSTEM_BUS_ADDRESS"],
|
||||
);
|
||||
assert!(stdout.contains("unix:path=/tmp/fake"));
|
||||
assert!(stdout.contains("unix:path=/tmp/fake-system"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_env_filter_whitelist_keeps_arbitrary_host_var() {
|
||||
let stdout = printenv_inside(
|
||||
&["--whitelist", "--no-env-filter"],
|
||||
&[("SOME_RANDOM_NOISE_VAR", "kept")],
|
||||
&["SOME_RANDOM_NOISE_VAR"],
|
||||
);
|
||||
assert!(
|
||||
stdout.contains("kept"),
|
||||
"expected --no-env-filter to pass host var through, got: {stdout}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_env_filter_blacklist_keeps_secrets() {
|
||||
let stdout = printenv_inside(&["--no-env-filter"], &[("GH_TOKEN", "kept")], &["GH_TOKEN"]);
|
||||
assert!(
|
||||
stdout.contains("kept"),
|
||||
"expected --no-env-filter to pass secrets through, got: {stdout}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_env_filter_still_honors_user_env() {
|
||||
let stdout = printenv_inside(
|
||||
&["--no-env-filter", "--env", "FORCED=yes"],
|
||||
&[],
|
||||
&["FORCED"],
|
||||
);
|
||||
assert!(
|
||||
stdout.contains("yes"),
|
||||
"expected user --env to still work with --no-env-filter, got: {stdout}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blacklist_env_overrides_builtin_deny() {
|
||||
let stdout = printenv_inside(
|
||||
&["--env", "GH_TOKEN=overridden"],
|
||||
&[("GH_TOKEN", "original")],
|
||||
&["GH_TOKEN"],
|
||||
);
|
||||
assert!(
|
||||
stdout.contains("overridden"),
|
||||
"expected --env to override deny, got: {stdout}"
|
||||
);
|
||||
assert!(!stdout.contains("original"));
|
||||
}
|
||||
Reference in New Issue
Block a user