mirror of
https://github.com/openai/codex.git
synced 2026-09-02 14:49:14 +00:00
## Why Managed network configures commands to use local HTTP and SOCKS proxies. For commands delegated to the exec server, the proxy environment and the sandbox policy were prepared separately. On macOS, that meant a command could receive `HTTPS_PROXY=http://127.0.0.1:43123` while Seatbelt still denied access to port `43123`. ## What changed `NetworkProxy` now prepares the command environment and sandbox context together from the same runtime snapshot: ```text Prepared managed network ├── command environment: HTTPS_PROXY=http://127.0.0.1:43123 └── sandbox context: allow outbound to 127.0.0.1:43123 ``` That context travels with remote exec requests. The exec server preserves the managed proxy and CA environment, and macOS Seatbelt allows only the prepared loopback proxy ports without enabling broad network access or local binding. The protocol field is optional and the existing enforcement flag remains in place, preserving compatibility with callers that do not send the new context.
157 lines
5.2 KiB
Rust
157 lines
5.2 KiB
Rust
use std::collections::HashMap;
|
|
|
|
#[cfg(target_os = "macos")]
|
|
use codex_network_proxy::ManagedNetworkSandboxContext;
|
|
#[cfg(unix)]
|
|
use codex_protocol::models::PermissionProfile;
|
|
use codex_utils_absolute_path::AbsolutePathBuf;
|
|
use codex_utils_path_uri::PathUri;
|
|
use pretty_assertions::assert_eq;
|
|
|
|
use super::prepare_exec_request;
|
|
use crate::ExecParams;
|
|
#[cfg(unix)]
|
|
use crate::ExecServerRuntimePaths;
|
|
#[cfg(unix)]
|
|
use crate::FileSystemSandboxContext;
|
|
use crate::ProcessId;
|
|
|
|
#[cfg(unix)]
|
|
#[test]
|
|
fn sandbox_request_wraps_native_argv_on_executor() {
|
|
let cwd: AbsolutePathBuf = std::env::current_dir()
|
|
.expect("current directory")
|
|
.try_into()
|
|
.expect("absolute cwd");
|
|
let cwd_uri = PathUri::from_abs_path(&cwd);
|
|
let self_exe = std::env::current_exe().expect("current executable");
|
|
let runtime_paths =
|
|
ExecServerRuntimePaths::new(self_exe.clone(), Some(self_exe)).expect("runtime paths");
|
|
let sandbox = FileSystemSandboxContext::from_permission_profile_with_cwd(
|
|
PermissionProfile::workspace_write(),
|
|
cwd_uri.clone(),
|
|
);
|
|
let params = ExecParams {
|
|
process_id: ProcessId::from("process-1"),
|
|
argv: vec![
|
|
"/bin/bash".to_string(),
|
|
"-lc".to_string(),
|
|
"pwd".to_string(),
|
|
],
|
|
cwd: cwd_uri,
|
|
env_policy: None,
|
|
env: HashMap::new(),
|
|
tty: false,
|
|
pipe_stdin: false,
|
|
arg0: None,
|
|
sandbox: Some(sandbox),
|
|
enforce_managed_network: false,
|
|
managed_network: None,
|
|
};
|
|
|
|
let prepared = prepare_exec_request(¶ms, HashMap::new(), Some(&runtime_paths))
|
|
.expect("prepare sandboxed request");
|
|
|
|
assert_ne!(prepared.command, params.argv);
|
|
assert_eq!(prepared.cwd, cwd);
|
|
#[cfg(target_os = "linux")]
|
|
{
|
|
assert_eq!(
|
|
prepared.command.first(),
|
|
Some(&runtime_paths.codex_self_exe.to_string_lossy().into_owned())
|
|
);
|
|
let permission_profile_json = prepared
|
|
.command
|
|
.iter()
|
|
.position(|arg| arg == "--permission-profile")
|
|
.and_then(|index| prepared.command.get(index + 1))
|
|
.expect("sandbox wrapper permission profile");
|
|
let permission_profile: PermissionProfile =
|
|
serde_json::from_str(permission_profile_json).expect("permission profile JSON");
|
|
assert_eq!(
|
|
permission_profile,
|
|
PermissionProfile::workspace_write()
|
|
.materialize_project_roots_with_workspace_roots(std::slice::from_ref(&cwd))
|
|
);
|
|
}
|
|
#[cfg(target_os = "macos")]
|
|
assert_eq!(
|
|
prepared.command.first().map(String::as_str),
|
|
Some("/usr/bin/sandbox-exec")
|
|
);
|
|
}
|
|
|
|
#[cfg(target_os = "macos")]
|
|
#[test]
|
|
fn sandbox_request_allows_prepared_managed_proxy_port() {
|
|
let cwd: AbsolutePathBuf = std::env::current_dir()
|
|
.expect("current directory")
|
|
.try_into()
|
|
.expect("absolute cwd");
|
|
let cwd_uri = PathUri::from_abs_path(&cwd);
|
|
let self_exe = std::env::current_exe().expect("current executable");
|
|
let runtime_paths =
|
|
ExecServerRuntimePaths::new(self_exe.clone(), Some(self_exe)).expect("runtime paths");
|
|
let sandbox = FileSystemSandboxContext::from_permission_profile_with_cwd(
|
|
PermissionProfile::workspace_write(),
|
|
cwd_uri.clone(),
|
|
);
|
|
let params = ExecParams {
|
|
process_id: ProcessId::from("process-managed-network"),
|
|
argv: vec!["/usr/bin/true".to_string()],
|
|
cwd: cwd_uri,
|
|
env_policy: None,
|
|
env: HashMap::new(),
|
|
tty: false,
|
|
pipe_stdin: false,
|
|
arg0: None,
|
|
sandbox: Some(sandbox),
|
|
enforce_managed_network: true,
|
|
managed_network: Some(ManagedNetworkSandboxContext {
|
|
loopback_ports: vec![43123],
|
|
allow_local_binding: false,
|
|
}),
|
|
};
|
|
|
|
let prepared = prepare_exec_request(¶ms, HashMap::new(), Some(&runtime_paths))
|
|
.expect("prepare managed-network sandbox request");
|
|
let policy = prepared
|
|
.command
|
|
.windows(2)
|
|
.find_map(|args| (args[0] == "-p").then_some(args[1].as_str()))
|
|
.expect("Seatbelt policy argument");
|
|
|
|
assert!(policy.contains("(allow network-outbound (remote ip \"localhost:43123\"))"));
|
|
}
|
|
|
|
#[test]
|
|
fn native_request_preserves_native_launch_fields() {
|
|
let cwd: AbsolutePathBuf = std::env::current_dir()
|
|
.expect("current directory")
|
|
.try_into()
|
|
.expect("absolute cwd");
|
|
let cwd_uri = PathUri::from_abs_path(&cwd);
|
|
let env = HashMap::from([("TEST_ENV".to_string(), "value".to_string())]);
|
|
let params = ExecParams {
|
|
process_id: ProcessId::from("process-1"),
|
|
argv: vec!["echo".to_string(), "hello".to_string()],
|
|
cwd: cwd_uri,
|
|
env_policy: None,
|
|
env: HashMap::new(),
|
|
tty: false,
|
|
pipe_stdin: false,
|
|
arg0: Some("custom-arg0".to_string()),
|
|
sandbox: None,
|
|
enforce_managed_network: false,
|
|
managed_network: None,
|
|
};
|
|
|
|
let prepared = prepare_exec_request(¶ms, env.clone(), /*runtime_paths*/ None)
|
|
.expect("prepare native request");
|
|
|
|
assert_eq!(prepared.command, params.argv);
|
|
assert_eq!(prepared.cwd, cwd);
|
|
assert_eq!(prepared.env, env);
|
|
assert_eq!(prepared.arg0, params.arg0);
|
|
}
|