From 9c1debcc05201b82dd8357823a719faf90c41581 Mon Sep 17 00:00:00 2001 From: viyatb-oai Date: Wed, 8 Jul 2026 12:49:56 -0700 Subject: [PATCH] test(linux-sandbox): cover host namespace guard Co-authored-by: Codex noreply@openai.com --- codex-rs/linux-sandbox/src/bwrap.rs | 2 -- codex-rs/linux-sandbox/src/launcher.rs | 12 ++++----- codex-rs/linux-sandbox/src/proxy_routing.rs | 18 +++++++++++++ .../linux-sandbox/src/transparent_network.rs | 4 +-- .../tests/suite/managed_proxy.rs | 27 +++++++++---------- 5 files changed, 36 insertions(+), 27 deletions(-) diff --git a/codex-rs/linux-sandbox/src/bwrap.rs b/codex-rs/linux-sandbox/src/bwrap.rs index 8c4bb58770..69d2a0941b 100644 --- a/codex-rs/linux-sandbox/src/bwrap.rs +++ b/codex-rs/linux-sandbox/src/bwrap.rs @@ -410,10 +410,8 @@ fn anonymous_data_file(contents: &[u8]) -> io::Result { // SAFETY: pipe2 initialized both descriptors, and each is moved into exactly one File. let read_file = unsafe { File::from_raw_fd(pipe_fds[0]) }; - // SAFETY: see above. let mut write_file = unsafe { File::from_raw_fd(pipe_fds[1]) }; write_file.write_all(contents)?; - drop(write_file); Ok(read_file) } diff --git a/codex-rs/linux-sandbox/src/launcher.rs b/codex-rs/linux-sandbox/src/launcher.rs index 617c04fc76..23cf50bd7d 100644 --- a/codex-rs/linux-sandbox/src/launcher.rs +++ b/codex-rs/linux-sandbox/src/launcher.rs @@ -35,7 +35,11 @@ struct SystemBwrapCapabilities { } pub(crate) fn exec_bwrap(argv: Vec, preserved_files: Vec) -> ! { - if requests_setup_capabilities(&argv) { + if argv + .iter() + .take_while(|arg| arg.as_str() != "--") + .any(|arg| arg == "--cap-add") + { clear_loader_environment(); } match preferred_bwrap_launcher() { @@ -52,12 +56,6 @@ pub(crate) fn exec_bwrap(argv: Vec, preserved_files: Vec) -> ! { } } -fn requests_setup_capabilities(argv: &[String]) -> bool { - argv.iter() - .take_while(|arg| arg.as_str() != "--") - .any(|arg| arg == "--cap-add") -} - fn clear_loader_environment() { let keys = std::env::vars_os() .map(|(key, _)| key) diff --git a/codex-rs/linux-sandbox/src/proxy_routing.rs b/codex-rs/linux-sandbox/src/proxy_routing.rs index 9b95fa071b..f2cf9c7d9d 100644 --- a/codex-rs/linux-sandbox/src/proxy_routing.rs +++ b/codex-rs/linux-sandbox/src/proxy_routing.rs @@ -830,6 +830,7 @@ mod tests { use pretty_assertions::assert_eq; use std::collections::HashMap; use std::net::SocketAddr; + use std::os::unix::fs::MetadataExt; use std::path::PathBuf; #[test] @@ -973,6 +974,23 @@ mod tests { ); } + #[test] + fn transparent_routes_refuse_the_host_network_namespace() { + let spec = ProxyRouteSpec { + routes: vec![ProxyRouteEntry { + env_key: String::new(), + uds_path: PathBuf::new(), + }], + host_netns_inode: std::fs::metadata("/proc/self/ns/net") + .expect("network namespace metadata") + .ino(), + transparent_networking: true, + }; + let serialized = serde_json::to_string(&spec).expect("serialize proxy route spec"); + let err = super::activate_proxy_routes_in_netns(&serialized).expect_err("must fail closed"); + assert_eq!(err.kind(), std::io::ErrorKind::PermissionDenied); + } + #[test] fn parse_proxy_socket_dir_owner_pid_reads_owner_pid() { assert_eq!( diff --git a/codex-rs/linux-sandbox/src/transparent_network.rs b/codex-rs/linux-sandbox/src/transparent_network.rs index 128b3ef2ec..fa939a6741 100644 --- a/codex-rs/linux-sandbox/src/transparent_network.rs +++ b/codex-rs/linux-sandbox/src/transparent_network.rs @@ -93,9 +93,7 @@ impl FakeIpPool { return Some(*address); } let slot = u32::try_from(self.ip_to_host.len()).ok()?.checked_add(1)?; - if slot >= 1 << 16 { - return None; - } + (slot < 1 << 16).then_some(())?; let address = Ipv4Addr::from(FAKE_POOL_BASE + slot); self.host_to_ip.insert(hostname.clone(), address); self.ip_to_host.insert(address, hostname); diff --git a/codex-rs/linux-sandbox/tests/suite/managed_proxy.rs b/codex-rs/linux-sandbox/tests/suite/managed_proxy.rs index a5c35c7bd1..460030e76c 100644 --- a/codex-rs/linux-sandbox/tests/suite/managed_proxy.rs +++ b/codex-rs/linux-sandbox/tests/suite/managed_proxy.rs @@ -293,21 +293,18 @@ async fn managed_proxy_mode_routes_proxy_aware_and_direct_ipv4_traffic() { stdout.ends_with("pong"), "expected HTTPS tunnel payload, got: {stdout}" ); - let request = request_rx - .recv_timeout(Duration::from_secs(3)) - .expect("expected direct HTTP request at proxy"); - assert!( - request.starts_with("GET /direct HTTP/1.1\r\n"), - "expected captured origin-form request, got request: {request}" - ); - - let request = request_rx - .recv_timeout(Duration::from_secs(3)) - .expect("expected CONNECT request at proxy"); - assert!( - request.starts_with("CONNECT example.com:443 HTTP/1.1\r\n"), - "expected captured hostname in CONNECT request, got: {request}" - ); + for expected in [ + "GET /direct HTTP/1.1\r\n", + "CONNECT example.com:443 HTTP/1.1\r\n", + ] { + let request = request_rx + .recv_timeout(Duration::from_secs(3)) + .expect("expected direct request at proxy"); + assert!( + request.starts_with(expected), + "expected captured request starting with {expected:?}, got: {request}" + ); + } } #[tokio::test]