feat(sandbox): enforce proxy-aware network routing in sandbox (#11113)

## Summary
- expand proxy env injection to cover common tool env vars
(`HTTP_PROXY`/`HTTPS_PROXY`/`ALL_PROXY`/`NO_PROXY` families +
tool-specific variants)
- harden macOS Seatbelt network policy generation to route through
inferred loopback proxy endpoints and fail closed when proxy env is
malformed
- thread proxy-aware Linux sandbox flags and add minimal bwrap netns
isolation hook for restricted non-proxy runs
- add/refresh tests for proxy env wiring, Seatbelt policy generation,
and Linux sandbox argument wiring
This commit is contained in:
viyatb-oai
2026-02-09 23:44:21 -08:00
committed by GitHub
Unverified
parent b61ea47e83
commit 3391e5ea86
24 changed files with 1046 additions and 122 deletions
+28 -19
View File
@@ -26,9 +26,10 @@ use crate::protocol::ExecCommandOutputDeltaEvent;
use crate::protocol::ExecOutputStream;
use crate::protocol::SandboxPolicy;
use crate::sandboxing::CommandSpec;
use crate::sandboxing::ExecEnv;
use crate::sandboxing::ExecRequest;
use crate::sandboxing::SandboxManager;
use crate::sandboxing::SandboxPermissions;
use crate::spawn::SpawnChildRequest;
use crate::spawn::StdioPolicy;
use crate::spawn::spawn_child_async;
use crate::text_encoding::bytes_to_string_smart;
@@ -157,9 +158,18 @@ pub async fn process_exec_tool_call(
stdout_stream: Option<StdoutStream>,
) -> Result<ExecToolCallOutput> {
let windows_sandbox_level = params.windows_sandbox_level;
let enforce_managed_network = params.network.is_some();
let sandbox_type = match &sandbox_policy {
SandboxPolicy::DangerFullAccess | SandboxPolicy::ExternalSandbox { .. } => {
SandboxType::None
if enforce_managed_network {
get_platform_sandbox(
windows_sandbox_level
!= codex_protocol::config_types::WindowsSandboxLevel::Disabled,
)
.unwrap_or(SandboxType::None)
} else {
SandboxType::None
}
}
_ => get_platform_sandbox(
windows_sandbox_level != codex_protocol::config_types::WindowsSandboxLevel::Disabled,
@@ -200,11 +210,13 @@ pub async fn process_exec_tool_call(
};
let manager = SandboxManager::new();
let exec_env = manager
let exec_req = manager
.transform(crate::sandboxing::SandboxTransformRequest {
spec,
policy: sandbox_policy,
sandbox: sandbox_type,
enforce_managed_network,
network: network.as_ref(),
sandbox_policy_cwd: sandbox_cwd,
codex_linux_sandbox_exe: codex_linux_sandbox_exe.as_ref(),
use_linux_sandbox_bwrap,
@@ -213,19 +225,19 @@ pub async fn process_exec_tool_call(
.map_err(CodexErr::from)?;
// Route through the sandboxing module for a single, unified execution path.
crate::sandboxing::execute_env(exec_env, sandbox_policy, network, stdout_stream).await
crate::sandboxing::execute_env(exec_req, sandbox_policy, stdout_stream).await
}
pub(crate) async fn execute_exec_env(
env: ExecEnv,
env: ExecRequest,
sandbox_policy: &SandboxPolicy,
stdout_stream: Option<StdoutStream>,
network: Option<NetworkProxy>,
) -> Result<ExecToolCallOutput> {
let ExecEnv {
let ExecRequest {
command,
cwd,
mut env,
env,
network,
expiration,
sandbox,
windows_sandbox_level,
@@ -234,10 +246,6 @@ pub(crate) async fn execute_exec_env(
arg0,
} = env;
if let Some(network) = network.as_ref() {
network.apply_to_env(&mut env);
}
let params = ExecParams {
command,
cwd,
@@ -694,7 +702,7 @@ async fn exec(
command,
cwd,
env,
network: _,
network,
arg0,
expiration,
windows_sandbox_level: _,
@@ -708,15 +716,16 @@ async fn exec(
))
})?;
let arg0_ref = arg0.as_deref();
let child = spawn_child_async(
PathBuf::from(program),
args.into(),
arg0_ref,
let child = spawn_child_async(SpawnChildRequest {
program: PathBuf::from(program),
args: args.into(),
arg0: arg0_ref,
cwd,
sandbox_policy,
StdioPolicy::RedirectForShellTool,
network: network.as_ref(),
stdio_policy: StdioPolicy::RedirectForShellTool,
env,
)
})
.await?;
consume_truncated_output(child, expiration, stdout_stream).await
}