Files
codex/codex-rs/core/src/sandboxing/mod.rs
T
bacb92b1d7 Build remote exec env from exec-server policy (#17216)
## Summary
- add an exec-server `envPolicy` field; when present, the server starts
from its own process env and applies the shell environment policy there
- keep `env` as the exact environment for local/embedded starts, but
make it an overlay for remote unified-exec starts
- move the shell-environment-policy builder into `codex-config` so Core
and exec-server share the inherit/filter/set/include behavior
- overlay only runtime/sandbox/network deltas from Core onto the
exec-server-derived env

## Why
Remote unified exec was materializing the shell env inside Core and
forwarding the whole map to exec-server, so remote processes could
inherit the orchestrator machine's `HOME`, `PATH`, etc. This keeps the
base env on the executor while preserving Core-owned runtime additions
like `CODEX_THREAD_ID`, unified-exec defaults, network proxy env, and
sandbox marker env.

## Validation
- `just fmt`
- `git diff --check`
- `cargo test -p codex-exec-server --lib`
- `cargo test -p codex-core --lib unified_exec::process_manager::tests`
- `cargo test -p codex-core --lib exec_env::tests`
- `cargo test -p codex-core --lib exec_env_tests` (compile-only; filter
matched 0 tests)
- `cargo test -p codex-config --lib shell_environment` (compile-only;
filter matched 0 tests)
- `just bazel-lock-update`

## Known local validation issue
- `just bazel-lock-check` is not runnable in this checkout: it invokes
`./scripts/check-module-bazel-lock.sh`, which is missing.

---------

Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: pakrym-oai <pakrym@openai.com>
2026-04-13 09:59:08 +01:00

162 lines
5.2 KiB
Rust

/*
Module: sandboxing
Core-owned adapter types for exec/runtime plumbing. Policy selection and
command transformation live in the codex-sandboxing crate; this module keeps
the exec-only metadata and translates transformed sandbox commands back into
ExecRequest for execution.
*/
use crate::exec::ExecCapturePolicy;
use crate::exec::ExecExpiration;
use crate::exec::StdoutStream;
use crate::exec::WindowsSandboxFilesystemOverrides;
use crate::exec::execute_exec_request;
#[cfg(target_os = "macos")]
use crate::spawn::CODEX_SANDBOX_ENV_VAR;
use crate::spawn::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR;
use codex_network_proxy::NetworkProxy;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::exec_output::ExecToolCallOutput;
pub use codex_protocol::models::SandboxPermissions;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::protocol::SandboxPolicy;
use codex_sandboxing::SandboxExecRequest;
use codex_sandboxing::SandboxType;
use codex_utils_absolute_path::AbsolutePathBuf;
use std::collections::HashMap;
#[derive(Debug)]
pub(crate) struct ExecOptions {
pub(crate) expiration: ExecExpiration,
pub(crate) capture_policy: ExecCapturePolicy,
}
#[derive(Clone, Debug)]
pub(crate) struct ExecServerEnvConfig {
pub(crate) policy: codex_exec_server::ExecEnvPolicy,
pub(crate) local_policy_env: HashMap<String, String>,
}
#[derive(Debug)]
pub struct ExecRequest {
pub command: Vec<String>,
pub cwd: AbsolutePathBuf,
pub env: HashMap<String, String>,
pub(crate) exec_server_env_config: Option<ExecServerEnvConfig>,
pub network: Option<NetworkProxy>,
pub expiration: ExecExpiration,
pub capture_policy: ExecCapturePolicy,
pub sandbox: SandboxType,
pub windows_sandbox_level: WindowsSandboxLevel,
pub windows_sandbox_private_desktop: bool,
pub sandbox_policy: SandboxPolicy,
pub file_system_sandbox_policy: FileSystemSandboxPolicy,
pub network_sandbox_policy: NetworkSandboxPolicy,
pub(crate) windows_sandbox_filesystem_overrides: Option<WindowsSandboxFilesystemOverrides>,
pub arg0: Option<String>,
}
impl ExecRequest {
#[allow(clippy::too_many_arguments)]
pub fn new(
command: Vec<String>,
cwd: AbsolutePathBuf,
env: HashMap<String, String>,
network: Option<NetworkProxy>,
expiration: ExecExpiration,
capture_policy: ExecCapturePolicy,
sandbox: SandboxType,
windows_sandbox_level: WindowsSandboxLevel,
windows_sandbox_private_desktop: bool,
sandbox_policy: SandboxPolicy,
file_system_sandbox_policy: FileSystemSandboxPolicy,
network_sandbox_policy: NetworkSandboxPolicy,
arg0: Option<String>,
) -> Self {
Self {
command,
cwd,
env,
exec_server_env_config: None,
network,
expiration,
capture_policy,
sandbox,
windows_sandbox_level,
windows_sandbox_private_desktop,
sandbox_policy,
file_system_sandbox_policy,
network_sandbox_policy,
windows_sandbox_filesystem_overrides: None,
arg0,
}
}
pub(crate) fn from_sandbox_exec_request(
request: SandboxExecRequest,
options: ExecOptions,
) -> Self {
let SandboxExecRequest {
command,
cwd,
mut env,
network,
sandbox,
windows_sandbox_level,
windows_sandbox_private_desktop,
sandbox_policy,
file_system_sandbox_policy,
network_sandbox_policy,
arg0,
} = request;
let ExecOptions {
expiration,
capture_policy,
} = options;
if !network_sandbox_policy.is_enabled() {
env.insert(
CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR.to_string(),
"1".to_string(),
);
}
#[cfg(target_os = "macos")]
if sandbox == SandboxType::MacosSeatbelt {
env.insert(CODEX_SANDBOX_ENV_VAR.to_string(), "seatbelt".to_string());
}
Self {
command,
cwd,
env,
exec_server_env_config: None,
network,
expiration,
capture_policy,
sandbox,
windows_sandbox_level,
windows_sandbox_private_desktop,
sandbox_policy,
file_system_sandbox_policy,
network_sandbox_policy,
windows_sandbox_filesystem_overrides: None,
arg0,
}
}
}
pub async fn execute_env(
exec_request: ExecRequest,
stdout_stream: Option<StdoutStream>,
) -> codex_protocol::error::Result<ExecToolCallOutput> {
execute_exec_request(exec_request, stdout_stream, /*after_spawn*/ None).await
}
pub async fn execute_exec_request_with_after_spawn(
exec_request: ExecRequest,
stdout_stream: Option<StdoutStream>,
after_spawn: Option<Box<dyn FnOnce() + Send>>,
) -> codex_protocol::error::Result<ExecToolCallOutput> {
execute_exec_request(exec_request, stdout_stream, after_spawn).await
}