From b61c7acb17b25328bce73ab491a251a4773820e1 Mon Sep 17 00:00:00 2001 From: Abhinav Vedmala Date: Sun, 7 Jun 2026 19:46:00 -0700 Subject: [PATCH 1/7] Add Windows managed deny-read regression --- codex-rs/core/tests/suite/windows_sandbox.rs | 147 +++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/codex-rs/core/tests/suite/windows_sandbox.rs b/codex-rs/core/tests/suite/windows_sandbox.rs index 74d7f6a23bb..f4c37da33a6 100644 --- a/codex-rs/core/tests/suite/windows_sandbox.rs +++ b/codex-rs/core/tests/suite/windows_sandbox.rs @@ -1,4 +1,6 @@ use anyhow::Context; +use codex_config::CONFIG_TOML_FILE; +use codex_config::test_support::CloudConfigBundleFixture; use codex_core::exec::ExecCapturePolicy; use codex_core::exec::ExecParams; use codex_core::exec::process_exec_tool_call; @@ -13,13 +15,27 @@ use codex_protocol::permissions::FileSystemSandboxEntry; use codex_protocol::permissions::FileSystemSandboxPolicy; use codex_protocol::permissions::FileSystemSpecialPath; use codex_protocol::permissions::NetworkSandboxPolicy; +use codex_protocol::protocol::EventMsg; +use codex_protocol::protocol::Op; +use codex_protocol::user_input::UserInput; use core_test_support::PathExt; +use core_test_support::responses::ev_assistant_message; +use core_test_support::responses::ev_completed; +use core_test_support::responses::ev_function_call; +use core_test_support::responses::ev_response_created; +use core_test_support::responses::mount_sse_sequence; +use core_test_support::responses::sse; +use core_test_support::responses::start_mock_server; +use core_test_support::test_codex::test_codex; +use core_test_support::wait_for_event; use pretty_assertions::assert_eq; +use serde_json::json; use serial_test::serial; use std::collections::HashMap; use std::ffi::OsString; use std::path::Path; use std::path::PathBuf; +use std::sync::Arc; use tempfile::TempDir; struct EnvVarGuard { @@ -320,3 +336,134 @@ async fn windows_elevated_enforces_deny_read_and_protects_setup_marker() -> anyh ); Ok(()) } + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +#[serial(codex_home)] +async fn windows_elevated_enforces_managed_deny_read_for_shell_subprocess() -> anyhow::Result<()> { + stage_windows_sandbox_helpers()?; + let codex_home = Arc::new(TempDir::new()?); + let _codex_home_guard = EnvVarGuard::set("CODEX_HOME", codex_home.path().as_os_str()); + std::fs::write( + codex_home.path().join(CONFIG_TOML_FILE), + r#"[windows] +sandbox = "elevated" +"#, + )?; + + let protected_dir = TempDir::new()?; + let denied_path = protected_dir.path().join("secret.env"); + let allowed_path = protected_dir.path().join("public.txt"); + std::fs::write(&denied_path, "managed secret\n")?; + std::fs::write(&allowed_path, "public ok\n")?; + let denied_path = dunce::canonicalize(denied_path)?.abs(); + let allowed_path = dunce::canonicalize(allowed_path)?.abs(); + let denied_path_toml = toml::Value::String(denied_path.to_string_lossy().into()).to_string(); + let requirements = format!( + r#"[permissions.filesystem] +deny_read = [{denied_path_toml}] + +[windows] +allowed_sandbox_implementations = ["elevated"] +"# + ); + + let server = start_mock_server().await; + let mut builder = test_codex() + .with_home(Arc::clone(&codex_home)) + .with_cloud_config_bundle( + CloudConfigBundleFixture::loader_with_enterprise_requirement(requirements), + ) + .with_windows_cmd_shell() + .with_config(|config| { + config.permissions.windows_sandbox_private_desktop = false; + }); + let fixture = builder.build(&server).await?; + + assert_eq!( + fixture.config.permissions.windows_sandbox_mode, + Some(codex_config::types::WindowsSandboxModeToml::Elevated) + ); + assert!( + fixture + .session_configured + .permission_profile + .file_system_sandbox_policy() + .entries + .iter() + .any(|entry| { + entry.access == FileSystemAccessMode::Deny + && matches!( + &entry.path, + FileSystemPath::Path { path } if path == &denied_path + ) + }), + "managed deny-read path should reach the configured session" + ); + + let call_id = "managed-deny-read"; + let command = format!( + "(type \"{}\" 1>NUL 2>NUL && echo SECRET-READ || echo SECRET-DENIED) & type \"{}\"", + denied_path.display(), + allowed_path.display() + ); + let args = json!({ + "command": command, + "login": false, + "timeout_ms": 10_000, + }); + let request_log = mount_sse_sequence( + &server, + vec![ + sse(vec![ + ev_response_created("resp-1"), + ev_function_call(call_id, "shell_command", &serde_json::to_string(&args)?), + ev_completed("resp-1"), + ]), + sse(vec![ + ev_assistant_message("msg-1", "done"), + ev_completed("resp-2"), + ]), + ], + ) + .await; + + fixture + .codex + .submit(Op::UserInput { + items: vec![UserInput::Text { + text: "read the fixture files".into(), + text_elements: Vec::new(), + }], + environments: None, + final_output_json_schema: None, + responsesapi_client_metadata: None, + additional_context: Default::default(), + thread_settings: Default::default(), + }) + .await?; + wait_for_event(&fixture.codex, |event| { + matches!(event, EventMsg::TurnComplete(_)) + }) + .await; + + let output = request_log + .function_call_output_text(call_id) + .context("shell output present")?; + assert!( + output.contains("SECRET-DENIED"), + "managed deny-read should block the subprocess read: {output}" + ); + assert!( + !output.contains("SECRET-READ"), + "managed deny-read must not allow the subprocess read: {output}" + ); + assert!( + output.contains("public ok"), + "allowed reads should still work: {output}" + ); + assert!( + !output.contains("managed secret"), + "denied file contents leaked into shell output: {output}" + ); + Ok(()) +} From 5b19b0857180d95194ab414f71c846e81c328b44 Mon Sep 17 00:00:00 2001 From: Abhinav Vedmala Date: Sun, 7 Jun 2026 20:03:42 -0700 Subject: [PATCH 2/7] Use stable Windows sandbox test harness --- codex-rs/core/tests/suite/windows_sandbox.rs | 153 +++++++------------ 1 file changed, 59 insertions(+), 94 deletions(-) diff --git a/codex-rs/core/tests/suite/windows_sandbox.rs b/codex-rs/core/tests/suite/windows_sandbox.rs index f4c37da33a6..6614a63bb75 100644 --- a/codex-rs/core/tests/suite/windows_sandbox.rs +++ b/codex-rs/core/tests/suite/windows_sandbox.rs @@ -15,27 +15,14 @@ use codex_protocol::permissions::FileSystemSandboxEntry; use codex_protocol::permissions::FileSystemSandboxPolicy; use codex_protocol::permissions::FileSystemSpecialPath; use codex_protocol::permissions::NetworkSandboxPolicy; -use codex_protocol::protocol::EventMsg; -use codex_protocol::protocol::Op; -use codex_protocol::user_input::UserInput; use core_test_support::PathExt; -use core_test_support::responses::ev_assistant_message; -use core_test_support::responses::ev_completed; -use core_test_support::responses::ev_function_call; -use core_test_support::responses::ev_response_created; -use core_test_support::responses::mount_sse_sequence; -use core_test_support::responses::sse; -use core_test_support::responses::start_mock_server; -use core_test_support::test_codex::test_codex; -use core_test_support::wait_for_event; +use core_test_support::load_default_config_for_test_with_cloud_config_bundle; use pretty_assertions::assert_eq; -use serde_json::json; use serial_test::serial; use std::collections::HashMap; use std::ffi::OsString; use std::path::Path; use std::path::PathBuf; -use std::sync::Arc; use tempfile::TempDir; struct EnvVarGuard { @@ -341,22 +328,23 @@ async fn windows_elevated_enforces_deny_read_and_protects_setup_marker() -> anyh #[serial(codex_home)] async fn windows_elevated_enforces_managed_deny_read_for_shell_subprocess() -> anyhow::Result<()> { stage_windows_sandbox_helpers()?; - let codex_home = Arc::new(TempDir::new()?); - let _codex_home_guard = EnvVarGuard::set("CODEX_HOME", codex_home.path().as_os_str()); + let sandbox_home = + codex_home_for_windows_sandbox_test("windows-elevated-managed-deny-read-codex-home")?; + let _codex_home_guard = EnvVarGuard::set("CODEX_HOME", sandbox_home.path().as_os_str()); + let config_home = TempDir::new()?; std::fs::write( - codex_home.path().join(CONFIG_TOML_FILE), + config_home.path().join(CONFIG_TOML_FILE), r#"[windows] sandbox = "elevated" "#, )?; - let protected_dir = TempDir::new()?; - let denied_path = protected_dir.path().join("secret.env"); - let allowed_path = protected_dir.path().join("public.txt"); + let workspace = TempDir::new()?; + let cwd = dunce::canonicalize(workspace.path())?.abs(); + let denied_path = cwd.join("secret.env"); + let allowed_path = cwd.join("public.txt"); std::fs::write(&denied_path, "managed secret\n")?; std::fs::write(&allowed_path, "public ok\n")?; - let denied_path = dunce::canonicalize(denied_path)?.abs(); - let allowed_path = dunce::canonicalize(allowed_path)?.abs(); let denied_path_toml = toml::Value::String(denied_path.to_string_lossy().into()).to_string(); let requirements = format!( r#"[permissions.filesystem] @@ -367,26 +355,18 @@ allowed_sandbox_implementations = ["elevated"] "# ); - let server = start_mock_server().await; - let mut builder = test_codex() - .with_home(Arc::clone(&codex_home)) - .with_cloud_config_bundle( - CloudConfigBundleFixture::loader_with_enterprise_requirement(requirements), - ) - .with_windows_cmd_shell() - .with_config(|config| { - config.permissions.windows_sandbox_private_desktop = false; - }); - let fixture = builder.build(&server).await?; - + let config = load_default_config_for_test_with_cloud_config_bundle( + &config_home, + CloudConfigBundleFixture::loader_with_enterprise_requirement(requirements), + ) + .await; assert_eq!( - fixture.config.permissions.windows_sandbox_mode, + config.permissions.windows_sandbox_mode, Some(codex_config::types::WindowsSandboxModeToml::Elevated) ); + let permission_profile = config.permissions.effective_permission_profile(); assert!( - fixture - .session_configured - .permission_profile + permission_profile .file_system_sandbox_policy() .entries .iter() @@ -397,73 +377,58 @@ allowed_sandbox_implementations = ["elevated"] FileSystemPath::Path { path } if path == &denied_path ) }), - "managed deny-read path should reach the configured session" + "managed deny-read path should reach the runtime permission profile" ); - let call_id = "managed-deny-read"; - let command = format!( - "(type \"{}\" 1>NUL 2>NUL && echo SECRET-READ || echo SECRET-DENIED) & type \"{}\"", - denied_path.display(), - allowed_path.display() - ); - let args = json!({ - "command": command, - "login": false, - "timeout_ms": 10_000, - }); - let request_log = mount_sse_sequence( - &server, - vec![ - sse(vec![ - ev_response_created("resp-1"), - ev_function_call(call_id, "shell_command", &serde_json::to_string(&args)?), - ev_completed("resp-1"), - ]), - sse(vec![ - ev_assistant_message("msg-1", "done"), - ev_completed("resp-2"), - ]), - ], + let ExecToolCallOutput { + exit_code, + stdout, + .. + } = process_exec_tool_call( + ExecParams { + command: vec![ + "cmd.exe".to_string(), + "/D".to_string(), + "/C".to_string(), + "(type secret.env 1>NUL 2>NUL && echo SECRET-READ || echo SECRET-DENIED) & type public.txt" + .to_string(), + ], + cwd: cwd.clone(), + expiration: 10_000.into(), + capture_policy: ExecCapturePolicy::ShellTool, + env: HashMap::new(), + network: None, + sandbox_permissions: SandboxPermissions::UseDefault, + windows_sandbox_level: WindowsSandboxLevel::Elevated, + windows_sandbox_private_desktop: false, + justification: None, + arg0: None, + }, + &permission_profile, + &cwd, + std::slice::from_ref(&cwd), + &None, + /*use_legacy_landlock*/ false, + /*stdout_stream*/ None, ) - .await; - - fixture - .codex - .submit(Op::UserInput { - items: vec![UserInput::Text { - text: "read the fixture files".into(), - text_elements: Vec::new(), - }], - environments: None, - final_output_json_schema: None, - responsesapi_client_metadata: None, - additional_context: Default::default(), - thread_settings: Default::default(), - }) - .await?; - wait_for_event(&fixture.codex, |event| { - matches!(event, EventMsg::TurnComplete(_)) - }) - .await; + .await?; - let output = request_log - .function_call_output_text(call_id) - .context("shell output present")?; + assert_eq!(exit_code, 0, "sandboxed command should complete"); assert!( - output.contains("SECRET-DENIED"), - "managed deny-read should block the subprocess read: {output}" + stdout.text.contains("SECRET-DENIED"), + "managed deny-read should block the subprocess read: {stdout:?}" ); assert!( - !output.contains("SECRET-READ"), - "managed deny-read must not allow the subprocess read: {output}" + !stdout.text.contains("SECRET-READ"), + "managed deny-read must not allow the subprocess read: {stdout:?}" ); assert!( - output.contains("public ok"), - "allowed reads should still work: {output}" + stdout.text.contains("public ok"), + "allowed reads should still work: {stdout:?}" ); assert!( - !output.contains("managed secret"), - "denied file contents leaked into shell output: {output}" + !stdout.text.contains("managed secret"), + "denied file contents leaked into shell output: {stdout:?}" ); Ok(()) } From bd3b85eb3d5dcbc8c555333a83402cb71979feed Mon Sep 17 00:00:00 2001 From: Abhinav Vedmala Date: Sun, 7 Jun 2026 20:22:14 -0700 Subject: [PATCH 3/7] Materialize Windows sandbox test workspace --- codex-rs/core/tests/suite/windows_sandbox.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/codex-rs/core/tests/suite/windows_sandbox.rs b/codex-rs/core/tests/suite/windows_sandbox.rs index 6614a63bb75..d3eabe12b77 100644 --- a/codex-rs/core/tests/suite/windows_sandbox.rs +++ b/codex-rs/core/tests/suite/windows_sandbox.rs @@ -334,7 +334,9 @@ async fn windows_elevated_enforces_managed_deny_read_for_shell_subprocess() -> a let config_home = TempDir::new()?; std::fs::write( config_home.path().join(CONFIG_TOML_FILE), - r#"[windows] + r#"sandbox_mode = "workspace-write" + +[windows] sandbox = "elevated" "#, )?; @@ -355,11 +357,12 @@ allowed_sandbox_implementations = ["elevated"] "# ); - let config = load_default_config_for_test_with_cloud_config_bundle( + let mut config = load_default_config_for_test_with_cloud_config_bundle( &config_home, CloudConfigBundleFixture::loader_with_enterprise_requirement(requirements), ) .await; + config.permissions.set_workspace_roots(vec![cwd.clone()]); assert_eq!( config.permissions.windows_sandbox_mode, Some(codex_config::types::WindowsSandboxModeToml::Elevated) From e211c2b57fde0572395e58990a753ea6770bda60 Mon Sep 17 00:00:00 2001 From: Abhinav Vedmala Date: Sun, 7 Jun 2026 20:39:42 -0700 Subject: [PATCH 4/7] Revert "Materialize Windows sandbox test workspace" This reverts commit bd3b85eb3d5dcbc8c555333a83402cb71979feed. --- codex-rs/core/tests/suite/windows_sandbox.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/codex-rs/core/tests/suite/windows_sandbox.rs b/codex-rs/core/tests/suite/windows_sandbox.rs index d3eabe12b77..6614a63bb75 100644 --- a/codex-rs/core/tests/suite/windows_sandbox.rs +++ b/codex-rs/core/tests/suite/windows_sandbox.rs @@ -334,9 +334,7 @@ async fn windows_elevated_enforces_managed_deny_read_for_shell_subprocess() -> a let config_home = TempDir::new()?; std::fs::write( config_home.path().join(CONFIG_TOML_FILE), - r#"sandbox_mode = "workspace-write" - -[windows] + r#"[windows] sandbox = "elevated" "#, )?; @@ -357,12 +355,11 @@ allowed_sandbox_implementations = ["elevated"] "# ); - let mut config = load_default_config_for_test_with_cloud_config_bundle( + let config = load_default_config_for_test_with_cloud_config_bundle( &config_home, CloudConfigBundleFixture::loader_with_enterprise_requirement(requirements), ) .await; - config.permissions.set_workspace_roots(vec![cwd.clone()]); assert_eq!( config.permissions.windows_sandbox_mode, Some(codex_config::types::WindowsSandboxModeToml::Elevated) From cf7cb65446366a15058a9a53203ec0ced73e7d3e Mon Sep 17 00:00:00 2001 From: Abhinav Vedmala Date: Sun, 7 Jun 2026 20:39:42 -0700 Subject: [PATCH 5/7] Revert "Use stable Windows sandbox test harness" This reverts commit 5b19b0857180d95194ab414f71c846e81c328b44. --- codex-rs/core/tests/suite/windows_sandbox.rs | 153 ++++++++++++------- 1 file changed, 94 insertions(+), 59 deletions(-) diff --git a/codex-rs/core/tests/suite/windows_sandbox.rs b/codex-rs/core/tests/suite/windows_sandbox.rs index 6614a63bb75..f4c37da33a6 100644 --- a/codex-rs/core/tests/suite/windows_sandbox.rs +++ b/codex-rs/core/tests/suite/windows_sandbox.rs @@ -15,14 +15,27 @@ use codex_protocol::permissions::FileSystemSandboxEntry; use codex_protocol::permissions::FileSystemSandboxPolicy; use codex_protocol::permissions::FileSystemSpecialPath; use codex_protocol::permissions::NetworkSandboxPolicy; +use codex_protocol::protocol::EventMsg; +use codex_protocol::protocol::Op; +use codex_protocol::user_input::UserInput; use core_test_support::PathExt; -use core_test_support::load_default_config_for_test_with_cloud_config_bundle; +use core_test_support::responses::ev_assistant_message; +use core_test_support::responses::ev_completed; +use core_test_support::responses::ev_function_call; +use core_test_support::responses::ev_response_created; +use core_test_support::responses::mount_sse_sequence; +use core_test_support::responses::sse; +use core_test_support::responses::start_mock_server; +use core_test_support::test_codex::test_codex; +use core_test_support::wait_for_event; use pretty_assertions::assert_eq; +use serde_json::json; use serial_test::serial; use std::collections::HashMap; use std::ffi::OsString; use std::path::Path; use std::path::PathBuf; +use std::sync::Arc; use tempfile::TempDir; struct EnvVarGuard { @@ -328,23 +341,22 @@ async fn windows_elevated_enforces_deny_read_and_protects_setup_marker() -> anyh #[serial(codex_home)] async fn windows_elevated_enforces_managed_deny_read_for_shell_subprocess() -> anyhow::Result<()> { stage_windows_sandbox_helpers()?; - let sandbox_home = - codex_home_for_windows_sandbox_test("windows-elevated-managed-deny-read-codex-home")?; - let _codex_home_guard = EnvVarGuard::set("CODEX_HOME", sandbox_home.path().as_os_str()); - let config_home = TempDir::new()?; + let codex_home = Arc::new(TempDir::new()?); + let _codex_home_guard = EnvVarGuard::set("CODEX_HOME", codex_home.path().as_os_str()); std::fs::write( - config_home.path().join(CONFIG_TOML_FILE), + codex_home.path().join(CONFIG_TOML_FILE), r#"[windows] sandbox = "elevated" "#, )?; - let workspace = TempDir::new()?; - let cwd = dunce::canonicalize(workspace.path())?.abs(); - let denied_path = cwd.join("secret.env"); - let allowed_path = cwd.join("public.txt"); + let protected_dir = TempDir::new()?; + let denied_path = protected_dir.path().join("secret.env"); + let allowed_path = protected_dir.path().join("public.txt"); std::fs::write(&denied_path, "managed secret\n")?; std::fs::write(&allowed_path, "public ok\n")?; + let denied_path = dunce::canonicalize(denied_path)?.abs(); + let allowed_path = dunce::canonicalize(allowed_path)?.abs(); let denied_path_toml = toml::Value::String(denied_path.to_string_lossy().into()).to_string(); let requirements = format!( r#"[permissions.filesystem] @@ -355,18 +367,26 @@ allowed_sandbox_implementations = ["elevated"] "# ); - let config = load_default_config_for_test_with_cloud_config_bundle( - &config_home, - CloudConfigBundleFixture::loader_with_enterprise_requirement(requirements), - ) - .await; + let server = start_mock_server().await; + let mut builder = test_codex() + .with_home(Arc::clone(&codex_home)) + .with_cloud_config_bundle( + CloudConfigBundleFixture::loader_with_enterprise_requirement(requirements), + ) + .with_windows_cmd_shell() + .with_config(|config| { + config.permissions.windows_sandbox_private_desktop = false; + }); + let fixture = builder.build(&server).await?; + assert_eq!( - config.permissions.windows_sandbox_mode, + fixture.config.permissions.windows_sandbox_mode, Some(codex_config::types::WindowsSandboxModeToml::Elevated) ); - let permission_profile = config.permissions.effective_permission_profile(); assert!( - permission_profile + fixture + .session_configured + .permission_profile .file_system_sandbox_policy() .entries .iter() @@ -377,58 +397,73 @@ allowed_sandbox_implementations = ["elevated"] FileSystemPath::Path { path } if path == &denied_path ) }), - "managed deny-read path should reach the runtime permission profile" + "managed deny-read path should reach the configured session" ); - let ExecToolCallOutput { - exit_code, - stdout, - .. - } = process_exec_tool_call( - ExecParams { - command: vec![ - "cmd.exe".to_string(), - "/D".to_string(), - "/C".to_string(), - "(type secret.env 1>NUL 2>NUL && echo SECRET-READ || echo SECRET-DENIED) & type public.txt" - .to_string(), - ], - cwd: cwd.clone(), - expiration: 10_000.into(), - capture_policy: ExecCapturePolicy::ShellTool, - env: HashMap::new(), - network: None, - sandbox_permissions: SandboxPermissions::UseDefault, - windows_sandbox_level: WindowsSandboxLevel::Elevated, - windows_sandbox_private_desktop: false, - justification: None, - arg0: None, - }, - &permission_profile, - &cwd, - std::slice::from_ref(&cwd), - &None, - /*use_legacy_landlock*/ false, - /*stdout_stream*/ None, + let call_id = "managed-deny-read"; + let command = format!( + "(type \"{}\" 1>NUL 2>NUL && echo SECRET-READ || echo SECRET-DENIED) & type \"{}\"", + denied_path.display(), + allowed_path.display() + ); + let args = json!({ + "command": command, + "login": false, + "timeout_ms": 10_000, + }); + let request_log = mount_sse_sequence( + &server, + vec![ + sse(vec![ + ev_response_created("resp-1"), + ev_function_call(call_id, "shell_command", &serde_json::to_string(&args)?), + ev_completed("resp-1"), + ]), + sse(vec![ + ev_assistant_message("msg-1", "done"), + ev_completed("resp-2"), + ]), + ], ) - .await?; + .await; - assert_eq!(exit_code, 0, "sandboxed command should complete"); + fixture + .codex + .submit(Op::UserInput { + items: vec![UserInput::Text { + text: "read the fixture files".into(), + text_elements: Vec::new(), + }], + environments: None, + final_output_json_schema: None, + responsesapi_client_metadata: None, + additional_context: Default::default(), + thread_settings: Default::default(), + }) + .await?; + wait_for_event(&fixture.codex, |event| { + matches!(event, EventMsg::TurnComplete(_)) + }) + .await; + + let output = request_log + .function_call_output_text(call_id) + .context("shell output present")?; assert!( - stdout.text.contains("SECRET-DENIED"), - "managed deny-read should block the subprocess read: {stdout:?}" + output.contains("SECRET-DENIED"), + "managed deny-read should block the subprocess read: {output}" ); assert!( - !stdout.text.contains("SECRET-READ"), - "managed deny-read must not allow the subprocess read: {stdout:?}" + !output.contains("SECRET-READ"), + "managed deny-read must not allow the subprocess read: {output}" ); assert!( - stdout.text.contains("public ok"), - "allowed reads should still work: {stdout:?}" + output.contains("public ok"), + "allowed reads should still work: {output}" ); assert!( - !stdout.text.contains("managed secret"), - "denied file contents leaked into shell output: {stdout:?}" + !output.contains("managed secret"), + "denied file contents leaked into shell output: {output}" ); Ok(()) } From 6196a0126da923f27691c264a8c5d67cec6e844c Mon Sep 17 00:00:00 2001 From: Abhinav Vedmala Date: Sun, 7 Jun 2026 20:40:08 -0700 Subject: [PATCH 6/7] Exercise managed deny read through shell tool --- codex-rs/core/tests/suite/windows_sandbox.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/codex-rs/core/tests/suite/windows_sandbox.rs b/codex-rs/core/tests/suite/windows_sandbox.rs index f4c37da33a6..c7f8ed52827 100644 --- a/codex-rs/core/tests/suite/windows_sandbox.rs +++ b/codex-rs/core/tests/suite/windows_sandbox.rs @@ -345,7 +345,9 @@ async fn windows_elevated_enforces_managed_deny_read_for_shell_subprocess() -> a let _codex_home_guard = EnvVarGuard::set("CODEX_HOME", codex_home.path().as_os_str()); std::fs::write( codex_home.path().join(CONFIG_TOML_FILE), - r#"[windows] + r#"sandbox_mode = "workspace-write" + +[windows] sandbox = "elevated" "#, )?; From 3694ff376afeb91dd464a03f750f9f3f73162064 Mon Sep 17 00:00:00 2001 From: Abhinav Vedmala Date: Sun, 7 Jun 2026 20:57:52 -0700 Subject: [PATCH 7/7] Focus Windows regression on denied read --- codex-rs/core/tests/suite/windows_sandbox.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/codex-rs/core/tests/suite/windows_sandbox.rs b/codex-rs/core/tests/suite/windows_sandbox.rs index c7f8ed52827..e2565903636 100644 --- a/codex-rs/core/tests/suite/windows_sandbox.rs +++ b/codex-rs/core/tests/suite/windows_sandbox.rs @@ -354,11 +354,8 @@ sandbox = "elevated" let protected_dir = TempDir::new()?; let denied_path = protected_dir.path().join("secret.env"); - let allowed_path = protected_dir.path().join("public.txt"); std::fs::write(&denied_path, "managed secret\n")?; - std::fs::write(&allowed_path, "public ok\n")?; let denied_path = dunce::canonicalize(denied_path)?.abs(); - let allowed_path = dunce::canonicalize(allowed_path)?.abs(); let denied_path_toml = toml::Value::String(denied_path.to_string_lossy().into()).to_string(); let requirements = format!( r#"[permissions.filesystem] @@ -404,9 +401,8 @@ allowed_sandbox_implementations = ["elevated"] let call_id = "managed-deny-read"; let command = format!( - "(type \"{}\" 1>NUL 2>NUL && echo SECRET-READ || echo SECRET-DENIED) & type \"{}\"", - denied_path.display(), - allowed_path.display() + "type \"{}\" 1>NUL 2>NUL && echo SECRET-READ || echo SECRET-DENIED", + denied_path.display() ); let args = json!({ "command": command, @@ -459,10 +455,6 @@ allowed_sandbox_implementations = ["elevated"] !output.contains("SECRET-READ"), "managed deny-read must not allow the subprocess read: {output}" ); - assert!( - output.contains("public ok"), - "allowed reads should still work: {output}" - ); assert!( !output.contains("managed secret"), "denied file contents leaked into shell output: {output}"