Preserve custom arg0 for sandboxed exec-server processes (#34497)

## Why

Sandbox wrappers replaced the process launch command and did not carry an
`ExecParams.arg0` override through to the inner process.

## What changed

- Route sandboxed Unix launches with a custom `arg0` through a helper mode that
  re-execs the requested program with the override.
- Expose the helper executable to the filesystem sandbox and dispatch its mode
  from Codex and exec-server test binaries.

## Testing

Add coverage for the prepared sandbox command and an end-to-end remote process
that verifies both the custom `arg0` and filesystem restrictions.

GitOrigin-RevId: c9f8eef3906d184e670184c2eeed250d5895a9ca
This commit is contained in:
jif
2026-07-21 08:59:41 +00:00
committed by copyberry
parent 99eb575649
commit 4f1992732c
10 changed files with 243 additions and 14 deletions
@@ -11,6 +11,8 @@ use codex_network_proxy::RemoteNetworkProxyLaunchConfig;
use codex_protocol::config_types::WindowsSandboxLevel;
#[cfg(any(unix, windows))]
use codex_protocol::models::PermissionProfile;
#[cfg(target_os = "linux")]
use codex_sandboxing::landlock::CODEX_LINUX_SANDBOX_ARG0;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_path_uri::PathUri;
use pretty_assertions::assert_eq;
@@ -19,6 +21,8 @@ use tokio::io::AsyncWriteExt;
use tokio::time::timeout;
use super::prepare_exec_request;
#[cfg(unix)]
use crate::CODEX_ARG0_EXEC_HELPER_ARG1;
use crate::ExecParams;
#[cfg(any(unix, windows))]
use crate::ExecServerRuntimePaths;
@@ -93,6 +97,61 @@ async fn sandbox_request_wraps_native_argv_on_executor() {
);
}
#[cfg(unix)]
#[tokio::test]
async fn sandbox_request_routes_custom_arg0_to_inner_helper() {
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-custom-arg0"),
argv: vec!["/bin/sh".to_string(), "-c".to_string(), "true".to_string()],
cwd: cwd_uri,
env_policy: None,
env: HashMap::new(),
tty: false,
pipe_stdin: false,
arg0: Some("custom-arg0".to_string()),
sandbox: Some(sandbox),
enforce_managed_network: false,
managed_network: None,
network_proxy: None,
};
let prepared = prepare_exec_request(&params, HashMap::new(), Some(&runtime_paths))
.await
.expect("prepare sandboxed request");
let helper_mode = prepared
.command
.iter()
.position(|arg| arg == CODEX_ARG0_EXEC_HELPER_ARG1)
.expect("sandboxed command should invoke arg0 helper");
assert_eq!(
prepared.command[helper_mode..],
[
CODEX_ARG0_EXEC_HELPER_ARG1,
"custom-arg0",
"/bin/sh",
"-c",
"true",
]
);
#[cfg(target_os = "linux")]
assert_eq!(prepared.arg0, Some(CODEX_LINUX_SANDBOX_ARG0.to_string()));
#[cfg(target_os = "macos")]
assert_eq!(prepared.arg0, None);
}
#[cfg(target_os = "macos")]
#[tokio::test]
async fn sandbox_request_allows_prepared_managed_proxy_port() {