diff --git a/codex-rs/windows-sandbox-rs/src/allow.rs b/codex-rs/windows-sandbox-rs/src/allow.rs index de6215999..7a879df4a 100644 --- a/codex-rs/windows-sandbox-rs/src/allow.rs +++ b/codex-rs/windows-sandbox-rs/src/allow.rs @@ -18,6 +18,13 @@ pub fn compute_allow_paths( allow.push(p); } }; + let include_tmp_env_vars = matches!( + policy, + SandboxPolicy::WorkspaceWrite { + exclude_tmpdir_env_var: false, + .. + } + ); if matches!(policy, SandboxPolicy::WorkspaceWrite { .. }) { add_path(command_cwd.to_path_buf()); @@ -33,7 +40,7 @@ pub fn compute_allow_paths( } } } - if !matches!(policy, SandboxPolicy::ReadOnly) { + if include_tmp_env_vars { for key in ["TEMP", "TMP"] { if let Some(v) = env_map.get(key) { let abs = PathBuf::from(v); @@ -80,4 +87,32 @@ mod tests { "additional writable root should be allowed" ); } + + #[test] + fn excludes_tmp_env_vars_when_requested() { + let command_cwd = PathBuf::from(r"C:\Workspace"); + let temp_dir = PathBuf::from(r"C:\TempDir"); + let _ = fs::create_dir_all(&command_cwd); + let _ = fs::create_dir_all(&temp_dir); + + let policy = SandboxPolicy::WorkspaceWrite { + writable_roots: vec![], + network_access: false, + exclude_tmpdir_env_var: true, + exclude_slash_tmp: false, + }; + let mut env_map = HashMap::new(); + env_map.insert("TEMP".into(), temp_dir.to_string_lossy().to_string()); + + let allow = compute_allow_paths(&policy, &command_cwd, &command_cwd, &env_map); + + assert!( + allow.iter().any(|p| p == &command_cwd), + "command cwd should be allowed" + ); + assert!( + !allow.iter().any(|p| p == &temp_dir), + "TEMP should be excluded when exclude_tmpdir_env_var is true" + ); + } } diff --git a/codex-rs/windows-sandbox-rs/src/lib.rs b/codex-rs/windows-sandbox-rs/src/lib.rs index 1720294f2..e2277daad 100644 --- a/codex-rs/windows-sandbox-rs/src/lib.rs +++ b/codex-rs/windows-sandbox-rs/src/lib.rs @@ -71,6 +71,10 @@ mod windows_impl { type PipeHandles = ((HANDLE, HANDLE), (HANDLE, HANDLE), (HANDLE, HANDLE)); + fn should_apply_network_block(policy: &SandboxPolicy) -> bool { + !policy.has_full_network_access() + } + fn ensure_dir(p: &Path) -> Result<()> { if let Some(d) = p.parent() { std::fs::create_dir_all(d)?; @@ -214,9 +218,12 @@ mod windows_impl { timeout_ms: Option, ) -> Result { let policy = parse_policy(policy_json_or_preset)?; + let apply_network_block = should_apply_network_block(&policy); normalize_null_device_env(&mut env_map); ensure_non_interactive_pager(&mut env_map); - apply_no_network_to_env(&mut env_map)?; + if apply_network_block { + apply_no_network_to_env(&mut env_map)?; + } ensure_codex_home_exists(codex_home)?; let current_dir = cwd.to_path_buf(); @@ -447,6 +454,36 @@ mod windows_impl { timed_out, }) } + + #[cfg(test)] + mod tests { + use super::should_apply_network_block; + use crate::policy::SandboxPolicy; + + fn workspace_policy(network_access: bool) -> SandboxPolicy { + SandboxPolicy::WorkspaceWrite { + writable_roots: Vec::new(), + network_access, + exclude_tmpdir_env_var: false, + exclude_slash_tmp: false, + } + } + + #[test] + fn applies_network_block_when_access_is_disabled() { + assert!(should_apply_network_block(&workspace_policy(false))); + } + + #[test] + fn skips_network_block_when_access_is_allowed() { + assert!(!should_apply_network_block(&workspace_policy(true))); + } + + #[test] + fn applies_network_block_for_read_only() { + assert!(should_apply_network_block(&SandboxPolicy::ReadOnly)); + } + } } #[cfg(not(target_os = "windows"))]