From e95abcdf4939f37f11f00f984efdbbf8b088346e Mon Sep 17 00:00:00 2001 From: iceweasel-oai Date: Wed, 7 Oct 2026 01:42:03 +0000 Subject: [PATCH] Add a Windows MXC sandbox opt-out (#51547) ## What changed Add `windows.allow_mxc` to configuration and its JSON schema. Setting it to `false` blocks automatic MXC selection even when `features.prefer_mxc` is enabled, and rejects explicit `windows.sandbox = "mxc"` configuration with a clear error. Omitting the setting preserves existing behavior. ## Testing Extend configuration tests to cover disabling automatic MXC selection and rejecting explicit MXC configuration when `windows.allow_mxc = false`. GitOrigin-RevId: bf7c0648cfcb3cc1d0dc41b9faffcb8078672780 --- codex-rs/config/src/types.rs | 2 + codex-rs/core/config.schema.json | 4 ++ codex-rs/core/src/config/config_tests.rs | 48 ++++++++++++++++--- codex-rs/core/src/config/mod.rs | 10 +++- .../core/src/config/windows_sandbox_config.rs | 15 +++--- 5 files changed, 64 insertions(+), 15 deletions(-) diff --git a/codex-rs/config/src/types.rs b/codex-rs/config/src/types.rs index fab6fb2e95..f72ad7ac98 100644 --- a/codex-rs/config/src/types.rs +++ b/codex-rs/config/src/types.rs @@ -194,6 +194,8 @@ pub enum WindowsSandboxModeToml { #[schemars(deny_unknown_fields)] pub struct WindowsToml { pub sandbox: Option, + /// False blocks both explicit MXC configuration and automatic selection. + pub allow_mxc: Option, } #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, JsonSchema)] diff --git a/codex-rs/core/config.schema.json b/codex-rs/core/config.schema.json index 5864a5df77..7e81582207 100644 --- a/codex-rs/core/config.schema.json +++ b/codex-rs/core/config.schema.json @@ -6738,6 +6738,10 @@ "WindowsToml": { "additionalProperties": false, "properties": { + "allow_mxc": { + "description": "False blocks both explicit MXC configuration and automatic selection.", + "type": "boolean" + }, "sandbox": { "$ref": "#/definitions/WindowsSandboxModeToml" } diff --git a/codex-rs/core/src/config/config_tests.rs b/codex-rs/core/src/config/config_tests.rs index 806bd8082f..81000cb7c7 100644 --- a/codex-rs/core/src/config/config_tests.rs +++ b/codex-rs/core/src/config/config_tests.rs @@ -1913,6 +1913,7 @@ async fn network_proxy_feature_matrix_preserves_sandbox_network_semantics() -> s }), windows: Some(WindowsToml { sandbox: Some(WindowsSandboxModeToml::Elevated), + ..Default::default() }), features, ..Default::default() @@ -3682,6 +3683,7 @@ async fn implicit_builtin_workspace_profile_preserves_sandbox_workspace_write_se }), windows: Some(WindowsToml { sandbox: Some(WindowsSandboxModeToml::Elevated), + ..Default::default() }), ..Default::default() }, @@ -3746,6 +3748,7 @@ async fn implicit_builtin_workspace_profile_preserves_add_dir_metadata_carveouts )])), windows: Some(WindowsToml { sandbox: Some(WindowsSandboxModeToml::Elevated), + ..Default::default() }), ..Default::default() }, @@ -11118,6 +11121,12 @@ async fn local_mxc_preference_preserves_configured_backend() -> anyhow::Result<( use codex_sandboxing::SandboxType::WindowsRestrictedToken as RestrictedToken; let codex_home = TempDir::new()?; + let permission_selection = EffectivePermissionSelection { + profiles: None, + selected_profile_id: None, + persisted_profile_id_was_provided: false, + requirements_force_profile_selection: false, + }; for (prefer, resolved_preference, binding, allow_mxc, mode, expected) in [ (true, true, true, true, "unelevated", WindowsMxc), (true, false, true, true, "unelevated", RestrictedToken), @@ -11150,16 +11159,11 @@ async fn local_mxc_preference_preserves_configured_backend() -> anyhow::Result<( .config_layer_stack .requirements() .windows_sandbox_mode, - &EffectivePermissionSelection { - profiles: None, - selected_profile_id: None, - persisted_profile_id_was_provided: false, - requirements_force_profile_selection: false, - }, + &permission_selection, /*profiles_are_active*/ false, /*permission_profile*/ None, /*network_requirements*/ None, - cfg.features.as_ref(), + &cfg, /*enable_network_proxy*/ true, )?, binding && allow_mxc, @@ -11186,6 +11190,36 @@ async fn local_mxc_preference_preserves_configured_backend() -> anyhow::Result<( "prefer={prefer}, resolved={resolved_preference}, binding={binding}, mode={mode}" ); } + for sandbox in ["unelevated", "mxc"] { + let cfg: ConfigToml = toml::from_str(&format!( + "[windows]\nsandbox = {sandbox:?}\nallow_mxc = false\n[features]\nprefer_mxc = true\n" + ))?; + assert!(!config_allows_mxc( + &ConfigRequirements::default().windows_sandbox_mode, + &permission_selection, + /*profiles_are_active*/ false, + /*permission_profile*/ None, + /*network_requirements*/ None, + &cfg, + /*enable_network_proxy*/ false, + )?); + let result = Config::load_from_base_config_with_overrides( + cfg, + ConfigOverrides::default(), + codex_home.abs(), + ) + .await; + if sandbox == "mxc" { + assert_eq!( + result + .expect_err("explicit MXC must respect the opt-out") + .to_string(), + "windows.sandbox = \"mxc\" is not allowed when windows.allow_mxc = false", + ); + } else { + assert!(!result?.prefer_mxc); + } + } Ok(()) } diff --git a/codex-rs/core/src/config/mod.rs b/codex-rs/core/src/config/mod.rs index 1eb2cdf83f..c264a0f0c5 100644 --- a/codex-rs/core/src/config/mod.rs +++ b/codex-rs/core/src/config/mod.rs @@ -3442,6 +3442,14 @@ impl Config { }; let respect_system_proxy = features.enabled(Feature::RespectSystemProxy); let enable_network_proxy = features.enabled(Feature::NetworkProxy); + let allow_mxc = + cfg.windows.as_ref().and_then(|windows| windows.allow_mxc) != Some(false); + if !allow_mxc && resolve_windows_sandbox_mode(&cfg) == Some(WindowsSandboxModeToml::Mxc) { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "windows.sandbox = \"mxc\" is not allowed when windows.allow_mxc = false", + )); + } let PreparedWindowsSandboxConfig { mode: windows_sandbox_mode, sandbox_type: windows_sandbox_type, @@ -3537,7 +3545,7 @@ impl Config { profiles_are_active, permission_profile.as_ref(), network_requirements.as_ref(), - cfg.features.as_ref(), + &cfg, enable_network_proxy, )? && codex_sandboxing::windows_mxc_available(); diff --git a/codex-rs/core/src/config/windows_sandbox_config.rs b/codex-rs/core/src/config/windows_sandbox_config.rs index dbe88ba9f1..8df89df767 100644 --- a/codex-rs/core/src/config/windows_sandbox_config.rs +++ b/codex-rs/core/src/config/windows_sandbox_config.rs @@ -11,6 +11,7 @@ use super::profile_allows_configured_network_proxy; use codex_config::ConstrainedWithSource; use codex_config::NetworkConstraints; use codex_config::Sourced; +use codex_config::config_toml::ConfigToml; use codex_config::types::WindowsSandboxModeToml; use codex_features::FeaturesToml; use codex_protocol::config_types::WindowsSandboxLevel; @@ -94,20 +95,20 @@ pub fn prepare_windows_sandbox_config( }) } -/// Managed requirements take precedence; otherwise preserve explicit -/// feature-level or active-profile binding denials during automatic selection. +/// Preserve ordinary and managed MXC opt-outs plus network binding denials. pub(super) fn config_allows_mxc( windows_sandbox_mode: &ConstrainedWithSource>, permission_selection: &EffectivePermissionSelection<'_>, profiles_are_active: bool, permission_profile: Option<&PermissionProfile>, network_requirements: Option<&Sourced>, - features: Option<&FeaturesToml>, + cfg: &ConfigToml, enable_network_proxy: bool, ) -> std::io::Result { - if windows_sandbox_mode - .can_set(&Some(WindowsSandboxModeToml::Mxc)) - .is_err() + if cfg.windows.as_ref().and_then(|windows| windows.allow_mxc) == Some(false) + || windows_sandbox_mode + .can_set(&Some(WindowsSandboxModeToml::Mxc)) + .is_err() { return Ok(false); } @@ -123,7 +124,7 @@ pub(super) fn config_allows_mxc( Ok(windows_mxc_allowed_by_config( windows_sandbox_mode, network_requirements, - features, + cfg.features.as_ref(), enable_network_proxy, profile_local_binding, ))