From b4317f4cc7f8e7bf1fb924974a0aa80993ab3f90 Mon Sep 17 00:00:00 2001 From: Chris Bookholt Date: Fri, 3 Jul 2026 10:38:38 -0700 Subject: [PATCH] Fix native Windows policy fixtures --- .../core/src/exec_policy_powershell_tests.rs | 19 +++-- codex-rs/core/src/exec_policy_tests.rs | 11 ++- .../core/src/exec_policy_windows_tests.rs | 72 ++++++++++++++++--- codex-rs/core/tests/suite/unified_exec.rs | 3 +- 4 files changed, 87 insertions(+), 18 deletions(-) diff --git a/codex-rs/core/src/exec_policy_powershell_tests.rs b/codex-rs/core/src/exec_policy_powershell_tests.rs index 5f1f7ffb60..be79163ff2 100644 --- a/codex-rs/core/src/exec_policy_powershell_tests.rs +++ b/codex-rs/core/src/exec_policy_powershell_tests.rs @@ -27,6 +27,14 @@ fn untrusted_powershell_command(script: &str) -> Vec { ] } +fn absolute_untrusted_powershell_command(script: &str) -> Vec { + vec![ + r"C:\workspace\powershell.exe".to_string(), + "-Command".to_string(), + script.to_string(), + ] +} + fn prefix_rule_for(command: &[String], decision: &str) -> String { let pattern = command .iter() @@ -479,7 +487,7 @@ fn untrusted_permission_and_windows_backend_gates_require_composed_authority() { use SandboxPermissions as SP; use WindowsSandboxLevel as WSL; - let outer = untrusted_powershell_command("Get-Content Cargo.toml"); + let outer = absolute_untrusted_powershell_command("Get-Content Cargo.toml"); let inner = vec_str(&["Get-Content", "Cargo.toml"]); let partial_policy = format!( "{}\n{}", @@ -686,7 +694,7 @@ fn untrusted_wrapper_identity_uses_exact_outer_and_restrictive_basename_rules() #[tokio::test] async fn untrusted_parsed_results_ignore_requested_amendments() { - let command = untrusted_powershell_command("echo allowed"); + let command = absolute_untrusted_powershell_command("echo allowed"); let inner_allow = prefix_rule_for(&vec_str(&["echo"]), "allow"); let requested_prefix = Some(vec_str(&["echo"])); @@ -837,7 +845,7 @@ async fn dangerous_trusted_unsupported_scripts_keep_generic_policy_protections() } #[tokio::test] -async fn sandbox_override_on_trusted_unsupported_script_uses_outer_argv() { +async fn sandbox_override_on_trusted_unsupported_script_does_not_offer_amendment() { let command = powershell_command("Write-Output 'confusing but inert'"); for (approval_policy, permits_prompt) in [ @@ -855,7 +863,10 @@ async fn sandbox_override_on_trusted_unsupported_script_uses_outer_argv() { ) .await, if permits_prompt { - outer_result(&command, true) + ExecApprovalRequirement::NeedsApproval { + reason: None, + proposed_execpolicy_amendment: None, + } } else { ExecApprovalRequirement::Forbidden { reason: REJECT_SANDBOX_APPROVAL_REASON.to_string(), diff --git a/codex-rs/core/src/exec_policy_tests.rs b/codex-rs/core/src/exec_policy_tests.rs index 2c9008530f..4edac8460c 100644 --- a/codex-rs/core/src/exec_policy_tests.rs +++ b/codex-rs/core/src/exec_policy_tests.rs @@ -79,6 +79,10 @@ fn host_program_path(name: &str) -> String { host_absolute_path(&["usr", "bin", &executable_name]) } +fn posix_script_program_path(name: &str) -> String { + host_program_path(name).replace('\\', "/") +} + fn starlark_string(value: &str) -> String { value.replace('\\', "\\\\").replace('"', "\\\"") } @@ -1712,7 +1716,10 @@ async fn configured_delegators_without_rules_preserve_legacy_sandboxed_behavior( #[tokio::test] async fn configured_exact_allow_can_authorize_an_opaque_nested_shell_only() { - let inner = vec![host_program_path("sh"), "/tmp/approved-script".to_string()]; + let inner = vec![ + posix_script_program_path("sh"), + "/tmp/approved-script".to_string(), + ]; let command = vec![ host_program_path("zsh"), "-lc".to_string(), @@ -1849,7 +1856,7 @@ async fn bare_nested_shell_allow_never_establishes_wrapper_authority() { ); let absolute_wrapper = vec![ - host_program_path("bash"), + posix_script_program_path("bash"), "-lc".to_string(), "echo hello".to_string(), ]; diff --git a/codex-rs/core/src/exec_policy_windows_tests.rs b/codex-rs/core/src/exec_policy_windows_tests.rs index 286efec211..5c6a072a28 100644 --- a/codex-rs/core/src/exec_policy_windows_tests.rs +++ b/codex-rs/core/src/exec_policy_windows_tests.rs @@ -180,31 +180,41 @@ async fn full_outer_allow_does_not_bypass_extended_unsupported_wrapper() { let mut extended = command.clone(); extended.push("trailing-runtime-argument".to_string()); - for (profile, level, permissions, prompts) in [ + for (profile, level, permissions, prompts, offers_amendment) in [ ( PermissionProfile::read_only(), RestrictedToken, UseDefault, false, + true, ), ( PermissionProfile::read_only(), RestrictedToken, RequireEscalated, true, + false, ), ( PermissionProfile::read_only(), RestrictedToken, WithAdditionalPermissions, true, + false, + ), + ( + PermissionProfile::read_only(), + Disabled, + UseDefault, + true, + true, ), - (PermissionProfile::read_only(), Disabled, UseDefault, true), ( PermissionProfile::Disabled, RestrictedToken, UseDefault, false, + true, ), ] { assert_eq!( @@ -218,9 +228,21 @@ async fn full_outer_allow_does_not_bypass_extended_unsupported_wrapper() { ) .await, if prompts { - prompt_outer(&extended) - } else { + if offers_amendment { + prompt_outer(&extended) + } else { + ExecApprovalRequirement::NeedsApproval { + reason: None, + proposed_execpolicy_amendment: None, + } + } + } else if offers_amendment { skip_outer(&extended, false) + } else { + ExecApprovalRequirement::Skip { + bypass_sandbox: false, + proposed_execpolicy_amendment: None, + } }, ); } @@ -296,45 +318,63 @@ async fn outer_authority_tracks_permission_deltas_and_missing_managed_sandbox() NetworkSandboxPolicy::Restricted, ); - for (profile, level, permissions, prompts) in [ + for (profile, level, permissions, prompts, offers_amendment) in [ ( PermissionProfile::read_only(), RestrictedToken, RequireEscalated, true, + false, + ), + ( + PermissionProfile::read_only(), + Disabled, + UseDefault, + true, + true, ), - (PermissionProfile::read_only(), Disabled, UseDefault, true), ( denied_read_profile, RestrictedToken, RequireEscalated, false, + false, ), ( PermissionProfile::read_only(), RestrictedToken, WithAdditionalPermissions, true, + false, ), ( PermissionProfile::Disabled, RestrictedToken, RequireEscalated, false, + false, ), ( PermissionProfile::Disabled, RestrictedToken, WithAdditionalPermissions, false, + false, + ), + (external_profile(), RestrictedToken, UseDefault, false, true), + ( + external_profile(), + RestrictedToken, + RequireEscalated, + true, + false, ), - (external_profile(), RestrictedToken, UseDefault, false), - (external_profile(), RestrictedToken, RequireEscalated, true), ( external_profile(), RestrictedToken, WithAdditionalPermissions, true, + false, ), ] { assert_eq!( @@ -348,9 +388,21 @@ async fn outer_authority_tracks_permission_deltas_and_missing_managed_sandbox() ) .await, if prompts { - prompt_outer(&command) - } else { + if offers_amendment { + prompt_outer(&command) + } else { + ExecApprovalRequirement::NeedsApproval { + reason: None, + proposed_execpolicy_amendment: None, + } + } + } else if offers_amendment { skip_outer(&command, false) + } else { + ExecApprovalRequirement::Skip { + bypass_sandbox: false, + proposed_execpolicy_amendment: None, + } }, ); } diff --git a/codex-rs/core/tests/suite/unified_exec.rs b/codex-rs/core/tests/suite/unified_exec.rs index dd12c99026..61dd3f9171 100644 --- a/codex-rs/core/tests/suite/unified_exec.rs +++ b/codex-rs/core/tests/suite/unified_exec.rs @@ -2333,7 +2333,7 @@ async fn write_stdin_ctrl_c_reports_unsupported_interrupt_to_model_on_windows() let server = start_mock_server().await; - let mut builder = test_codex().with_config(|config| { + let mut builder = test_codex().with_windows_cmd_shell().with_config(|config| { config .features .enable(Feature::UnifiedExec) @@ -2345,7 +2345,6 @@ async fn write_stdin_ctrl_c_reports_unsupported_interrupt_to_model_on_windows() let interrupt_call_id = "uexec-windows-interrupt"; let start_args = serde_json::json!({ - "shell": "cmd", "cmd": "echo READY && ping -n 30 127.0.0.1 >NUL", "yield_time_ms": 250, "tty": false,