From 872cd96843ecc274b405019ba123ce85b7b3065c Mon Sep 17 00:00:00 2001 From: viyatb-oai Date: Fri, 10 Apr 2026 14:21:13 -0700 Subject: [PATCH 1/6] Fix NO_PROXY to keep private targets proxied --- codex-rs/network-proxy/src/proxy.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/codex-rs/network-proxy/src/proxy.rs b/codex-rs/network-proxy/src/proxy.rs index 0270966d4414..42aaf73e213c 100644 --- a/codex-rs/network-proxy/src/proxy.rs +++ b/codex-rs/network-proxy/src/proxy.rs @@ -377,11 +377,7 @@ pub const NO_PROXY_ENV_KEYS: &[&str] = &[ "BUNDLE_NO_PROXY", ]; -pub const DEFAULT_NO_PROXY_VALUE: &str = concat!( - "localhost,127.0.0.1,::1,", - "*.local,.local,", - "169.254.0.0/16,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16" -); +pub const DEFAULT_NO_PROXY_VALUE: &str = "localhost,127.0.0.1,::1"; pub fn proxy_url_env_value<'a>( env: &'a HashMap, @@ -452,7 +448,8 @@ fn apply_proxy_env_overrides( // HTTP(S)_PROXY. Keep them aligned with the managed HTTP proxy endpoint. set_env_keys(env, WEBSOCKET_PROXY_ENV_KEYS, &http_proxy_url); - // Keep local/private targets direct so local IPC and metadata endpoints avoid the proxy. + // Keep loopback targets direct so local IPC avoids the proxy. Private/internal endpoints must + // still use the proxy because proxy-enforced sandboxes only allow outbound traffic to the proxy. set_env_keys(env, NO_PROXY_ENV_KEYS, DEFAULT_NO_PROXY_VALUE); env.insert("ELECTRON_GET_USE_PROXY".to_string(), "true".to_string()); @@ -927,6 +924,11 @@ mod tests { env.get("NO_PROXY"), Some(&DEFAULT_NO_PROXY_VALUE.to_string()) ); + let no_proxy = env.get("NO_PROXY").expect("NO_PROXY should be set"); + assert!(!no_proxy.contains("10.0.0.0/8")); + assert!(!no_proxy.contains("172.16.0.0/12")); + assert!(!no_proxy.contains("192.168.0.0/16")); + assert!(!no_proxy.contains("169.254.0.0/16")); assert_eq!(env.get(ALLOW_LOCAL_BINDING_ENV_KEY), Some(&"0".to_string())); assert_eq!(env.get("ELECTRON_GET_USE_PROXY"), Some(&"true".to_string())); #[cfg(target_os = "macos")] From 8c5aa94fa5af42f3917004048b67f528f068e200 Mon Sep 17 00:00:00 2001 From: viyatb-oai Date: Fri, 10 Apr 2026 14:32:48 -0700 Subject: [PATCH 2/6] fix: keep hostname targets proxied by default Preserve private/link-local CIDRs in NO_PROXY while removing hostname suffix bypasses so internal hostnames can be resolved through the managed proxy path. Co-authored-by: Codex noreply@openai.com --- codex-rs/network-proxy/src/proxy.rs | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/codex-rs/network-proxy/src/proxy.rs b/codex-rs/network-proxy/src/proxy.rs index 42aaf73e213c..9109c0ead54d 100644 --- a/codex-rs/network-proxy/src/proxy.rs +++ b/codex-rs/network-proxy/src/proxy.rs @@ -377,7 +377,13 @@ pub const NO_PROXY_ENV_KEYS: &[&str] = &[ "BUNDLE_NO_PROXY", ]; -pub const DEFAULT_NO_PROXY_VALUE: &str = "localhost,127.0.0.1,::1"; +pub const DEFAULT_NO_PROXY_VALUE: &str = concat!( + "localhost,127.0.0.1,::1,", + "169.254.0.0/16,", + "10.0.0.0/8,", + "172.16.0.0/12,", + "192.168.0.0/16" +); pub fn proxy_url_env_value<'a>( env: &'a HashMap, @@ -448,8 +454,9 @@ fn apply_proxy_env_overrides( // HTTP(S)_PROXY. Keep them aligned with the managed HTTP proxy endpoint. set_env_keys(env, WEBSOCKET_PROXY_ENV_KEYS, &http_proxy_url); - // Keep loopback targets direct so local IPC avoids the proxy. Private/internal endpoints must - // still use the proxy because proxy-enforced sandboxes only allow outbound traffic to the proxy. + // Keep loopback and IP-literal private targets direct so local IPC/LAN access avoids the proxy. + // Do not include hostname suffixes here: those can force clients to resolve internal names + // locally instead of letting the proxy resolve them. set_env_keys(env, NO_PROXY_ENV_KEYS, DEFAULT_NO_PROXY_VALUE); env.insert("ELECTRON_GET_USE_PROXY".to_string(), "true".to_string()); @@ -925,10 +932,12 @@ mod tests { Some(&DEFAULT_NO_PROXY_VALUE.to_string()) ); let no_proxy = env.get("NO_PROXY").expect("NO_PROXY should be set"); - assert!(!no_proxy.contains("10.0.0.0/8")); - assert!(!no_proxy.contains("172.16.0.0/12")); - assert!(!no_proxy.contains("192.168.0.0/16")); - assert!(!no_proxy.contains("169.254.0.0/16")); + assert!(no_proxy.contains("10.0.0.0/8")); + assert!(no_proxy.contains("172.16.0.0/12")); + assert!(no_proxy.contains("192.168.0.0/16")); + assert!(no_proxy.contains("169.254.0.0/16")); + assert!(!no_proxy.contains("*.local")); + assert!(!no_proxy.contains(".local")); assert_eq!(env.get(ALLOW_LOCAL_BINDING_ENV_KEY), Some(&"0".to_string())); assert_eq!(env.get("ELECTRON_GET_USE_PROXY"), Some(&"true".to_string())); #[cfg(target_os = "macos")] From 6791859eece4a8ead9c834ce597e9661529b5f1c Mon Sep 17 00:00:00 2001 From: viyatb-oai Date: Fri, 10 Apr 2026 17:04:52 -0700 Subject: [PATCH 3/6] fix: allow DNS lookups in macOS proxy sandbox Co-authored-by: Codex --- codex-rs/sandboxing/src/seatbelt.rs | 6 ++++++ codex-rs/sandboxing/src/seatbelt_tests.rs | 24 +++++++++++++++++++---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/codex-rs/sandboxing/src/seatbelt.rs b/codex-rs/sandboxing/src/seatbelt.rs index bb784bc0dfdb..d1f55d75eb72 100644 --- a/codex-rs/sandboxing/src/seatbelt.rs +++ b/codex-rs/sandboxing/src/seatbelt.rs @@ -254,6 +254,12 @@ fn dynamic_network_policy_for_network( policy.push_str("(allow network-inbound (local ip \"localhost:*\"))\n"); policy.push_str("(allow network-outbound (remote ip \"localhost:*\"))\n"); } + if !proxy.ports.is_empty() { + policy.push_str("; allow DNS lookups while application traffic remains proxy-routed\n"); + policy.push_str("(allow network-bind (local ip \"*:*\"))\n"); + policy.push_str("(allow network-inbound (local udp \"localhost:*\"))\n"); + policy.push_str("(allow network-outbound (remote ip \"*:53\"))\n"); + } for port in &proxy.ports { policy.push_str(&format!( "(allow network-outbound (remote ip \"localhost:{port}\"))\n" diff --git a/codex-rs/sandboxing/src/seatbelt_tests.rs b/codex-rs/sandboxing/src/seatbelt_tests.rs index 0daff82e15dd..c1d9eaf3fa36 100644 --- a/codex-rs/sandboxing/src/seatbelt_tests.rs +++ b/codex-rs/sandboxing/src/seatbelt_tests.rs @@ -95,12 +95,20 @@ fn create_seatbelt_args_routes_network_through_proxy_ports() { "expected SOCKS proxy port allow rule in policy:\n{policy}" ); assert!( - !policy.contains("\n(allow network-outbound)\n"), - "policy should not include blanket outbound allowance when proxy ports are present:\n{policy}" + policy.contains("(allow network-bind (local ip \"*:*\"))"), + "expected DNS local bind allow rule in policy:\n{policy}" + ); + assert!( + policy.contains("(allow network-inbound (local udp \"localhost:*\"))"), + "expected DNS UDP reply allow rule in policy:\n{policy}" + ); + assert!( + policy.contains("(allow network-outbound (remote ip \"*:53\"))"), + "expected DNS egress allow rule in policy:\n{policy}" ); assert!( - !policy.contains("(allow network-bind (local ip \"localhost:*\"))"), - "policy should not allow loopback binding unless explicitly enabled:\n{policy}" + !policy.contains("\n(allow network-outbound)\n"), + "policy should not include blanket outbound allowance when proxy ports are present:\n{policy}" ); assert!( !policy.contains("(allow network-inbound (local ip \"localhost:*\"))"), @@ -338,6 +346,10 @@ fn dynamic_network_policy_preserves_restricted_policy_when_proxy_config_without_ !policy.contains("(allow network-outbound (remote ip \"localhost:"), "policy should not include proxy port allowance when proxy config is present without ports:\n{policy}" ); + assert!( + !policy.contains("(allow network-outbound (remote ip \"*:53\"))"), + "policy should stay fail-closed for DNS when no proxy ports are available:\n{policy}" + ); } #[test] @@ -367,6 +379,10 @@ fn dynamic_network_policy_preserves_restricted_policy_for_managed_network_withou !policy.contains("\n(allow network-outbound)\n"), "policy should not include blanket outbound allowance when managed network is active without proxy endpoints:\n{policy}" ); + assert!( + !policy.contains("(allow network-outbound (remote ip \"*:53\"))"), + "policy should stay fail-closed for DNS when no proxy endpoints are available:\n{policy}" + ); } #[test] From 40106382df1d0186bbbd80ae1d333e838ba3aec0 Mon Sep 17 00:00:00 2001 From: viyatb-oai Date: Fri, 10 Apr 2026 17:18:50 -0700 Subject: [PATCH 4/6] fix: gate macOS DNS carveout on local binding Co-authored-by: Codex --- codex-rs/sandboxing/src/seatbelt.rs | 9 ++------ codex-rs/sandboxing/src/seatbelt_tests.rs | 26 +++++++++++------------ 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/codex-rs/sandboxing/src/seatbelt.rs b/codex-rs/sandboxing/src/seatbelt.rs index d1f55d75eb72..a732b16d7960 100644 --- a/codex-rs/sandboxing/src/seatbelt.rs +++ b/codex-rs/sandboxing/src/seatbelt.rs @@ -249,15 +249,10 @@ fn dynamic_network_policy_for_network( if should_use_restricted_network_policy { let mut policy = String::new(); if proxy.allow_local_binding { - policy.push_str("; allow loopback local binding and loopback traffic\n"); - policy.push_str("(allow network-bind (local ip \"localhost:*\"))\n"); + policy.push_str("; allow local binding, loopback traffic, and DNS lookups\n"); + policy.push_str("(allow network-bind (local ip \"*:*\"))\n"); policy.push_str("(allow network-inbound (local ip \"localhost:*\"))\n"); policy.push_str("(allow network-outbound (remote ip \"localhost:*\"))\n"); - } - if !proxy.ports.is_empty() { - policy.push_str("; allow DNS lookups while application traffic remains proxy-routed\n"); - policy.push_str("(allow network-bind (local ip \"*:*\"))\n"); - policy.push_str("(allow network-inbound (local udp \"localhost:*\"))\n"); policy.push_str("(allow network-outbound (remote ip \"*:53\"))\n"); } for port in &proxy.ports { diff --git a/codex-rs/sandboxing/src/seatbelt_tests.rs b/codex-rs/sandboxing/src/seatbelt_tests.rs index c1d9eaf3fa36..5a18e7d9eab0 100644 --- a/codex-rs/sandboxing/src/seatbelt_tests.rs +++ b/codex-rs/sandboxing/src/seatbelt_tests.rs @@ -94,26 +94,22 @@ fn create_seatbelt_args_routes_network_through_proxy_ports() { policy.contains("(allow network-outbound (remote ip \"localhost:48081\"))"), "expected SOCKS proxy port allow rule in policy:\n{policy}" ); - assert!( - policy.contains("(allow network-bind (local ip \"*:*\"))"), - "expected DNS local bind allow rule in policy:\n{policy}" - ); - assert!( - policy.contains("(allow network-inbound (local udp \"localhost:*\"))"), - "expected DNS UDP reply allow rule in policy:\n{policy}" - ); - assert!( - policy.contains("(allow network-outbound (remote ip \"*:53\"))"), - "expected DNS egress allow rule in policy:\n{policy}" - ); assert!( !policy.contains("\n(allow network-outbound)\n"), "policy should not include blanket outbound allowance when proxy ports are present:\n{policy}" ); + assert!( + !policy.contains("(allow network-bind (local ip \"*:*\"))"), + "policy should not allow local binding unless explicitly enabled:\n{policy}" + ); assert!( !policy.contains("(allow network-inbound (local ip \"localhost:*\"))"), "policy should not allow loopback inbound unless explicitly enabled:\n{policy}" ); + assert!( + !policy.contains("(allow network-outbound (remote ip \"*:53\"))"), + "policy should not allow raw DNS unless local binding is explicitly enabled:\n{policy}" + ); } #[test] @@ -298,7 +294,7 @@ fn create_seatbelt_args_allows_local_binding_when_explicitly_enabled() { ); assert!( - policy.contains("(allow network-bind (local ip \"localhost:*\"))"), + policy.contains("(allow network-bind (local ip \"*:*\"))"), "policy should allow loopback local binding when explicitly enabled:\n{policy}" ); assert!( @@ -309,6 +305,10 @@ fn create_seatbelt_args_allows_local_binding_when_explicitly_enabled() { policy.contains("(allow network-outbound (remote ip \"localhost:*\"))"), "policy should allow loopback outbound when explicitly enabled:\n{policy}" ); + assert!( + policy.contains("(allow network-outbound (remote ip \"*:53\"))"), + "policy should allow DNS egress when local binding is explicitly enabled:\n{policy}" + ); assert!( !policy.contains("\n(allow network-outbound)\n"), "policy should keep proxy-routed behavior without blanket outbound allowance:\n{policy}" From 5af606d284d8838f678c01997200decf0691db4f Mon Sep 17 00:00:00 2001 From: viyatb-oai Date: Fri, 10 Apr 2026 18:42:26 -0700 Subject: [PATCH 5/6] fix: gate macOS DNS carveout on proxy ports Co-authored-by: Codex --- codex-rs/sandboxing/src/seatbelt.rs | 5 +++- codex-rs/sandboxing/src/seatbelt_tests.rs | 29 +++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/codex-rs/sandboxing/src/seatbelt.rs b/codex-rs/sandboxing/src/seatbelt.rs index a732b16d7960..517a9163df5e 100644 --- a/codex-rs/sandboxing/src/seatbelt.rs +++ b/codex-rs/sandboxing/src/seatbelt.rs @@ -249,10 +249,13 @@ fn dynamic_network_policy_for_network( if should_use_restricted_network_policy { let mut policy = String::new(); if proxy.allow_local_binding { - policy.push_str("; allow local binding, loopback traffic, and DNS lookups\n"); + policy.push_str("; allow local binding and loopback traffic\n"); policy.push_str("(allow network-bind (local ip \"*:*\"))\n"); policy.push_str("(allow network-inbound (local ip \"localhost:*\"))\n"); policy.push_str("(allow network-outbound (remote ip \"localhost:*\"))\n"); + } + if proxy.allow_local_binding && !proxy.ports.is_empty() { + policy.push_str("; allow DNS lookups while application traffic remains proxy-routed\n"); policy.push_str("(allow network-outbound (remote ip \"*:53\"))\n"); } for port in &proxy.ports { diff --git a/codex-rs/sandboxing/src/seatbelt_tests.rs b/codex-rs/sandboxing/src/seatbelt_tests.rs index 5a18e7d9eab0..419f3968d6ab 100644 --- a/codex-rs/sandboxing/src/seatbelt_tests.rs +++ b/codex-rs/sandboxing/src/seatbelt_tests.rs @@ -352,6 +352,35 @@ fn dynamic_network_policy_preserves_restricted_policy_when_proxy_config_without_ ); } +#[test] +fn dynamic_network_policy_blocks_dns_when_local_binding_has_no_proxy_ports() { + let policy = dynamic_network_policy( + &SandboxPolicy::WorkspaceWrite { + writable_roots: vec![], + read_only_access: Default::default(), + network_access: true, + exclude_tmpdir_env_var: false, + exclude_slash_tmp: false, + }, + /*enforce_managed_network*/ false, + &ProxyPolicyInputs { + ports: vec![], + has_proxy_config: true, + allow_local_binding: true, + ..ProxyPolicyInputs::default() + }, + ); + + assert!( + policy.contains("(allow network-bind (local ip \"*:*\"))"), + "policy should still allow explicitly configured local binding:\n{policy}" + ); + assert!( + !policy.contains("(allow network-outbound (remote ip \"*:53\"))"), + "policy should not allow DNS egress when no proxy ports are available:\n{policy}" + ); +} + #[test] fn dynamic_network_policy_preserves_restricted_policy_for_managed_network_without_proxy_config() { let policy = dynamic_network_policy( From f343b6c06055220510ff6aa258fae51c39dc9537 Mon Sep 17 00:00:00 2001 From: viyatb-oai Date: Fri, 10 Apr 2026 19:50:45 -0700 Subject: [PATCH 6/6] test: avoid pinning local no_proxy exclusions Co-authored-by: Codex noreply@openai.com --- codex-rs/network-proxy/src/proxy.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/codex-rs/network-proxy/src/proxy.rs b/codex-rs/network-proxy/src/proxy.rs index 9109c0ead54d..97ea6735f4bf 100644 --- a/codex-rs/network-proxy/src/proxy.rs +++ b/codex-rs/network-proxy/src/proxy.rs @@ -936,8 +936,6 @@ mod tests { assert!(no_proxy.contains("172.16.0.0/12")); assert!(no_proxy.contains("192.168.0.0/16")); assert!(no_proxy.contains("169.254.0.0/16")); - assert!(!no_proxy.contains("*.local")); - assert!(!no_proxy.contains(".local")); assert_eq!(env.get(ALLOW_LOCAL_BINDING_ENV_KEY), Some(&"0".to_string())); assert_eq!(env.get("ELECTRON_GET_USE_PROXY"), Some(&"true".to_string())); #[cfg(target_os = "macos")]