Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions codex-rs/core/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1504,6 +1504,15 @@ impl Config {
}
self.forced_auto_mode_downgraded_on_windows = !value;
}

pub fn set_windows_elevated_sandbox_globally(&mut self, value: bool) {
crate::safety::set_windows_elevated_sandbox_enabled(value);
if value {
self.features.enable(Feature::WindowsSandboxElevated);
} else {
self.features.disable(Feature::WindowsSandboxElevated);
}
}
}

fn default_review_model() -> String {
Expand Down
3 changes: 3 additions & 0 deletions codex-rs/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ pub mod token_data;
mod truncate;
mod unified_exec;
mod user_instructions;
pub mod windows_sandbox;
pub use model_provider_info::CHAT_WIRE_API_DEPRECATION_SUMMARY;
pub use model_provider_info::DEFAULT_LMSTUDIO_PORT;
pub use model_provider_info::DEFAULT_OLLAMA_PORT;
Expand Down Expand Up @@ -114,6 +115,8 @@ pub use command_safety::is_safe_command;
pub use exec_policy::ExecPolicyError;
pub use exec_policy::load_exec_policy;
pub use safety::get_platform_sandbox;
pub use safety::is_windows_elevated_sandbox_enabled;
pub use safety::set_windows_elevated_sandbox_enabled;
pub use safety::set_windows_sandbox_enabled;
// Re-export the protocol types from the standalone `codex-protocol` crate so existing
// `codex_core::protocol::...` references continue to work across the workspace.
Expand Down
49 changes: 49 additions & 0 deletions codex-rs/core/src/windows_sandbox.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use crate::protocol::SandboxPolicy;
use std::collections::HashMap;
use std::path::Path;

/// Kill switch for the elevated sandbox NUX on Windows.
///
/// When false, revert to the previous sandbox NUX, which only
/// prompts users to enable the legacy sandbox feature.
pub const ELEVATED_SANDBOX_NUX_ENABLED: bool = true;

#[cfg(target_os = "windows")]
pub fn sandbox_setup_is_complete(codex_home: &Path) -> bool {
codex_windows_sandbox::sandbox_setup_is_complete(codex_home)
}

#[cfg(not(target_os = "windows"))]
pub fn sandbox_setup_is_complete(_codex_home: &Path) -> bool {
false
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this return true? There is no need to do windows sandbox setup on other platforms, so it is compete in a technical/literal sense. Less technically speaking, if a check on this condition leaks into linux/macos code, we probably don't want to follow the path more likely to to invoke run_elevated_setup

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I'll update this in a future PR. For now there is no leakage

}

#[cfg(target_os = "windows")]
pub fn run_elevated_setup(
policy: &SandboxPolicy,
policy_cwd: &Path,
command_cwd: &Path,
env_map: &HashMap<String, String>,
codex_home: &Path,
) -> anyhow::Result<()> {
codex_windows_sandbox::run_elevated_setup(
policy,
policy_cwd,
command_cwd,
env_map,
codex_home,
None,
None,
)
}

#[cfg(not(target_os = "windows"))]
pub fn run_elevated_setup(
_policy: &SandboxPolicy,
_policy_cwd: &Path,
_command_cwd: &Path,
_env_map: &HashMap<String, String>,
_codex_home: &Path,
) -> anyhow::Result<()> {
anyhow::bail!("elevated Windows sandbox setup is only supported on Windows")
}
89 changes: 86 additions & 3 deletions codex-rs/tui/src/app.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use crate::app_backtrack::BacktrackState;
use crate::app_event::AppEvent;
#[cfg(target_os = "windows")]
use crate::app_event::WindowsSandboxEnableMode;
#[cfg(target_os = "windows")]
use crate::app_event::WindowsSandboxFallbackReason;
use crate::app_event_sender::AppEventSender;
use crate::bottom_pane::ApprovalRequest;
use crate::chatwidget::ChatWidget;
Expand Down Expand Up @@ -792,19 +796,91 @@ impl App {
AppEvent::OpenWindowsSandboxEnablePrompt { preset } => {
self.chat_widget.open_windows_sandbox_enable_prompt(preset);
}
AppEvent::EnableWindowsSandboxForAgentMode { preset } => {
AppEvent::OpenWindowsSandboxFallbackPrompt { preset, reason } => {
self.chat_widget.clear_windows_sandbox_setup_status();
self.chat_widget
.open_windows_sandbox_fallback_prompt(preset, reason);
}
AppEvent::BeginWindowsSandboxElevatedSetup { preset } => {
#[cfg(target_os = "windows")]
{
let policy = preset.sandbox.clone();
let policy_cwd = self.config.cwd.clone();
let command_cwd = policy_cwd.clone();
let env_map: std::collections::HashMap<String, String> =
std::env::vars().collect();
let codex_home = self.config.codex_home.clone();
let tx = self.app_event_tx.clone();

// If the elevated setup already ran on this machine, don't prompt for
// elevation again - just flip the config to use the elevated path.
if codex_core::windows_sandbox::sandbox_setup_is_complete(codex_home.as_path())
{
tx.send(AppEvent::EnableWindowsSandboxForAgentMode {
preset,
mode: WindowsSandboxEnableMode::Elevated,
});
return Ok(true);
}

self.chat_widget.show_windows_sandbox_setup_status();
tokio::task::spawn_blocking(move || {
let result = codex_core::windows_sandbox::run_elevated_setup(
&policy,
policy_cwd.as_path(),
command_cwd.as_path(),
&env_map,
codex_home.as_path(),
);
let event = match result {
Ok(()) => AppEvent::EnableWindowsSandboxForAgentMode {
preset: preset.clone(),
mode: WindowsSandboxEnableMode::Elevated,
},
Err(err) => {
tracing::error!(
error = %err,
"failed to run elevated Windows sandbox setup"
);
AppEvent::OpenWindowsSandboxFallbackPrompt {
preset,
reason: WindowsSandboxFallbackReason::ElevationFailed,
}
}
};
tx.send(event);
});
}
#[cfg(not(target_os = "windows"))]
{
let _ = preset;
}
}
AppEvent::EnableWindowsSandboxForAgentMode { preset, mode } => {
#[cfg(target_os = "windows")]
{
self.chat_widget.clear_windows_sandbox_setup_status();
let profile = self.active_profile.as_deref();
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
{
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,
);
self.chat_widget.clear_forced_auto_mode_downgrade();
if let Some((sample_paths, extra_count, failed_scan)) =
self.chat_widget.world_writable_warning_details()
Expand Down Expand Up @@ -833,7 +909,14 @@ impl App {
self.app_event_tx
.send(AppEvent::UpdateSandboxPolicy(preset.sandbox.clone()));
self.chat_widget.add_info_message(
"Enabled experimental Windows sandbox.".to_string(),
match mode {
WindowsSandboxEnableMode::Elevated => {
"Enabled elevated agent sandbox.".to_string()
}
WindowsSandboxEnableMode::Legacy => {
"Enabled non-elevated agent sandbox.".to_string()
}
},
None,
);
}
Expand All @@ -851,7 +934,7 @@ impl App {
}
#[cfg(not(target_os = "windows"))]
{
let _ = preset;
let _ = (preset, mode);
}
}
AppEvent::PersistModelSelection { model, effort } => {
Expand Down
27 changes: 27 additions & 0 deletions codex-rs/tui/src/app_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@ use codex_core::protocol::AskForApproval;
use codex_core::protocol::SandboxPolicy;
use codex_protocol::openai_models::ReasoningEffort;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
pub(crate) enum WindowsSandboxEnableMode {
Elevated,
Legacy,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
pub(crate) enum WindowsSandboxFallbackReason {
ElevationFailed,
}

#[allow(clippy::large_enum_variant)]
#[derive(Debug)]
pub(crate) enum AppEvent {
Expand Down Expand Up @@ -106,10 +119,24 @@ pub(crate) enum AppEvent {
preset: ApprovalPreset,
},

/// Open the Windows sandbox fallback prompt after declining or failing elevation.
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
OpenWindowsSandboxFallbackPrompt {
preset: ApprovalPreset,
reason: WindowsSandboxFallbackReason,
},

/// Begin the elevated Windows sandbox setup flow.
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
BeginWindowsSandboxElevatedSetup {
preset: ApprovalPreset,
},

/// Enable the Windows sandbox feature and switch to Agent mode.
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
EnableWindowsSandboxForAgentMode {
preset: ApprovalPreset,
mode: WindowsSandboxEnableMode,
},

/// Update the current approval policy in the running app and widget.
Expand Down
Loading
Loading