Compare commits

...

1 Commits

Author SHA1 Message Date
David Wiesen
e82783be9c Preserve minimal env for apply_patch sandboxing 2026-04-17 15:39:12 -07:00
2 changed files with 78 additions and 2 deletions

View File

@@ -95,6 +95,52 @@ impl ApplyPatchRuntime {
.map_err(|e| ToolError::Rejected(format!("failed to determine codex exe: {e}")))
}
fn minimal_env_for_apply_patch() -> HashMap<String, String> {
Self::minimal_env_for_apply_patch_from_vars(std::env::vars())
}
fn minimal_env_for_apply_patch_from_vars<I>(vars: I) -> HashMap<String, String>
where
I: IntoIterator<Item = (String, String)>,
{
const BASE_ENV_VARS: &[&str] = &[
"HOME", "LOGNAME", "PATH", "SHELL", "USER", "USERNAME", "TMPDIR", "TEMP", "TMP",
];
#[cfg(target_os = "windows")]
const WINDOWS_ENV_VARS: &[&str] = &[
"APPDATA",
"LOCALAPPDATA",
"ProgramFiles",
"ProgramFiles(x86)",
"ProgramW6432",
"CommonProgramFiles",
"CommonProgramFiles(x86)",
"SystemRoot",
"ComSpec",
"PATHEXT",
"HOMEDRIVE",
"HOMEPATH",
];
vars.into_iter()
.filter(|(key, _)| {
BASE_ENV_VARS.contains(&key.as_str())
|| {
#[cfg(target_os = "windows")]
{
WINDOWS_ENV_VARS
.iter()
.any(|allowed| allowed.eq_ignore_ascii_case(key))
}
#[cfg(not(target_os = "windows"))]
{
false
}
}
})
.collect()
}
fn build_sandbox_command_with_program(req: &ApplyPatchRequest, exe: PathBuf) -> SandboxCommand {
SandboxCommand {
program: exe.into_os_string(),
@@ -103,8 +149,8 @@ impl ApplyPatchRuntime {
req.action.patch.clone(),
],
cwd: req.action.cwd.clone(),
// Run apply_patch with a minimal environment for determinism and to avoid leaks.
env: HashMap::new(),
// Preserve the minimal platform env needed for sandbox resolution.
env: Self::minimal_env_for_apply_patch(),
additional_permissions: req.additional_permissions.clone(),
}
}

View File

@@ -69,6 +69,36 @@ fn guardian_review_request_includes_patch_context() {
);
}
#[test]
fn build_sandbox_command_preserves_minimal_apply_patch_environment() {
let env = ApplyPatchRuntime::minimal_env_for_apply_patch_from_vars([
("PATH".to_string(), "/usr/bin:/bin".to_string()),
("TMP".to_string(), "/tmp".to_string()),
("SECRET_TOKEN".to_string(), "nope".to_string()),
]);
assert_eq!(env.get("PATH"), Some(&"/usr/bin:/bin".to_string()));
assert_eq!(env.get("TMP"), Some(&"/tmp".to_string()));
assert!(!env.contains_key("SECRET_TOKEN"));
}
#[cfg(target_os = "windows")]
#[test]
fn build_sandbox_command_preserves_windows_core_environment_case_insensitively() {
let env = ApplyPatchRuntime::minimal_env_for_apply_patch_from_vars([
("systemroot".to_string(), "C:\\Windows".to_string()),
("ComSpec".to_string(), "C:\\Windows\\System32\\cmd.exe".to_string()),
("UNRELATED".to_string(), "ignored".to_string()),
]);
assert_eq!(env.get("systemroot"), Some(&"C:\\Windows".to_string()));
assert_eq!(
env.get("ComSpec"),
Some(&"C:\\Windows\\System32\\cmd.exe".to_string())
);
assert!(!env.contains_key("UNRELATED"));
}
#[cfg(not(target_os = "windows"))]
#[test]
fn build_sandbox_command_prefers_configured_codex_self_exe_for_apply_patch() {