Enable MXC selection through Windows sandbox configuration (#46271)

## What changed

- Accept `windows.sandbox = "mxc"` and preserve the selected backend through environment configuration, command execution, patch writes, and sandbox metadata.
- Treat MXC as enabled in the TUI and report Windows sandbox readiness as `ready`, avoiding legacy setup prompts.
- Keep `allowed_sandbox_implementations` scoped to the legacy elevated and unelevated backends without restricting MXC.
- Default `windows.sandbox_private_desktop` to `false` for MXC while retaining `true` for legacy sandboxes.

## Testing

Add coverage for MXC configuration precedence, legacy requirement handling, sandbox selection, and TUI state. Add a Wine integration test that verifies command and patch routing fails when native MXC is unavailable and reports `windows_mxc` in turn metadata.

GitOrigin-RevId: e2162447d0750f60753864c92a20e02a7f297bca
This commit is contained in:
iceweasel-oai
2026-09-17 18:20:28 +00:00
committed by copyberry
parent 3d3ae4965a
commit 8b78600dc8
72 changed files with 557 additions and 142 deletions
@@ -68,6 +68,7 @@ use crate::unified_exec::process::UnifiedExecProcess;
use crate::unified_exec::shell_snapshot::shell_snapshot_request;
use crate::unified_exec::take_plugin_metrics_sidecar;
use crate::unified_exec::trace_id;
use crate::windows_sandbox::windows_sandbox_level_for_legacy_checks;
use codex_core_plugins::PLUGIN_METRICS_OUTPUT_ENV_VAR;
use codex_core_plugins::PluginCommandAttribution;
use codex_core_plugins::PluginMetricsSidecar;
@@ -1488,7 +1489,10 @@ impl UnifiedExecProcessManager {
approval_policy: context.step_context.settings.approval_policy(),
permission_profile: request.turn_environment.permission_profile().clone(),
environment_policy: request.turn_environment.config().exec_policy.as_ref(),
windows_sandbox_level: request.turn_environment.config().windows_sandbox_level,
windows_sandbox_level: windows_sandbox_level_for_legacy_checks(
request.turn_environment.config().windows_sandbox_type,
request.turn_environment.config().windows_sandbox_level,
),
sandbox_permissions: if request.additional_permissions_preapproved {
crate::sandboxing::SandboxPermissions::UseDefault
} else {
@@ -98,6 +98,7 @@ impl Session {
user_home_dir: None,
temporary_directories: None,
windows_sandbox_selection: executor_windows_sandbox_selection(
environment.config().windows_sandbox_type,
environment.config().windows_sandbox_level,
environment.cwd(),
),
@@ -11,6 +11,7 @@ use crate::session::turn_context::TurnContext;
use crate::session::turn_context::TurnEnvironment;
use crate::tools::sandboxing::ApprovalAction;
use crate::tools::sandboxing::ToolError;
use crate::windows_sandbox::windows_sandbox_level_for_legacy_checks;
use codex_features::Feature;
use codex_file_system::FileSystemSandboxContext;
use codex_network_proxy::EnvironmentNetworkPolicy;
@@ -52,10 +53,16 @@ impl TerminalPolicy {
additional_permissions: Option<AdditionalPermissionProfile>,
) -> Self {
let mut sandbox = environment.sandbox_context(additional_permissions);
if matches!(sandbox_source, TerminalSandboxSource::Native) {
if matches!(sandbox_source, TerminalSandboxSource::Native)
&& windows_sandbox_level_for_legacy_checks(
environment.config().windows_sandbox_type,
environment.config().windows_sandbox_level,
) == codex_protocol::config_types::WindowsSandboxLevel::Disabled
{
// The filesystem helper applies executor defaults, but native process
// launches honor Disabled. Preserve it so later enablement is detected.
sandbox.windows_sandbox_selection = environment.config().windows_sandbox_level.into();
sandbox.windows_sandbox_selection =
codex_protocol::config_types::WindowsSandboxLevel::Disabled.into();
}
Self {
sandbox,
@@ -10,6 +10,7 @@ use codex_protocol::permissions::FileSystemAccessMode;
use codex_protocol::permissions::FileSystemPath;
use codex_protocol::permissions::FileSystemSandboxEntry;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_sandboxing::SandboxType;
use codex_utils_path_uri::PathUri;
use pretty_assertions::assert_eq;
use serde_json::json;
@@ -221,11 +222,13 @@ async fn readable_snapshot_does_not_require_stdin_approval() -> anyhow::Result<(
Ok(())
}
#[test_case::test_case(TerminalSandboxSource::Native, SandboxPermissions::RequireEscalated; "native_disabled_sandbox_needs_review_when_enabled")]
#[test_case::test_case(TerminalSandboxSource::Executor, SandboxPermissions::UseDefault; "executor_keeps_its_restricted_token_default")]
#[test_case::test_case(TerminalSandboxSource::Native, SandboxType::None, SandboxPermissions::RequireEscalated; "native_disabled_sandbox_needs_review_when_enabled")]
#[test_case::test_case(TerminalSandboxSource::Executor, SandboxType::None, SandboxPermissions::UseDefault; "executor_keeps_its_restricted_token_default")]
#[test_case::test_case(TerminalSandboxSource::Native, SandboxType::WindowsMxc, SandboxPermissions::UseDefault; "native_mxc_ignores_legacy_level_changes")]
#[tokio::test]
async fn enabling_windows_sandbox_respects_the_launch_backend(
source: TerminalSandboxSource,
sandbox_type: SandboxType,
expected: SandboxPermissions,
) -> anyhow::Result<()> {
let (_session, turn) = make_session_and_context().await;
@@ -233,7 +236,16 @@ async fn enabling_windows_sandbox_respects_the_launch_backend(
environment.selection.cwd = PathUri::parse("file:///C:/workspace")?;
environment.config_mut().permission_profile =
PermissionProfileSnapshot::legacy(PermissionProfile::read_only());
environment.config_mut().windows_sandbox_type = sandbox_type;
environment.config_mut().windows_sandbox_level = WindowsSandboxLevel::Disabled;
assert_eq!(
environment.windows_sandbox_selection_for_turn_metadata(),
if sandbox_type == SandboxType::WindowsMxc {
codex_file_system::WindowsSandboxSelection::Mxc
} else {
codex_file_system::WindowsSandboxSelection::Disabled
}
);
let permissions = TerminalPermissions::for_launch(
&environment,
&turn,