From 43354d0f61c1bda3eff26decaf13276bb57039e7 Mon Sep 17 00:00:00 2001 From: open-matt Date: Wed, 16 Sep 2026 18:10:11 +0000 Subject: [PATCH] Preserve attachment Unix socket grants when controller policy is omitted (#46004) ## Why An omitted controller socket policy was treated as an explicit denial, preventing execution environments from supplying their own Unix socket grants. ## What changed - Preserve omission of `dangerously_allow_all_unix_sockets` separately from `false`, and retain explicitly empty `unix_sockets` maps. - Defer to attachment socket permissions when the controller supplies neither setting. Continue enforcing explicit restrictions, socket denials, and managed requirements. - Resolve omitted values to `false` for ordinary execution and remote configuration, preserving the default for commands without attachment grants. - Add debug logging for effective environment and remote execution network policies. ## Testing Add regression coverage for omitted, explicit, finite, empty, and managed socket policies through remote launch configuration, including live policy replacement and serialization round trips. Adjust remote environment tests to tolerate child-completion ordering and box large cold-resume test futures to reduce Windows stack usage. GitOrigin-RevId: 98a88d01cbb91aea1faaa03b3c414ef3be7af398 --- codex-rs/config/src/permissions_toml.rs | 5 +- .../core/src/config/network_proxy_spec.rs | 11 +- .../src/config/network_proxy_spec_tests.rs | 132 +++++++++++++++++- codex-rs/core/tests/suite/remote_env.rs | 11 ++ .../suite/managed_proxy_unix_sockets_tests.rs | 2 +- codex-rs/network-proxy/src/config.rs | 18 +-- .../network-proxy/src/environment_policy.rs | 14 +- codex-rs/network-proxy/src/proxy.rs | 25 +++- codex-rs/network-proxy/src/remote_config.rs | 6 +- .../network-proxy/src/remote_config_tests.rs | 32 ++++- codex-rs/network-proxy/src/runtime.rs | 18 ++- codex-rs/network-proxy/src/state.rs | 2 +- codex-rs/sandboxing/src/manager_tests.rs | 2 +- codex-rs/sandboxing/src/seatbelt_tests.rs | 2 +- codex-rs/tui/src/app/agents_overview_tests.rs | 29 ++-- 15 files changed, 258 insertions(+), 51 deletions(-) diff --git a/codex-rs/config/src/permissions_toml.rs b/codex-rs/config/src/permissions_toml.rs index 98fd3534d6..48c54ef71c 100644 --- a/codex-rs/config/src/permissions_toml.rs +++ b/codex-rs/config/src/permissions_toml.rs @@ -493,7 +493,7 @@ impl NetworkToml { config.dangerously_allow_non_loopback_proxy = dangerously_allow_non_loopback_proxy; } if let Some(dangerously_allow_all_unix_sockets) = self.dangerously_allow_all_unix_sockets { - config.dangerously_allow_all_unix_sockets = dangerously_allow_all_unix_sockets; + config.dangerously_allow_all_unix_sockets = Some(dangerously_allow_all_unix_sockets); } if let Some(mode) = self.mode { config.mode = mode; @@ -512,8 +512,7 @@ impl NetworkToml { }; proxy_unix_sockets.entries.insert(path.clone(), permission); } - config.unix_sockets = - (!proxy_unix_sockets.entries.is_empty()).then_some(proxy_unix_sockets); + config.unix_sockets = Some(proxy_unix_sockets); } if let Some(allow_local_binding) = self.allow_local_binding { config.allow_local_binding = allow_local_binding; diff --git a/codex-rs/core/src/config/network_proxy_spec.rs b/codex-rs/core/src/config/network_proxy_spec.rs index f29beb47e9..a195ff5f44 100644 --- a/codex-rs/core/src/config/network_proxy_spec.rs +++ b/codex-rs/core/src/config/network_proxy_spec.rs @@ -266,7 +266,7 @@ impl NetworkProxySpec { NetworkProxyConfig { enabled: true, // Without a controller, the owner supplies the entire permission ceiling. - dangerously_allow_all_unix_sockets: true, + dangerously_allow_all_unix_sockets: Some(true), allow_local_binding: true, ..NetworkProxyConfig::default() }, @@ -308,6 +308,13 @@ impl NetworkProxySpec { format!("environment network policy violates managed requirements: {error}"), ) })?; + tracing::debug!( + controller_policy = ?controller.map(Self::environment_policy), + controller_allow_all_unix_sockets = ?controller.and_then(|spec| spec.config.dangerously_allow_all_unix_sockets), + attachment_policy = ?policy, + effective_policy = ?spec.environment_policy(), + "resolved environment network policy" + ); Ok(spec) } @@ -394,7 +401,7 @@ impl NetworkProxySpec { if let Some(dangerously_allow_all_unix_sockets) = requirements.dangerously_allow_all_unix_sockets { - config.dangerously_allow_all_unix_sockets = dangerously_allow_all_unix_sockets; + config.dangerously_allow_all_unix_sockets = Some(dangerously_allow_all_unix_sockets); constraints.dangerously_allow_all_unix_sockets = Some(dangerously_allow_all_unix_sockets); } diff --git a/codex-rs/core/src/config/network_proxy_spec_tests.rs b/codex-rs/core/src/config/network_proxy_spec_tests.rs index 0f3506cac0..b4a2f55be2 100644 --- a/codex-rs/core/src/config/network_proxy_spec_tests.rs +++ b/codex-rs/core/src/config/network_proxy_spec_tests.rs @@ -24,6 +24,134 @@ fn domain_permissions( } } +#[tokio::test] +async fn attachment_socket_grants_respect_configured_restrictions_at_remote_launch() +-> anyhow::Result<()> { + let profile = PermissionProfile::workspace_write(); + let owner = EnvironmentNetworkPolicy::from_config( + &NetworkProxyConfig { + dangerously_allow_all_unix_sockets: Some(true), + ..Default::default() + }, + /*managed_allowed_domains_only*/ false, + ); + for (name, toml, required, expected) in [ + ("omitted", "", None, true), + ( + "explicit_deny", + "dangerously_allow_all_unix_sockets = false", + None, + false, + ), + ( + "explicit_allow", + "dangerously_allow_all_unix_sockets = true", + None, + true, + ), + ( + "finite", + "unix_sockets = { '/tmp/allowed.sock' = 'allow', '/tmp/denied.sock' = 'deny' }", + None, + false, + ), + ("empty", "unix_sockets = {}", None, false), + ( + "managed_deny", + "dangerously_allow_all_unix_sockets = true", + Some(false), + false, + ), + ] { + let configured: codex_config::permissions_toml::NetworkToml = toml::from_str(toml)?; + let controller = NetworkProxySpec::from_config_and_constraints( + configured.to_network_proxy_config(), + Some(NetworkConstraints { + enabled: Some(true), + dangerously_allow_all_unix_sockets: required, + ..Default::default() + }), + &profile, + )?; + let composed = NetworkProxySpec::for_environment( + Some(&controller), + &owner, + &profile, + &Policy::empty(), + )?; + assert_eq!( + composed.config.dangerously_allow_all_unix_sockets, + Some(expected), + "{name}" + ); + let state = Arc::new(controller.build_state_with_audit_metadata(Default::default())?); + let proxy = NetworkProxy::builder() + .state(Arc::clone(&state)) + .managed_by_codex(false) + .build() + .await?; + let scoped = proxy.for_execution( + "socket-test", + name, + format!("socket-{name}"), + Some(composed.environment_policy()), + /*fallback_policy_decider*/ None, + )?; + let launch = scoped.remote_launch_config().await?; + assert_eq!( + launch.proxy.dangerously_allow_all_unix_sockets, expected, + "{name}" + ); + assert_eq!( + launch.proxy.unix_sockets, composed.config.unix_sockets, + "{name}" + ); + if name == "finite" { + assert_eq!(launch.proxy.unix_sockets, controller.config.unix_sockets); + } + if name == "omitted" { + // Inheritance must not grant sockets to ordinary controller-only commands. + assert!( + !proxy + .remote_launch_config() + .await? + .proxy + .dangerously_allow_all_unix_sockets + ); + state.add_allowed_domain("granted.example").await?; + assert!( + scoped + .remote_launch_config() + .await? + .proxy + .dangerously_allow_all_unix_sockets + ); + + // A live handle must also honor a subsequently installed explicit restriction. + let restricted = NetworkProxySpec::from_config_and_constraints( + NetworkProxyConfig { + enabled: true, + dangerously_allow_all_unix_sockets: Some(false), + ..Default::default() + }, + /*requirements*/ None, + &profile, + )?; + proxy + .replace_config_state(restricted.build_config_state_for_spec()?) + .await?; + assert!( + !scoped + .remote_launch_config() + .await? + .proxy + .dangerously_allow_all_unix_sockets + ); + } + } + Ok(()) +} + #[test] fn build_state_with_audit_metadata_threads_metadata_to_state() { let spec = NetworkProxySpec { @@ -119,7 +247,7 @@ fn environment_policy_replaces_soft_controller_allowlist_and_preserves_denials() "/private/tmp/controller.sock".to_string(), "/tmp/allowed.sock".to_string(), ]); - owner.dangerously_allow_all_unix_sockets = true; + owner.dangerously_allow_all_unix_sockets = Some(true); owner.allow_local_binding = true; let owner_policy = EnvironmentNetworkPolicy::from_config(&owner, /*managed_allowed_domains_only*/ false); @@ -162,7 +290,7 @@ fn environment_policy_replaces_soft_controller_allowlist_and_preserves_denials() ); owner.unix_sockets.clone_from(&spec.config.unix_sockets); owner.allow_upstream_proxy = false; - owner.dangerously_allow_all_unix_sockets = false; + owner.dangerously_allow_all_unix_sockets = Some(false); owner.allow_local_binding = false; assert_eq!( restricted.environment_policy(), diff --git a/codex-rs/core/tests/suite/remote_env.rs b/codex-rs/core/tests/suite/remote_env.rs index 78c3d5ed3b..f305233450 100644 --- a/codex-rs/core/tests/suite/remote_env.rs +++ b/codex-rs/core/tests/suite/remote_env.rs @@ -2383,6 +2383,17 @@ async fn deferred_executor_spawn_agent_inherits_ready_step_environments( ], ) .await; + // If the child finishes after the parent's last request, its completion + // notification triggers one more parent request. Allow either ordering. + mount_sse_once( + &server, + sse(vec![ + ev_response_created("resp-child-completion"), + ev_assistant_message("msg-child-completion", "done"), + ev_completed("resp-child-completion"), + ]), + ) + .await; let mut builder = test_codex() .with_exec_server_url(format!("ws://{}", listener.local_addr()?)) .with_config(move |config| { diff --git a/codex-rs/linux-sandbox/tests/suite/managed_proxy_unix_sockets_tests.rs b/codex-rs/linux-sandbox/tests/suite/managed_proxy_unix_sockets_tests.rs index 24b4b748fe..f8f26ca3c7 100644 --- a/codex-rs/linux-sandbox/tests/suite/managed_proxy_unix_sockets_tests.rs +++ b/codex-rs/linux-sandbox/tests/suite/managed_proxy_unix_sockets_tests.rs @@ -134,7 +134,7 @@ for family in (socket.AF_NETLINK, getattr(socket, 'AF_VSOCK', 40)): let mut config = NetworkProxyConfig { enabled: true, proxy_url: "http://127.0.0.1:9".to_string(), - dangerously_allow_all_unix_sockets, + dangerously_allow_all_unix_sockets: Some(dangerously_allow_all_unix_sockets), ..Default::default() }; if allow_path { diff --git a/codex-rs/network-proxy/src/config.rs b/codex-rs/network-proxy/src/config.rs index 0a880282ed..587318a845 100644 --- a/codex-rs/network-proxy/src/config.rs +++ b/codex-rs/network-proxy/src/config.rs @@ -126,8 +126,9 @@ pub struct NetworkProxyConfig { pub allow_upstream_proxy: bool, #[serde(default)] pub dangerously_allow_non_loopback_proxy: bool, - #[serde(default)] - pub dangerously_allow_all_unix_sockets: bool, + /// When no socket map is set, omission defers to attachment policy; execution defaults to false. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub dangerously_allow_all_unix_sockets: Option, #[serde(default)] pub mode: NetworkMode, #[serde(default)] @@ -166,7 +167,7 @@ impl Default for NetworkProxyConfig { enable_socks5_udp: true, allow_upstream_proxy: true, dangerously_allow_non_loopback_proxy: false, - dangerously_allow_all_unix_sockets: false, + dangerously_allow_all_unix_sockets: None, mode: NetworkMode::default(), domains: None, unix_sockets: None, @@ -345,7 +346,7 @@ impl NetworkProxyConfig { for entry in entries { unix_sockets.entries.insert(entry, permission); } - self.unix_sockets = (!unix_sockets.entries.is_empty()).then_some(unix_sockets); + self.unix_sockets = Some(unix_sockets); } } @@ -428,7 +429,9 @@ pub(crate) fn clamp_bind_addrs( "SOCKS5 proxy", "dangerously_allow_non_loopback_proxy", ); - if cfg.allow_unix_sockets().is_empty() && !cfg.dangerously_allow_all_unix_sockets { + if cfg.allow_unix_sockets().is_empty() + && !cfg.dangerously_allow_all_unix_sockets.unwrap_or(false) + { return (http_addr, socks_addr); } @@ -694,7 +697,7 @@ mod tests { enable_socks5_udp: true, allow_upstream_proxy: true, dangerously_allow_non_loopback_proxy: false, - dangerously_allow_all_unix_sockets: false, + dangerously_allow_all_unix_sockets: None, mode: NetworkMode::Full, domains: None, unix_sockets: None, @@ -888,7 +891,6 @@ mod tests { "enable_socks5_udp": true, "allow_upstream_proxy": true, "dangerously_allow_non_loopback_proxy": false, - "dangerously_allow_all_unix_sockets": false, "mode": "full", "domains": { "example.com": "deny", @@ -1070,7 +1072,7 @@ mod tests { fn clamp_bind_addrs_forces_loopback_when_all_unix_sockets_enabled() { let cfg = NetworkProxyConfig { dangerously_allow_non_loopback_proxy: true, - dangerously_allow_all_unix_sockets: true, + dangerously_allow_all_unix_sockets: Some(true), ..Default::default() }; let http_addr = "0.0.0.0:3128".parse::().unwrap(); diff --git a/codex-rs/network-proxy/src/environment_policy.rs b/codex-rs/network-proxy/src/environment_policy.rs index 365f7b2690..c375a90553 100644 --- a/codex-rs/network-proxy/src/environment_policy.rs +++ b/codex-rs/network-proxy/src/environment_policy.rs @@ -27,7 +27,9 @@ impl EnvironmentNetworkPolicy { domains: config.domains.clone(), unix_sockets: config.unix_sockets.clone(), allow_upstream_proxy: config.allow_upstream_proxy, - dangerously_allow_all_unix_sockets: config.dangerously_allow_all_unix_sockets, + dangerously_allow_all_unix_sockets: config + .dangerously_allow_all_unix_sockets + .unwrap_or(false), allow_local_binding: config.allow_local_binding, managed_allowed_domains_only, } @@ -45,11 +47,16 @@ impl EnvironmentNetworkPolicy { crate::normalize_host, ); } + // An omitted controller policy leaves socket permissions to the attachment; + // an explicitly empty socket map still supplies a restrictive ceiling. + let inherited_allow_all = config + .dangerously_allow_all_unix_sockets + .unwrap_or(config.unix_sockets.is_none()); let inherited_sockets = config.unix_sockets.take().unwrap_or_default(); let mut effective_sockets = self.unix_sockets.clone().unwrap_or_default(); // "Allow all" cannot override a socket denied by either policy. - let inherited_permits_all = config.dangerously_allow_all_unix_sockets + let inherited_permits_all = inherited_allow_all && !inherited_sockets .entries .values() @@ -77,7 +84,8 @@ impl EnvironmentNetworkPolicy { // Enable permissions only when both controller and owner allow them. config.unix_sockets = (!effective_sockets.entries.is_empty()).then_some(effective_sockets); - config.dangerously_allow_all_unix_sockets = inherited_permits_all && owner_permits_all; + config.dangerously_allow_all_unix_sockets = + Some(inherited_permits_all && owner_permits_all); config.allow_upstream_proxy &= self.allow_upstream_proxy; config.allow_local_binding &= self.allow_local_binding; } diff --git a/codex-rs/network-proxy/src/proxy.rs b/codex-rs/network-proxy/src/proxy.rs index e8dc40864d..ab7d398b41 100644 --- a/codex-rs/network-proxy/src/proxy.rs +++ b/codex-rs/network-proxy/src/proxy.rs @@ -228,7 +228,7 @@ impl NetworkProxyBuilder { #[cfg(target_os = "windows")] let (current_cfg, runtime_settings, mut windows_ingress) = { let current_cfg = config::NetworkProxyConfig { - dangerously_allow_all_unix_sockets: false, + dangerously_allow_all_unix_sockets: Some(false), unix_sockets: None, ..current_cfg }; @@ -461,7 +461,7 @@ impl NetworkProxyRuntimeSettings { config.allow_unix_sockets().into() }, dangerously_allow_all_unix_sockets: !cfg!(target_os = "windows") - && config.dangerously_allow_all_unix_sockets, + && config.dangerously_allow_all_unix_sockets.unwrap_or(false), mitm_ca_trust_bundle, }) } @@ -940,6 +940,20 @@ impl NetworkProxy { }; broker_only_config.set_credential_broker_enabled(/*enabled*/ true); broker_only_config.set_allowed_domains(vec!["*".to_string()]); + // Omission and explicit false have the same effective broker-only permissions. + if !config.dangerously_allow_all_unix_sockets.unwrap_or(false) { + broker_only_config.dangerously_allow_all_unix_sockets = + config.dangerously_allow_all_unix_sockets; + } + if config + .unix_sockets + .as_ref() + .is_some_and(|sockets| sockets.entries.is_empty()) + { + broker_only_config + .unix_sockets + .clone_from(&config.unix_sockets); + } let broker_only = brokerage_created_default_allowlist && config == broker_only_config; let environment_policy = self @@ -962,6 +976,7 @@ impl NetworkProxy { "environment network policy requires an enabled executor proxy" ); let proxy = crate::RemoteNetworkProxyConfig::from_effective_config(&config)?; + tracing::debug!(network_config = ?proxy, "resolved remote execution network configuration"); let (environment_id, execution_id) = self .execution_scope .as_ref() @@ -1774,7 +1789,7 @@ mod tests { proxy_url: "http://0.0.0.0:3128".to_string(), socks_url: "http://0.0.0.0:8081".to_string(), dangerously_allow_non_loopback_proxy: true, - dangerously_allow_all_unix_sockets: true, + dangerously_allow_all_unix_sockets: Some(true), unix_sockets: Some(unix_sockets.clone()), ..NetworkProxyConfig::default() }; @@ -2153,7 +2168,7 @@ mod tests { .await?; for allow_all in [false, true] { - config.dangerously_allow_all_unix_sockets = allow_all; + config.dangerously_allow_all_unix_sockets = Some(allow_all); let replacement = crate::state::build_config_state(config.clone(), Default::default())?; proxy.replace_config_state(replacement).await?; let prepared = proxy @@ -2310,8 +2325,10 @@ mod tests { let _permit = WINDOWS_INGRESS_TEST_LOCK.acquire().await.unwrap(); let mut config = NetworkProxyConfig { dangerously_allow_plaintext_credential_injection: true, + dangerously_allow_all_unix_sockets: Some(false), ..NetworkProxyConfig::default() }; + config.set_allow_unix_sockets(Vec::new()); config.set_credential_broker_enabled(/*enabled*/ true); config.configure_credential_broker_environment(&HashMap::from([( "GH_HOST".to_string(), diff --git a/codex-rs/network-proxy/src/remote_config.rs b/codex-rs/network-proxy/src/remote_config.rs index 3bb5101a61..a230699489 100644 --- a/codex-rs/network-proxy/src/remote_config.rs +++ b/codex-rs/network-proxy/src/remote_config.rs @@ -87,7 +87,9 @@ impl RemoteNetworkProxyConfig { enable_socks5: config.enable_socks5, enable_socks5_udp: config.enable_socks5_udp, allow_upstream_proxy: config.allow_upstream_proxy, - dangerously_allow_all_unix_sockets: config.dangerously_allow_all_unix_sockets, + dangerously_allow_all_unix_sockets: config + .dangerously_allow_all_unix_sockets + .unwrap_or(false), mode: config.mode, domains: config.domains.clone(), unix_sockets: config.unix_sockets.clone(), @@ -101,7 +103,7 @@ impl RemoteNetworkProxyConfig { enable_socks5: self.enable_socks5, enable_socks5_udp: self.enable_socks5_udp, allow_upstream_proxy: self.allow_upstream_proxy, - dangerously_allow_all_unix_sockets: self.dangerously_allow_all_unix_sockets, + dangerously_allow_all_unix_sockets: Some(self.dangerously_allow_all_unix_sockets), mode: self.mode, domains: self.domains, unix_sockets: self.unix_sockets, diff --git a/codex-rs/network-proxy/src/remote_config_tests.rs b/codex-rs/network-proxy/src/remote_config_tests.rs index 5094acfaad..6661ec81a1 100644 --- a/codex-rs/network-proxy/src/remote_config_tests.rs +++ b/codex-rs/network-proxy/src/remote_config_tests.rs @@ -8,6 +8,36 @@ use crate::NetworkProxyAuditMetadata; use crate::NetworkProxyConfig; use crate::NetworkProxyState; +#[test] +fn optional_socket_policy_preserves_input_and_resolves_at_remote_boundary() { + for allow_all in [None, Some(false), Some(true)] { + let mut config = NetworkProxyConfig { + dangerously_allow_all_unix_sockets: allow_all, + ..NetworkProxyConfig::default() + }; + config.set_allow_unix_sockets(Vec::new()); + let serialized = serde_json::to_value(&config).expect("serialize config"); + assert_eq!( + serialized.get("dangerously_allow_all_unix_sockets"), + allow_all.map(serde_json::Value::Bool).as_ref() + ); + assert_eq!(serialized["unix_sockets"], serde_json::json!({})); + assert_eq!( + serde_json::from_value::(serialized).expect("deserialize config"), + config + ); + + let remote = RemoteNetworkProxyConfig::from_effective_config(&config) + .expect("supported remote config"); + assert_eq!( + remote.dangerously_allow_all_unix_sockets, + allow_all.unwrap_or(false) + ); + config.dangerously_allow_all_unix_sockets = Some(allow_all.unwrap_or(false)); + assert_eq!(remote.into_network_proxy_config(), config); + } +} + #[test] fn round_trip_preserves_supported_effective_settings() { let mut config = NetworkProxyConfig { @@ -15,7 +45,7 @@ fn round_trip_preserves_supported_effective_settings() { enable_socks5: false, enable_socks5_udp: false, allow_upstream_proxy: false, - dangerously_allow_all_unix_sockets: true, + dangerously_allow_all_unix_sockets: Some(true), mode: NetworkMode::Limited, allow_local_binding: true, ..NetworkProxyConfig::default() diff --git a/codex-rs/network-proxy/src/runtime.rs b/codex-rs/network-proxy/src/runtime.rs index dad445d622..4ffe48d349 100644 --- a/codex-rs/network-proxy/src/runtime.rs +++ b/codex-rs/network-proxy/src/runtime.rs @@ -786,7 +786,11 @@ impl NetworkProxyState { } let guard = self.state.read().await; - if guard.config.dangerously_allow_all_unix_sockets { + if guard + .config + .dangerously_allow_all_unix_sockets + .unwrap_or(false) + { return Ok(true); } @@ -2038,7 +2042,7 @@ mod tests { let config = NetworkProxyConfig { enabled: true, - dangerously_allow_all_unix_sockets: true, + dangerously_allow_all_unix_sockets: Some(true), ..NetworkProxyConfig::default() }; @@ -2055,7 +2059,7 @@ mod tests { let config = NetworkProxyConfig { enabled: true, - dangerously_allow_all_unix_sockets: true, + dangerously_allow_all_unix_sockets: Some(true), ..NetworkProxyConfig::default() }; @@ -2071,7 +2075,7 @@ mod tests { let config = NetworkProxyConfig { enabled: true, - dangerously_allow_all_unix_sockets: true, + dangerously_allow_all_unix_sockets: Some(true), ..NetworkProxyConfig::default() }; @@ -2084,7 +2088,7 @@ mod tests { let config = NetworkProxyConfig { enabled: true, - dangerously_allow_all_unix_sockets: true, + dangerously_allow_all_unix_sockets: Some(true), ..NetworkProxyConfig::default() }; @@ -2244,7 +2248,7 @@ mod tests { async fn unix_socket_allow_all_flag_bypasses_allowlist() { let state = network_proxy_state_for_policy({ let mut network = network_settings(&["example.com"], &[]); - network.dangerously_allow_all_unix_sockets = true; + network.dangerously_allow_all_unix_sockets = Some(true); network }); @@ -2262,7 +2266,7 @@ mod tests { &[], std::slice::from_ref(&socket_path), ); - network.dangerously_allow_all_unix_sockets = true; + network.dangerously_allow_all_unix_sockets = Some(true); network }); diff --git a/codex-rs/network-proxy/src/state.rs b/codex-rs/network-proxy/src/state.rs index 90731a8b4e..d5a1f513b4 100644 --- a/codex-rs/network-proxy/src/state.rs +++ b/codex-rs/network-proxy/src/state.rs @@ -209,7 +209,7 @@ pub fn validate_policy_against_constraints( .dangerously_allow_all_unix_sockets .unwrap_or(constraints.allow_unix_sockets.is_none()); validate( - config.dangerously_allow_all_unix_sockets, + config.dangerously_allow_all_unix_sockets.unwrap_or(false), move |candidate| { if *candidate && !allow_all_unix_sockets { Err(invalid_value( diff --git a/codex-rs/sandboxing/src/manager_tests.rs b/codex-rs/sandboxing/src/manager_tests.rs index 149c741a3c..5bf8736fc7 100644 --- a/codex-rs/sandboxing/src/manager_tests.rs +++ b/codex-rs/sandboxing/src/manager_tests.rs @@ -520,7 +520,7 @@ async fn linux_unix_socket_grant_uses_effective_managed_policy() -> anyhow::Resu let state = build_config_state( NetworkProxyConfig { enabled: true, - dangerously_allow_all_unix_sockets: true, + dangerously_allow_all_unix_sockets: Some(true), ..Default::default() }, NetworkProxyConstraints::default(), diff --git a/codex-rs/sandboxing/src/seatbelt_tests.rs b/codex-rs/sandboxing/src/seatbelt_tests.rs index 4b2a36f670..824e83af6a 100644 --- a/codex-rs/sandboxing/src/seatbelt_tests.rs +++ b/codex-rs/sandboxing/src/seatbelt_tests.rs @@ -813,7 +813,7 @@ async fn prepared_managed_network_context_takes_precedence_over_live_proxy_socke let network_config = NetworkProxyConfig { enabled: true, mode: NetworkMode::Full, - dangerously_allow_all_unix_sockets: true, + dangerously_allow_all_unix_sockets: Some(true), ..Default::default() }; let state = build_config_state(network_config, NetworkProxyConstraints::default())?; diff --git a/codex-rs/tui/src/app/agents_overview_tests.rs b/codex-rs/tui/src/app/agents_overview_tests.rs index a045d8d613..f1ebf9250a 100644 --- a/codex-rs/tui/src/app/agents_overview_tests.rs +++ b/codex-rs/tui/src/app/agents_overview_tests.rs @@ -1787,7 +1787,8 @@ async fn overview_cold_resume_honors_working_directory_selection() -> Result<()> ("session", true, true), ("session", false, false), ] { - let mut app = make_test_app().await; + // Keep the large setup and cold-resume futures off the Windows test stack. + let mut app = Box::pin(make_test_app()).await; trust_fixture_folders(&mut app); let chosen = app.config.codex_home.join("chosen"); let overridden = app.config.codex_home.join("overridden"); @@ -1814,7 +1815,8 @@ async fn overview_cold_resume_honors_working_directory_selection() -> Result<()> ) .expect("create historical session"), )?; - let mut app_server = crate::start_embedded_app_server_for_picker(&app.config).await?; + let mut app_server = + Box::pin(crate::start_embedded_app_server_for_picker(&app.config)).await?; app.launch_cwd = chosen.to_path_buf(); app.harness_overrides.cwd = cli_cwd.then(|| { if runtime_cwd { @@ -1838,8 +1840,7 @@ async fn overview_cold_resume_honors_working_directory_selection() -> Result<()> drop(selection); let pending_loading = crate::custom_terminal::test_support::last_rendered_buffer(&tui.terminal).clone(); - app.select_agents_overview_thread(&mut tui, &mut app_server, thread_id) - .await?; + Box::pin(app.select_agents_overview_thread(&mut tui, &mut app_server, thread_id)).await?; // The widget replacement clears the terminal; feedback must remain until the next draw. let loading = crate::custom_terminal::test_support::last_rendered_buffer(&tui.terminal).clone(); @@ -1858,21 +1859,19 @@ async fn overview_cold_resume_honors_working_directory_selection() -> Result<()> insta::allow_duplicates! { insta::assert_snapshot!(loading_text.trim_end(), @"Loading task…"); } - app.handle_tui_event(&mut tui, &mut app_server, TuiEvent::Draw) - .await?; + Box::pin(app.handle_tui_event(&mut tui, &mut app_server, TuiEvent::Draw)).await?; assert_ne!( crate::custom_terminal::test_support::last_rendered_buffer(&tui.terminal), &loading, ); - let observed = app_server - .resume_thread( - &app.local_settings, - app.config.clone(), - thread_id, - crate::app_server_session::ResumeModelSettings::PreserveExistingThread, - ) - .await? - .session; + let observed = Box::pin(app_server.resume_thread( + &app.local_settings, + app.config.clone(), + thread_id, + crate::app_server_session::ResumeModelSettings::PreserveExistingThread, + )) + .await? + .session; assert_eq!( (app.config.cwd.clone(), observed.cwd), (expected_cwd.clone(), expected_cwd),