From 18fe1d9fe3fd7b58c8d36763660e915d3b84dd53 Mon Sep 17 00:00:00 2001 From: iceweasel-oai Date: Tue, 23 Jun 2026 12:29:46 -0700 Subject: [PATCH] [codex] Preserve proxy state for filesystem sandbox helpers (#29671) ## Why Filesystem helpers intentionally run with a minimal environment that excludes proxy variables. After filesystem operations started using the Windows sandbox wrapper, the wrapper derived an empty proxy configuration from that helper environment and compared it with the persistent sandbox setup marker. When the marker contained proxy ports, every filesystem operation appeared to require a firewall update, which could launch elevated setup, show a UAC or loader dialog, and fail operations such as `apply_patch` with error 1223. Filesystem helpers do not use network access, so they should preserve the proxy/firewall state established by normal sandboxed process launches. ## What changed - Add an explicit Windows sandbox proxy-settings mode for reconciling or preserving persistent proxy state. - Use preserve mode for filesystem helpers while normal process launches continue to reconcile proxy settings from their environment. - Carry the selected proxy state consistently through setup validation, elevated setup, and non-elevated ACL refreshes. - Cover wrapper argument propagation and marker-derived proxy preservation. ## Validation - `cargo build -p codex-cli --bin codex` - `just test -p codex-windows-sandbox preserving_proxy_settings_uses_the_existing_marker` - `just test -p codex-windows-sandbox windows_wrapper_args_round_trip` - `just test -p codex-windows-sandbox setup_request_prefers_explicit_proxy_settings` - `just test -p codex-sandboxing transform_for_direct_spawn_windows` - `just test -p codex-exec-server fs_sandbox::tests` - Ran the same sandboxed `fs/writeFile` reproduction against published `0.142.0-alpha.6` and the new CLI. The published CLI launched elevated setup and failed with `ShellExecuteExW ... 1223`; the new CLI completed without elevation. Related to #28359. --- codex-rs/cli/src/debug_sandbox.rs | 2 + codex-rs/exec-server/src/fs_sandbox.rs | 2 + codex-rs/exec-server/src/process_sandbox.rs | 2 + codex-rs/sandboxing/src/lib.rs | 1 + codex-rs/sandboxing/src/manager.rs | 5 ++ codex-rs/sandboxing/src/manager_tests.rs | 8 ++ .../windows-sandbox-rs/src/elevated_impl.rs | 2 + codex-rs/windows-sandbox-rs/src/identity.rs | 81 ++++++++++++++++-- codex-rs/windows-sandbox-rs/src/lib.rs | 9 ++ codex-rs/windows-sandbox-rs/src/setup.rs | 85 +++++++++++++++++-- codex-rs/windows-sandbox-rs/src/spawn_prep.rs | 2 + .../src/unified_exec/backends/elevated.rs | 3 + .../src/unified_exec/mod.rs | 5 +- codex-rs/windows-sandbox-rs/src/wrapper.rs | 12 +++ .../windows-sandbox-rs/src/wrapper_tests.rs | 7 ++ 15 files changed, 211 insertions(+), 15 deletions(-) diff --git a/codex-rs/cli/src/debug_sandbox.rs b/codex-rs/cli/src/debug_sandbox.rs index 9561dde36..4f77a4b82 100644 --- a/codex-rs/cli/src/debug_sandbox.rs +++ b/codex-rs/cli/src/debug_sandbox.rs @@ -473,6 +473,7 @@ async fn run_command_under_windows_session( ) -> ! { use codex_core::windows_sandbox::WindowsSandboxLevelExt; use codex_protocol::config_types::WindowsSandboxLevel; + use codex_windows_sandbox::WindowsSandboxProxySettingsMode; use codex_windows_sandbox::WindowsSandboxSessionRequest; use codex_windows_sandbox::spawn_windows_sandbox_session_for_level; @@ -485,6 +486,7 @@ async fn run_command_under_windows_session( cwd: cwd.as_path(), env_map: env, windows_sandbox_level: WindowsSandboxLevel::from_config(config), + proxy_settings_mode: WindowsSandboxProxySettingsMode::Reconcile, proxy_enforced: false, timeout_ms: None, read_roots_override: None, diff --git a/codex-rs/exec-server/src/fs_sandbox.rs b/codex-rs/exec-server/src/fs_sandbox.rs index 6db3804ae..41a3e1221 100644 --- a/codex-rs/exec-server/src/fs_sandbox.rs +++ b/codex-rs/exec-server/src/fs_sandbox.rs @@ -131,6 +131,8 @@ impl FileSystemSandboxRunner { sandbox_manager .transform_for_direct_spawn(SandboxDirectSpawnTransformRequest { workspace_roots, + windows_sandbox_proxy_settings_mode: + codex_sandboxing::WindowsSandboxProxySettingsMode::Preserve, transform: SandboxTransformRequest { command, permissions: permission_profile, diff --git a/codex-rs/exec-server/src/process_sandbox.rs b/codex-rs/exec-server/src/process_sandbox.rs index e3bffeaa6..ef97d0ce5 100644 --- a/codex-rs/exec-server/src/process_sandbox.rs +++ b/codex-rs/exec-server/src/process_sandbox.rs @@ -107,6 +107,8 @@ pub(crate) fn prepare_exec_request( let request = sandbox_manager .transform_for_direct_spawn(SandboxDirectSpawnTransformRequest { workspace_roots, + windows_sandbox_proxy_settings_mode: + codex_sandboxing::WindowsSandboxProxySettingsMode::Reconcile, transform: SandboxTransformRequest { // TODO(jif): Preserve params.arg0 for the inner command across the sandbox // wrapper, or reject sandboxed requests with a custom arg0. diff --git a/codex-rs/sandboxing/src/lib.rs b/codex-rs/sandboxing/src/lib.rs index 0688d0f92..c9109146b 100644 --- a/codex-rs/sandboxing/src/lib.rs +++ b/codex-rs/sandboxing/src/lib.rs @@ -12,6 +12,7 @@ mod windows; pub use bwrap::find_system_bwrap_in_path; #[cfg(target_os = "linux")] pub use bwrap::system_bwrap_warning; +pub use codex_windows_sandbox::WindowsSandboxProxySettingsMode; pub use denial::is_likely_sandbox_denied; pub use manager::SandboxCommand; pub use manager::SandboxDirectSpawnTransformRequest; diff --git a/codex-rs/sandboxing/src/manager.rs b/codex-rs/sandboxing/src/manager.rs index 30870417f..51d2d6c98 100644 --- a/codex-rs/sandboxing/src/manager.rs +++ b/codex-rs/sandboxing/src/manager.rs @@ -152,6 +152,7 @@ pub struct SandboxTransformRequest<'a> { pub struct SandboxDirectSpawnTransformRequest<'a> { pub transform: SandboxTransformRequest<'a>, pub workspace_roots: &'a [AbsolutePathBuf], + pub windows_sandbox_proxy_settings_mode: codex_windows_sandbox::WindowsSandboxProxySettingsMode, } // TODO(anp): Revisit this preparation type once this module's PathUri migration is complete. @@ -484,12 +485,14 @@ impl SandboxManager { codex_home: &Path, ) -> Result { let workspace_roots = request.workspace_roots; + let proxy_settings_mode = request.windows_sandbox_proxy_settings_mode; let mut request = self.transform(request.transform)?; if request.sandbox == SandboxType::WindowsRestrictedToken { wrap_windows_sandbox_exec_request_for_direct_spawn( &mut request, workspace_roots, codex_home, + proxy_settings_mode, )?; } Ok(request) @@ -501,6 +504,7 @@ fn wrap_windows_sandbox_exec_request_for_direct_spawn( request: &mut SandboxExecRequest, workspace_roots: &[AbsolutePathBuf], codex_home: &Path, + proxy_settings_mode: codex_windows_sandbox::WindowsSandboxProxySettingsMode, ) -> Result<(), SandboxTransformError> { // TODO(anp): Keep PathUri through the Windows sandbox wrapper boundary. let native_cwd = @@ -572,6 +576,7 @@ fn wrap_windows_sandbox_exec_request_for_direct_spawn( request.windows_sandbox_level, request.windows_sandbox_private_desktop, proxy_enforced, + proxy_settings_mode, read_roots_override, read_roots_include_platform_defaults, write_roots_override, diff --git a/codex-rs/sandboxing/src/manager_tests.rs b/codex-rs/sandboxing/src/manager_tests.rs index 547155c63..c1a52182d 100644 --- a/codex-rs/sandboxing/src/manager_tests.rs +++ b/codex-rs/sandboxing/src/manager_tests.rs @@ -499,6 +499,8 @@ fn transform_for_direct_spawn_windows_materializes_inner_helper() { .transform_for_direct_spawn_with_codex_home( SandboxDirectSpawnTransformRequest { workspace_roots: workspace_roots.as_slice(), + windows_sandbox_proxy_settings_mode: + codex_windows_sandbox::WindowsSandboxProxySettingsMode::Preserve, transform: SandboxTransformRequest { command: SandboxCommand { program: configured_helper.as_os_str().to_owned(), @@ -544,6 +546,12 @@ fn transform_for_direct_spawn_windows_materializes_inner_helper() { .iter() .any(|arg| arg == "--run-as-windows-sandbox") ); + assert!( + exec_request + .command + .iter() + .any(|arg| arg == "--preserve-proxy-settings") + ); assert!( exec_request .command diff --git a/codex-rs/windows-sandbox-rs/src/elevated_impl.rs b/codex-rs/windows-sandbox-rs/src/elevated_impl.rs index 5b8bb3769..1da6f3294 100644 --- a/codex-rs/windows-sandbox-rs/src/elevated_impl.rs +++ b/codex-rs/windows-sandbox-rs/src/elevated_impl.rs @@ -150,6 +150,7 @@ mod windows_impl { &deny_read_paths_override, &deny_write_paths_override, proxy_enforced, + crate::WindowsSandboxProxySettingsMode::Reconcile, )?; // Build capability SID for ACL grants. let caps = load_or_create_cap_sids(codex_home)?; @@ -214,6 +215,7 @@ mod windows_impl { &deny_read_paths_override, &deny_write_paths_override, proxy_enforced, + crate::WindowsSandboxProxySettingsMode::Reconcile, )?; spawn_runner_transport( codex_home, diff --git a/codex-rs/windows-sandbox-rs/src/identity.rs b/codex-rs/windows-sandbox-rs/src/identity.rs index fcba538ce..9ac3402be 100644 --- a/codex-rs/windows-sandbox-rs/src/identity.rs +++ b/codex-rs/windows-sandbox-rs/src/identity.rs @@ -8,8 +8,8 @@ use crate::setup::SetupMarker; use crate::setup::gather_read_roots; use crate::setup::gather_write_roots_for_permissions; use crate::setup::offline_proxy_settings_from_env; -use crate::setup::run_elevated_setup; -use crate::setup::run_setup_refresh_with_overrides; +use crate::setup::run_elevated_setup_with_proxy_settings; +use crate::setup::run_setup_refresh_with_overrides_and_proxy_settings; use crate::setup::sandbox_users_path; use crate::setup::setup_marker_path; use anyhow::Context; @@ -154,6 +154,7 @@ pub fn require_logon_sandbox_creds( deny_read_paths_override: &[PathBuf], deny_write_paths_override: &[PathBuf], proxy_enforced: bool, + proxy_settings_mode: crate::WindowsSandboxProxySettingsMode, ) -> Result { let sandbox_dir = crate::setup::sandbox_dir(codex_home); let needed_read = read_roots_override @@ -163,13 +164,19 @@ pub fn require_logon_sandbox_creds( .map(<[PathBuf]>::to_vec) .unwrap_or_else(|| gather_write_roots_for_permissions(permissions, command_cwd, env_map)); let network_identity = SandboxNetworkIdentity::from_permissions(permissions, proxy_enforced); - let desired_offline_proxy_settings = offline_proxy_settings_from_env(env_map, network_identity); + let marker = load_marker(codex_home)?; + let desired_offline_proxy_settings = desired_offline_proxy_settings( + marker.as_ref(), + proxy_settings_mode, + env_map, + network_identity, + ); // NOTE: Do not add CODEX_HOME/.sandbox to `needed_write`; it must remain non-writable by the // restricted capability token. The setup helper's `lock_sandbox_dir` is responsible for // granting the sandbox group access to this directory without granting the capability SID. let mut setup_reason: Option = None; - let mut identity = match load_marker(codex_home)? { + let mut identity = match marker { Some(marker) if marker.version_matches() => { if let Some(reason) = marker.request_mismatch_reason(network_identity, &desired_offline_proxy_settings) @@ -201,7 +208,7 @@ pub fn require_logon_sandbox_creds( } else { crate::logging::log_note("sandbox setup required", Some(&sandbox_dir)); } - run_elevated_setup( + run_elevated_setup_with_proxy_settings( crate::setup::SandboxSetupRequest { permissions, command_cwd, @@ -216,11 +223,12 @@ pub fn require_logon_sandbox_creds( deny_read_paths: Some(deny_read_paths_override.to_vec()), deny_write_paths: Some(deny_write_paths_override.to_vec()), }, + &desired_offline_proxy_settings, )?; identity = select_identity(network_identity, codex_home)?; } // Always refresh ACLs (non-elevated) for current roots via the setup binary. - run_setup_refresh_with_overrides( + run_setup_refresh_with_overrides_and_proxy_settings( crate::setup::SandboxSetupRequest { permissions, command_cwd, @@ -235,6 +243,7 @@ pub fn require_logon_sandbox_creds( deny_read_paths: Some(deny_read_paths_override.to_vec()), deny_write_paths: Some(deny_write_paths_override.to_vec()), }, + &desired_offline_proxy_settings, )?; let identity = identity.ok_or_else(|| { anyhow!( @@ -247,6 +256,22 @@ pub fn require_logon_sandbox_creds( }) } +fn desired_offline_proxy_settings( + marker: Option<&SetupMarker>, + proxy_settings_mode: crate::WindowsSandboxProxySettingsMode, + env_map: &HashMap, + network_identity: SandboxNetworkIdentity, +) -> crate::setup::OfflineProxySettings { + match (marker, proxy_settings_mode) { + (Some(marker), crate::WindowsSandboxProxySettingsMode::Preserve) + if marker.version_matches() => + { + marker.offline_proxy_settings() + } + _ => offline_proxy_settings_from_env(env_map, network_identity), + } +} + #[allow(clippy::too_many_arguments)] pub(crate) fn refresh_logon_sandbox_creds( permissions: &ResolvedWindowsSandboxPermissions, @@ -259,6 +284,7 @@ pub(crate) fn refresh_logon_sandbox_creds( deny_read_paths_override: &[PathBuf], deny_write_paths_override: &[PathBuf], proxy_enforced: bool, + proxy_settings_mode: crate::WindowsSandboxProxySettingsMode, ) -> Result { remove_sandbox_users_file(codex_home, "sandbox user login failed")?; require_logon_sandbox_creds( @@ -272,13 +298,20 @@ pub(crate) fn refresh_logon_sandbox_creds( deny_read_paths_override, deny_write_paths_override, proxy_enforced, + proxy_settings_mode, ) } #[cfg(test)] mod tests { + use super::desired_offline_proxy_settings; use super::remove_sandbox_users_file; + use crate::WindowsSandboxProxySettingsMode; + use crate::setup::SandboxNetworkIdentity; + use crate::setup::SetupMarker; use crate::setup::sandbox_users_path; + use pretty_assertions::assert_eq; + use std::collections::HashMap; use std::fs; use tempfile::TempDir; @@ -302,4 +335,40 @@ mod tests { remove_sandbox_users_file(codex_home.path(), "stale creds").expect("remove users"); assert!(!users_path.exists()); } + + #[test] + fn preserving_proxy_settings_uses_the_existing_marker() { + let marker = SetupMarker { + version: crate::setup::SETUP_VERSION, + offline_username: "offline".to_string(), + online_username: "online".to_string(), + created_at: None, + proxy_ports: vec![7890], + allow_local_binding: true, + }; + let env_map = HashMap::from([( + "HTTP_PROXY".to_string(), + "http://127.0.0.1:8080".to_string(), + )]); + + assert_eq!( + desired_offline_proxy_settings( + Some(&marker), + WindowsSandboxProxySettingsMode::Preserve, + &env_map, + SandboxNetworkIdentity::Offline, + ), + marker.offline_proxy_settings() + ); + assert_eq!( + desired_offline_proxy_settings( + Some(&marker), + WindowsSandboxProxySettingsMode::Reconcile, + &env_map, + SandboxNetworkIdentity::Offline, + ) + .proxy_ports, + vec![8080] + ); + } } diff --git a/codex-rs/windows-sandbox-rs/src/lib.rs b/codex-rs/windows-sandbox-rs/src/lib.rs index a7f3df6e2..766faf1a0 100644 --- a/codex-rs/windows-sandbox-rs/src/lib.rs +++ b/codex-rs/windows-sandbox-rs/src/lib.rs @@ -35,6 +35,15 @@ impl fmt::Debug for WindowsSandboxCancellationToken { } } +/// Controls whether a Windows sandbox launch reconciles persistent proxy +/// firewall settings or preserves the settings established by another launch. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub enum WindowsSandboxProxySettingsMode { + #[default] + Reconcile, + Preserve, +} + #[cfg(target_os = "windows")] mod acl; #[cfg(target_os = "windows")] diff --git a/codex-rs/windows-sandbox-rs/src/setup.rs b/codex-rs/windows-sandbox-rs/src/setup.rs index 428b5ff6a..1995c3336 100644 --- a/codex-rs/windows-sandbox-rs/src/setup.rs +++ b/codex-rs/windows-sandbox-rs/src/setup.rs @@ -130,14 +130,16 @@ pub fn run_setup_refresh( proxy_enforced, }, SetupRootOverrides::default(), + /*offline_proxy_settings_override*/ None, ) } -pub fn run_setup_refresh_with_overrides( +pub(crate) fn run_setup_refresh_with_overrides_and_proxy_settings( request: SandboxSetupRequest<'_>, overrides: SetupRootOverrides, + offline_proxy_settings: &OfflineProxySettings, ) -> Result<()> { - run_setup_refresh_inner(request, overrides) + run_setup_refresh_inner(request, overrides, Some(offline_proxy_settings)) } pub fn run_setup_refresh_with_extra_read_roots( @@ -174,12 +176,14 @@ pub fn run_setup_refresh_with_extra_read_roots( deny_read_paths: None, deny_write_paths: None, }, + /*offline_proxy_settings_override*/ None, ) } fn run_setup_refresh_inner( request: SandboxSetupRequest<'_>, overrides: SetupRootOverrides, + offline_proxy_settings_override: Option<&OfflineProxySettings>, ) -> Result<()> { if !request.permissions.is_enforceable_by_windows_sandbox() { anyhow::bail!("unsupported filesystem permissions for Windows sandbox setup"); @@ -187,9 +191,8 @@ fn run_setup_refresh_inner( let (read_roots, write_roots) = build_payload_roots(&request, &overrides); let deny_read_paths = build_payload_deny_read_paths(overrides.deny_read_paths); let deny_write_paths = build_payload_deny_write_paths(&request, overrides.deny_write_paths); - let network_identity = - SandboxNetworkIdentity::from_permissions(request.permissions, request.proxy_enforced); - let offline_proxy_settings = offline_proxy_settings_from_env(request.env_map, network_identity); + let offline_proxy_settings = + offline_proxy_settings_for_request(&request, offline_proxy_settings_override); let payload = ElevationPayload { version: SETUP_VERSION, offline_username: OFFLINE_USERNAME.to_string(), @@ -283,6 +286,13 @@ impl SetupMarker { self.version == SETUP_VERSION } + pub(crate) fn offline_proxy_settings(&self) -> OfflineProxySettings { + OfflineProxySettings { + proxy_ports: self.proxy_ports.clone(), + allow_local_binding: self.allow_local_binding, + } + } + pub(crate) fn request_mismatch_reason( &self, network_identity: SandboxNetworkIdentity, @@ -593,6 +603,17 @@ pub(crate) fn offline_proxy_settings_from_env( } } +fn offline_proxy_settings_for_request( + request: &SandboxSetupRequest<'_>, + offline_proxy_settings_override: Option<&OfflineProxySettings>, +) -> OfflineProxySettings { + offline_proxy_settings_override.cloned().unwrap_or_else(|| { + let network_identity = + SandboxNetworkIdentity::from_permissions(request.permissions, request.proxy_enforced); + offline_proxy_settings_from_env(request.env_map, network_identity) + }) +} + pub(crate) fn proxy_ports_from_env(env_map: &HashMap) -> Vec { let mut ports = BTreeSet::new(); for key in PROXY_ENV_KEYS { @@ -821,6 +842,24 @@ fn run_setup_exe( pub fn run_elevated_setup( request: SandboxSetupRequest<'_>, overrides: SetupRootOverrides, +) -> Result<()> { + run_elevated_setup_inner( + request, overrides, /*offline_proxy_settings_override*/ None, + ) +} + +pub(crate) fn run_elevated_setup_with_proxy_settings( + request: SandboxSetupRequest<'_>, + overrides: SetupRootOverrides, + offline_proxy_settings: &OfflineProxySettings, +) -> Result<()> { + run_elevated_setup_inner(request, overrides, Some(offline_proxy_settings)) +} + +fn run_elevated_setup_inner( + request: SandboxSetupRequest<'_>, + overrides: SetupRootOverrides, + offline_proxy_settings_override: Option<&OfflineProxySettings>, ) -> Result<()> { if !request.permissions.is_enforceable_by_windows_sandbox() { anyhow::bail!("unsupported filesystem permissions for Windows sandbox setup"); @@ -836,9 +875,8 @@ pub fn run_elevated_setup( let (read_roots, write_roots) = build_payload_roots(&request, &overrides); let deny_read_paths = build_payload_deny_read_paths(overrides.deny_read_paths); let deny_write_paths = build_payload_deny_write_paths(&request, overrides.deny_write_paths); - let network_identity = - SandboxNetworkIdentity::from_permissions(request.permissions, request.proxy_enforced); - let offline_proxy_settings = offline_proxy_settings_from_env(request.env_map, network_identity); + let offline_proxy_settings = + offline_proxy_settings_for_request(&request, offline_proxy_settings_override); let payload = ElevationPayload { version: SETUP_VERSION, offline_username: OFFLINE_USERNAME.to_string(), @@ -1170,6 +1208,37 @@ mod tests { ) } + #[test] + fn setup_request_prefers_explicit_proxy_settings() { + let tmp = TempDir::new().expect("tempdir"); + let command_cwd = tmp.path().join("workspace"); + fs::create_dir_all(&command_cwd).expect("create workspace"); + let permissions = permissions_for( + &PermissionProfile::read_only(), + workspace_roots_for(&command_cwd).as_slice(), + ); + let env_map = HashMap::from([( + "HTTP_PROXY".to_string(), + "http://127.0.0.1:8080".to_string(), + )]); + let explicit = super::OfflineProxySettings { + proxy_ports: vec![7890], + allow_local_binding: true, + }; + let request = super::SandboxSetupRequest { + permissions: &permissions, + command_cwd: &command_cwd, + env_map: &env_map, + codex_home: tmp.path(), + proxy_enforced: false, + }; + + assert_eq!( + super::offline_proxy_settings_for_request(&request, Some(&explicit)), + explicit + ); + } + #[test] fn report_helper_failure_uses_setup_error_report_when_clear_succeeded() { let tmp = TempDir::new().expect("tempdir"); diff --git a/codex-rs/windows-sandbox-rs/src/spawn_prep.rs b/codex-rs/windows-sandbox-rs/src/spawn_prep.rs index 9668edb1a..382a6a453 100644 --- a/codex-rs/windows-sandbox-rs/src/spawn_prep.rs +++ b/codex-rs/windows-sandbox-rs/src/spawn_prep.rs @@ -357,6 +357,7 @@ pub(crate) fn prepare_elevated_spawn_context_for_permissions( deny_read_paths_override: &[PathBuf], deny_write_paths_override: &[PathBuf], proxy_enforced: bool, + proxy_settings_mode: crate::WindowsSandboxProxySettingsMode, ) -> Result { normalize_null_device_env(env_map); ensure_non_interactive_pager(env_map); @@ -412,6 +413,7 @@ pub(crate) fn prepare_elevated_spawn_context_for_permissions( deny_write_paths_override }, proxy_enforced, + proxy_settings_mode, )?; let caps = load_or_create_cap_sids(codex_home)?; let (psid_to_use, cap_sids) = if uses_write_capabilities { diff --git a/codex-rs/windows-sandbox-rs/src/unified_exec/backends/elevated.rs b/codex-rs/windows-sandbox-rs/src/unified_exec/backends/elevated.rs index 2e7437ad3..efe4a3f42 100644 --- a/codex-rs/windows-sandbox-rs/src/unified_exec/backends/elevated.rs +++ b/codex-rs/windows-sandbox-rs/src/unified_exec/backends/elevated.rs @@ -56,6 +56,7 @@ pub(crate) async fn spawn_windows_sandbox_session_elevated_for_permission_profil cwd: &Path, mut env_map: HashMap, proxy_enforced: bool, + proxy_settings_mode: crate::WindowsSandboxProxySettingsMode, timeout_ms: Option, read_roots_override: Option<&[PathBuf]>, read_roots_include_platform_defaults: bool, @@ -91,6 +92,7 @@ pub(crate) async fn spawn_windows_sandbox_session_elevated_for_permission_profil &deny_read_paths_override, &deny_write_paths_override, proxy_enforced, + proxy_settings_mode, )?; let spawn_request = SpawnRequest { @@ -133,6 +135,7 @@ pub(crate) async fn spawn_windows_sandbox_session_elevated_for_permission_profil &deny_read_paths_override, &deny_write_paths_override, /*proxy_enforced*/ false, + proxy_settings_mode, )?; spawn_runner_transport_task( codex_home, diff --git a/codex-rs/windows-sandbox-rs/src/unified_exec/mod.rs b/codex-rs/windows-sandbox-rs/src/unified_exec/mod.rs index 02792d6cb..84ccd0d8c 100644 --- a/codex-rs/windows-sandbox-rs/src/unified_exec/mod.rs +++ b/codex-rs/windows-sandbox-rs/src/unified_exec/mod.rs @@ -31,6 +31,7 @@ pub struct WindowsSandboxSessionRequest<'a> { pub env_map: HashMap, pub windows_sandbox_level: WindowsSandboxLevel, pub proxy_enforced: bool, + pub proxy_settings_mode: crate::WindowsSandboxProxySettingsMode, pub timeout_ms: Option, pub read_roots_override: Option<&'a [PathBuf]>, pub read_roots_include_platform_defaults: bool, @@ -48,7 +49,7 @@ pub async fn spawn_windows_sandbox_session_for_level( if request.proxy_enforced || matches!(request.windows_sandbox_level, WindowsSandboxLevel::Elevated) { - spawn_windows_sandbox_session_elevated_for_permission_profile( + backends::elevated::spawn_windows_sandbox_session_elevated_for_permission_profile( request.permission_profile, request.workspace_roots, request.codex_home, @@ -56,6 +57,7 @@ pub async fn spawn_windows_sandbox_session_for_level( request.cwd, request.env_map, request.proxy_enforced, + request.proxy_settings_mode, request.timeout_ms, request.read_roots_override, request.read_roots_include_platform_defaults, @@ -145,6 +147,7 @@ pub async fn spawn_windows_sandbox_session_elevated_for_permission_profile( cwd, env_map, proxy_enforced, + crate::WindowsSandboxProxySettingsMode::Reconcile, timeout_ms, read_roots_override, read_roots_include_platform_defaults, diff --git a/codex-rs/windows-sandbox-rs/src/wrapper.rs b/codex-rs/windows-sandbox-rs/src/wrapper.rs index 375fb07cf..f02af2613 100644 --- a/codex-rs/windows-sandbox-rs/src/wrapper.rs +++ b/codex-rs/windows-sandbox-rs/src/wrapper.rs @@ -26,6 +26,7 @@ const DENY_WRITE_PATHS_JSON_FLAG: &str = "--deny-write-paths-json"; const ENV_JSON_FLAG: &str = "--env-json"; const PERMISSION_PROFILE_FLAG: &str = "--permission-profile"; const PRIVATE_DESKTOP_FLAG: &str = "--windows-sandbox-private-desktop"; +const PRESERVE_PROXY_SETTINGS_FLAG: &str = "--preserve-proxy-settings"; const PROXY_ENFORCED_FLAG: &str = "--proxy-enforced"; const READ_ROOTS_INCLUDE_PLATFORM_DEFAULTS_FLAG: &str = "--read-roots-include-platform-defaults"; const READ_ROOTS_JSON_FLAG: &str = "--read-roots-json"; @@ -43,6 +44,7 @@ pub fn create_windows_sandbox_command_args_for_permission_profile( windows_sandbox_level: WindowsSandboxLevel, windows_sandbox_private_desktop: bool, proxy_enforced: bool, + proxy_settings_mode: crate::WindowsSandboxProxySettingsMode, read_roots_override: Option<&[PathBuf]>, read_roots_include_platform_defaults: bool, write_roots_override: Option<&[PathBuf]>, @@ -82,6 +84,9 @@ pub fn create_windows_sandbox_command_args_for_permission_profile( if proxy_enforced { args.push(PROXY_ENFORCED_FLAG.to_string()); } + if proxy_settings_mode == crate::WindowsSandboxProxySettingsMode::Preserve { + args.push(PRESERVE_PROXY_SETTINGS_FLAG.to_string()); + } if let Some(read_roots_override) = read_roots_override { push_json_arg(&mut args, READ_ROOTS_JSON_FLAG, &read_roots_override); } @@ -154,6 +159,7 @@ struct WindowsSandboxWrapperRequest { windows_sandbox_level: WindowsSandboxLevel, windows_sandbox_private_desktop: bool, proxy_enforced: bool, + proxy_settings_mode: crate::WindowsSandboxProxySettingsMode, read_roots_override: Option>, read_roots_include_platform_defaults: bool, write_roots_override: Option>, @@ -176,6 +182,7 @@ async fn run_windows_sandbox_wrapper_request(request: WindowsSandboxWrapperReque env_map: request.env_map, windows_sandbox_level: request.windows_sandbox_level, proxy_enforced: request.proxy_enforced, + proxy_settings_mode: request.proxy_settings_mode, timeout_ms: None, read_roots_override: request.read_roots_override.as_deref(), read_roots_include_platform_defaults: request.read_roots_include_platform_defaults, @@ -201,6 +208,7 @@ fn parse_windows_sandbox_wrapper_args(args: Vec) -> Result) -> Result windows_sandbox_private_desktop = true, + PRESERVE_PROXY_SETTINGS_FLAG => { + proxy_settings_mode = crate::WindowsSandboxProxySettingsMode::Preserve; + } PROXY_ENFORCED_FLAG => proxy_enforced = true, READ_ROOTS_INCLUDE_PLATFORM_DEFAULTS_FLAG => { read_roots_include_platform_defaults = true; @@ -282,6 +293,7 @@ fn parse_windows_sandbox_wrapper_args(args: Vec) -> Result