diff --git a/codex-rs/windows-sandbox-rs/src/setup_orchestrator.rs b/codex-rs/windows-sandbox-rs/src/setup_orchestrator.rs index ef952a4e03..2a83f45e82 100644 --- a/codex-rs/windows-sandbox-rs/src/setup_orchestrator.rs +++ b/codex-rs/windows-sandbox-rs/src/setup_orchestrator.rs @@ -192,8 +192,11 @@ fn run_setup_refresh_inner( let exe = find_setup_exe(); // Refresh should never request elevation; ensure verb isn't set and we don't trigger UAC. let mut cmd = Command::new(&exe); - cmd.arg(&b64).stdout(Stdio::null()).stderr(Stdio::null()); - let cwd = std::env::current_dir().unwrap_or_else(|_| request.codex_home.to_path_buf()); + let cwd = request.codex_home.to_path_buf(); + cmd.arg(&b64) + .current_dir(&cwd) + .stdout(Stdio::null()) + .stderr(Stdio::null()); log_note( &format!( "setup refresh: spawning {} (cwd={}, payload_len={})", @@ -767,6 +770,7 @@ fn build_payload_roots( let write_roots = filter_user_profile_root_exclusions(write_roots); let write_roots = filter_ssh_config_dependency_roots(write_roots); let write_roots = filter_sensitive_write_roots(write_roots, request.codex_home); + let write_roots = filter_acl_unsupported_roots(write_roots); let mut read_roots = if let Some(roots) = overrides.read_roots.as_deref() { // An explicit override is the split policy's complete readable set. Keep only the // helper/platform roots the elevated setup needs; do not re-add legacy cwd/full-read roots. @@ -814,6 +818,7 @@ fn build_payload_deny_write_paths( }) .collect(); deny_write_paths.extend(allow_deny_paths.deny); + deny_write_paths.retain(|path| !is_acl_unsupported_root(path)); deny_write_paths } @@ -940,6 +945,21 @@ fn filter_sensitive_write_roots(mut roots: Vec, codex_home: &Path) -> V roots } +fn filter_acl_unsupported_roots(roots: Vec) -> Vec { + roots + .into_iter() + .filter(|root| !is_acl_unsupported_root(root)) + .collect() +} + +fn is_acl_unsupported_root(path: &Path) -> bool { + let key = canonical_path_key(path); + key.starts_with("//wsl.localhost/") + || key.starts_with("//wsl$/") + || key.starts_with("//?/unc/wsl.localhost/") + || key.starts_with("//?/unc/wsl$/") +} + #[cfg(test)] mod tests { use super::WINDOWS_PLATFORM_DEFAULT_READ_ROOTS; @@ -957,6 +977,7 @@ mod tests { use std::collections::HashMap; use std::collections::HashSet; use std::fs; + use std::path::Path; use std::path::PathBuf; use tempfile::TempDir; @@ -1438,6 +1459,71 @@ mod tests { ); } + #[test] + fn acl_unsupported_root_detection_matches_wsl_unc_variants() { + assert!(super::is_acl_unsupported_root(Path::new( + r"\\wsl.localhost\Ubuntu\home\dev\repo" + ))); + assert!(super::is_acl_unsupported_root(Path::new( + r"\\wsl$\Ubuntu\home\dev\repo" + ))); + assert!(super::is_acl_unsupported_root(Path::new( + r"\\?\UNC\wsl.localhost\Ubuntu\home\dev\repo" + ))); + assert!(!super::is_acl_unsupported_root(Path::new( + r"C:\Users\dev\repo" + ))); + } + + #[test] + fn filter_acl_unsupported_roots_drops_wsl_unc_entries() { + let local_root = PathBuf::from(r"C:\Users\dev\repo"); + let roots = super::filter_acl_unsupported_roots(vec![ + PathBuf::from(r"\\wsl.localhost\Ubuntu\home\dev\repo"), + PathBuf::from(r"\\?\UNC\wsl.localhost\Ubuntu\home\dev\repo"), + local_root.clone(), + ]); + + assert_eq!(vec![local_root], roots); + } + + #[test] + fn payload_deny_write_paths_skip_acl_unsupported_entries() { + let tmp = TempDir::new().expect("tempdir"); + let codex_home = tmp.path().join("codex-home"); + let command_cwd = tmp.path().join("workspace"); + fs::create_dir_all(&command_cwd).expect("create workspace"); + let policy = SandboxPolicy::WorkspaceWrite { + writable_roots: vec![], + network_access: false, + exclude_tmpdir_env_var: true, + exclude_slash_tmp: true, + }; + let request = super::SandboxSetupRequest { + policy: &policy, + policy_cwd: &command_cwd, + command_cwd: &command_cwd, + env_map: &HashMap::new(), + codex_home: &codex_home, + proxy_enforced: false, + }; + + let deny_write_paths = super::build_payload_deny_write_paths( + &request, + Some(vec![ + PathBuf::from(r"\\wsl.localhost\Ubuntu\home\dev\repo\.git"), + command_cwd.join(".git"), + ]), + ); + + assert_eq!( + [dunce::canonicalize(command_cwd.join(".git")).expect("canonical .git")] + .into_iter() + .collect::>(), + deny_write_paths.into_iter().collect() + ); + } + #[test] fn full_read_roots_preserve_legacy_platform_defaults() { let tmp = TempDir::new().expect("tempdir");