diff --git a/codex-rs/exec-server/Cargo.toml b/codex-rs/exec-server/Cargo.toml index bc54fb6a06..317ec7f45d 100644 --- a/codex-rs/exec-server/Cargo.toml +++ b/codex-rs/exec-server/Cargo.toml @@ -43,6 +43,9 @@ tokio = { workspace = true, features = [ tokio-tungstenite = { workspace = true } tracing = { workspace = true } +[target.'cfg(target_os = "linux")'.dependencies] +codex-linux-sandbox = { workspace = true } + [dev-dependencies] anyhow = { workspace = true } codex-utils-cargo-bin = { workspace = true } diff --git a/codex-rs/exec-server/src/bin/codex-exec-server.rs b/codex-rs/exec-server/src/bin/codex-exec-server.rs index 2002acef03..6ecdb67a47 100644 --- a/codex-rs/exec-server/src/bin/codex-exec-server.rs +++ b/codex-rs/exec-server/src/bin/codex-exec-server.rs @@ -1,4 +1,9 @@ +#[cfg(target_os = "linux")] +use std::path::Path; + use clap::Parser; +#[cfg(target_os = "linux")] +use codex_sandboxing::landlock::CODEX_LINUX_SANDBOX_ARG0; #[derive(Debug, Parser)] struct ExecServerArgs { @@ -11,11 +16,30 @@ struct ExecServerArgs { listen: String, } -#[tokio::main] -async fn main() -> anyhow::Result<()> { - let args = ExecServerArgs::parse(); - codex_exec_server::run_main_with_listen_url(&args.listen) - .await - .map_err(|err| anyhow::Error::msg(err.to_string()))?; - Ok(()) +fn main() -> anyhow::Result<()> { + dispatch_arg0(); + + let runtime = tokio::runtime::Runtime::new()?; + runtime.block_on(async { + let args = ExecServerArgs::parse(); + codex_exec_server::run_main_with_listen_url(&args.listen) + .await + .map_err(|err| anyhow::Error::msg(err.to_string())) + }) } + +#[cfg(target_os = "linux")] +fn dispatch_arg0() { + let argv0 = std::env::args_os().next().unwrap_or_default(); + let exe_name = Path::new(&argv0) + .file_name() + .and_then(|name| name.to_str()) + .unwrap_or_default(); + + if exe_name == CODEX_LINUX_SANDBOX_ARG0 { + codex_linux_sandbox::run_main(); + } +} + +#[cfg(not(target_os = "linux"))] +fn dispatch_arg0() {} diff --git a/codex-rs/exec-server/src/local_process.rs b/codex-rs/exec-server/src/local_process.rs index bb9693f81e..09f6a695d6 100644 --- a/codex-rs/exec-server/src/local_process.rs +++ b/codex-rs/exec-server/src/local_process.rs @@ -104,9 +104,14 @@ struct ExecServerRuntimeConfig { impl ExecServerRuntimeConfig { fn detect() -> Self { - let env_path = std::env::var_os("CODEX_LINUX_SANDBOX_EXE").map(PathBuf::from); Self { - codex_linux_sandbox_exe: env_path, + // The Codex CLI and codex-exec-server both dispatch the Linux + // sandbox helper from their own executable via argv[0]. + codex_linux_sandbox_exe: if cfg!(target_os = "linux") { + std::env::current_exe().ok() + } else { + None + }, } } } diff --git a/codex-rs/exec-server/tests/exec_process.rs b/codex-rs/exec-server/tests/exec_process.rs index eb7ba91523..f411b7a0f8 100644 --- a/codex-rs/exec-server/tests/exec_process.rs +++ b/codex-rs/exec-server/tests/exec_process.rs @@ -235,7 +235,16 @@ fn platform_sandbox_type() -> SandboxType { } fn write_outside_workspace_sandbox(workspace_root: &std::path::Path) -> SandboxLaunchConfig { - let policy = SandboxPolicy::new_workspace_write_policy(); + let mut policy = SandboxPolicy::new_workspace_write_policy(); + if let SandboxPolicy::WorkspaceWrite { + exclude_tmpdir_env_var, + exclude_slash_tmp, + .. + } = &mut policy + { + *exclude_tmpdir_env_var = true; + *exclude_slash_tmp = true; + } SandboxLaunchConfig { sandbox: platform_sandbox_type(), policy: policy.clone(), @@ -368,9 +377,7 @@ async fn exec_process_preserves_queued_events_before_subscribe(use_remote: bool) assert_exec_process_preserves_queued_events_before_subscribe(use_remote).await } -#[test_case(false ; "local")] -#[test_case(true ; "remote")] #[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn exec_process_sandbox_denies_write_outside_workspace(use_remote: bool) -> Result<()> { - assert_exec_process_sandbox_denies_write_outside_workspace(use_remote).await +async fn remote_exec_process_sandbox_denies_write_outside_workspace() -> Result<()> { + assert_exec_process_sandbox_denies_write_outside_workspace(/*use_remote*/ true).await } diff --git a/codex-rs/exec-server/tests/process.rs b/codex-rs/exec-server/tests/process.rs index 6dfa611a78..456078b279 100644 --- a/codex-rs/exec-server/tests/process.rs +++ b/codex-rs/exec-server/tests/process.rs @@ -4,9 +4,11 @@ mod common; use codex_app_server_protocol::JSONRPCMessage; use codex_app_server_protocol::JSONRPCResponse; +use codex_exec_server::ExecParams; use codex_exec_server::ExecResponse; use codex_exec_server::InitializeParams; use codex_exec_server::ProcessId; +use codex_sandboxing::SandboxLaunchConfig; use common::exec_server::exec_server; use pretty_assertions::assert_eq; @@ -45,14 +47,15 @@ async fn exec_server_starts_process_over_websocket() -> anyhow::Result<()> { let process_start_id = server .send_request( "process/start", - serde_json::json!({ - "processId": "proc-1", - "argv": ["true"], - "cwd": std::env::current_dir()?, - "env": {}, - "tty": false, - "arg0": null - }), + serde_json::to_value(ExecParams { + process_id: ProcessId::from("proc-1"), + argv: vec!["true".to_string()], + cwd: std::env::current_dir()?, + env: Default::default(), + tty: false, + arg0: None, + sandbox: SandboxLaunchConfig::no_sandbox(std::env::current_dir()?), + })?, ) .await?; let response = server