diff --git a/codex-rs/cli/src/debug_sandbox.rs b/codex-rs/cli/src/debug_sandbox.rs index 8c1f3e5d3..13cb8cdd8 100644 --- a/codex-rs/cli/src/debug_sandbox.rs +++ b/codex-rs/cli/src/debug_sandbox.rs @@ -136,7 +136,8 @@ async fn run_command_under_sandbox( if let SandboxType::Windows = sandbox_type { #[cfg(target_os = "windows")] { - use codex_core::features::Feature; + use codex_core::windows_sandbox::WindowsSandboxLevelExt; + use codex_protocol::config_types::WindowsSandboxLevel; use codex_windows_sandbox::run_windows_sandbox_capture; use codex_windows_sandbox::run_windows_sandbox_capture_elevated; @@ -147,8 +148,10 @@ async fn run_command_under_sandbox( let env_map = env.clone(); let command_vec = command.clone(); let base_dir = config.codex_home.clone(); - let use_elevated = config.features.enabled(Feature::WindowsSandbox) - && config.features.enabled(Feature::WindowsSandboxElevated); + let use_elevated = matches!( + WindowsSandboxLevel::from_config(&config), + WindowsSandboxLevel::Elevated + ); // Preflight audit is invoked elsewhere at the appropriate times. let res = tokio::task::spawn_blocking(move || { diff --git a/codex-rs/core/src/config/mod.rs b/codex-rs/core/src/config/mod.rs index 5d84d577e..98716472e 100644 --- a/codex-rs/core/src/config/mod.rs +++ b/codex-rs/core/src/config/mod.rs @@ -1679,18 +1679,19 @@ impl Config { } } - pub fn set_windows_sandbox_globally(&mut self, value: bool) { + pub fn set_windows_sandbox_enabled(&mut self, value: bool) { if value { self.features.enable(Feature::WindowsSandbox); + self.forced_auto_mode_downgraded_on_windows = false; } else { self.features.disable(Feature::WindowsSandbox); } - self.forced_auto_mode_downgraded_on_windows = !value; } - pub fn set_windows_elevated_sandbox_globally(&mut self, value: bool) { + pub fn set_windows_elevated_sandbox_enabled(&mut self, value: bool) { if value { self.features.enable(Feature::WindowsSandboxElevated); + self.forced_auto_mode_downgraded_on_windows = false; } else { self.features.disable(Feature::WindowsSandboxElevated); } diff --git a/codex-rs/core/src/windows_sandbox.rs b/codex-rs/core/src/windows_sandbox.rs index 22825752b..7b9451c24 100644 --- a/codex-rs/core/src/windows_sandbox.rs +++ b/codex-rs/core/src/windows_sandbox.rs @@ -23,13 +23,13 @@ impl WindowsSandboxLevelExt for WindowsSandboxLevel { } fn from_features(features: &Features) -> WindowsSandboxLevel { - if !features.enabled(Feature::WindowsSandbox) { - return WindowsSandboxLevel::Disabled; - } if features.enabled(Feature::WindowsSandboxElevated) { - WindowsSandboxLevel::Elevated - } else { + return WindowsSandboxLevel::Elevated; + } + if features.enabled(Feature::WindowsSandbox) { WindowsSandboxLevel::RestrictedToken + } else { + WindowsSandboxLevel::Disabled } } } @@ -94,3 +94,54 @@ pub fn run_elevated_setup( ) -> anyhow::Result<()> { anyhow::bail!("elevated Windows sandbox setup is only supported on Windows") } + +#[cfg(test)] +mod tests { + use super::*; + use crate::features::Features; + use pretty_assertions::assert_eq; + + #[test] + fn elevated_flag_works_by_itself() { + let mut features = Features::with_defaults(); + features.enable(Feature::WindowsSandboxElevated); + + assert_eq!( + WindowsSandboxLevel::from_features(&features), + WindowsSandboxLevel::Elevated + ); + } + + #[test] + fn restricted_token_flag_works_by_itself() { + let mut features = Features::with_defaults(); + features.enable(Feature::WindowsSandbox); + + assert_eq!( + WindowsSandboxLevel::from_features(&features), + WindowsSandboxLevel::RestrictedToken + ); + } + + #[test] + fn no_flags_means_no_sandbox() { + let features = Features::with_defaults(); + + assert_eq!( + WindowsSandboxLevel::from_features(&features), + WindowsSandboxLevel::Disabled + ); + } + + #[test] + fn elevated_wins_when_both_flags_are_enabled() { + let mut features = Features::with_defaults(); + features.enable(Feature::WindowsSandbox); + features.enable(Feature::WindowsSandboxElevated); + + assert_eq!( + WindowsSandboxLevel::from_features(&features), + WindowsSandboxLevel::Elevated + ); + } +} diff --git a/codex-rs/tui/src/app.rs b/codex-rs/tui/src/app.rs index bf4ab6798..36a5ddd4b 100644 --- a/codex-rs/tui/src/app.rs +++ b/codex-rs/tui/src/app.rs @@ -1685,23 +1685,29 @@ impl App { let feature_key = Feature::WindowsSandbox.key(); let elevated_key = Feature::WindowsSandboxElevated.key(); let elevated_enabled = matches!(mode, WindowsSandboxEnableMode::Elevated); - match ConfigEditsBuilder::new(&self.config.codex_home) - .with_profile(profile) - .set_feature_enabled(feature_key, true) - .set_feature_enabled(elevated_key, elevated_enabled) - .apply() - .await - { + let mut builder = + ConfigEditsBuilder::new(&self.config.codex_home).with_profile(profile); + if elevated_enabled { + builder = builder.set_feature_enabled(elevated_key, true); + } else { + builder = builder + .set_feature_enabled(feature_key, true) + .set_feature_enabled(elevated_key, false); + } + match builder.apply().await { Ok(()) => { - self.config.set_windows_sandbox_globally(true); - self.config - .set_windows_elevated_sandbox_globally(elevated_enabled); - self.chat_widget - .set_feature_enabled(Feature::WindowsSandbox, true); - self.chat_widget.set_feature_enabled( - Feature::WindowsSandboxElevated, - elevated_enabled, - ); + if elevated_enabled { + self.config.set_windows_elevated_sandbox_enabled(true); + self.chat_widget + .set_feature_enabled(Feature::WindowsSandboxElevated, true); + } else { + self.config.set_windows_sandbox_enabled(true); + self.config.set_windows_elevated_sandbox_enabled(false); + self.chat_widget + .set_feature_enabled(Feature::WindowsSandbox, true); + self.chat_widget + .set_feature_enabled(Feature::WindowsSandboxElevated, false); + } self.chat_widget.clear_forced_auto_mode_downgrade(); let windows_sandbox_level = WindowsSandboxLevel::from_config(&self.config); diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index 17b839f7a..bced1e065 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -882,7 +882,7 @@ mod tests { let mut config = build_config(&temp_dir).await?; config.did_user_set_custom_approval_policy_or_sandbox_mode = false; config.active_project = ProjectConfig { trust_level: None }; - config.set_windows_sandbox_globally(false); + config.set_windows_sandbox_enabled(false); let should_show = should_show_trust_screen(&config); if cfg!(target_os = "windows") { @@ -905,7 +905,7 @@ mod tests { let mut config = build_config(&temp_dir).await?; config.did_user_set_custom_approval_policy_or_sandbox_mode = false; config.active_project = ProjectConfig { trust_level: None }; - config.set_windows_sandbox_globally(true); + config.set_windows_sandbox_enabled(true); let should_show = should_show_trust_screen(&config); if cfg!(target_os = "windows") {