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
parent b61ea47e83
commit 3391e5ea86
24 changed files with 1046 additions and 122 deletions

View File

@@ -4,7 +4,7 @@
//! - Manages interactive processes (create, reuse, buffer output with caps).
//! - Uses the shared ToolOrchestrator to handle approval, sandbox selection, and
//! retry semantics in a single, descriptive flow.
//! - Spawns the PTY from a sandboxtransformed `ExecEnv`; on sandbox denial,
//! - Spawns the PTY from a sandbox-transformed `ExecRequest`; on sandbox denial,
//! retries without sandbox when policy allows (no reprompt thanks to caching).
//! - Uses the shared `is_likely_sandbox_denied` heuristic to keep denial messages
//! consistent with other exec paths.
@@ -12,7 +12,7 @@
//! Flow at a glance (open process)
//! 1) Build a small request `{ command, cwd }`.
//! 2) Orchestrator: approval (bypass/cache/prompt) → select sandbox → run.
//! 3) Runtime: transform `CommandSpec` `ExecEnv` → spawn PTY.
//! 3) Runtime: transform `CommandSpec` -> `ExecRequest` -> spawn PTY.
//! 4) If denial, orchestrator retries with `SandboxType::None`.
//! 5) Process handle is returned with streaming output + metadata.
//!
@@ -27,6 +27,7 @@ use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
use codex_network_proxy::NetworkProxy;
use rand::Rng;
use rand::rng;
use tokio::sync::Mutex;
@@ -79,6 +80,7 @@ pub(crate) struct ExecCommandRequest {
pub yield_time_ms: u64,
pub max_output_tokens: Option<usize>,
pub workdir: Option<PathBuf>,
pub network: Option<NetworkProxy>,
pub tty: bool,
pub sandbox_permissions: SandboxPermissions,
pub justification: Option<String>,
@@ -203,6 +205,7 @@ mod tests {
yield_time_ms,
max_output_tokens: None,
workdir: None,
network: None,
tty: true,
sandbox_permissions: SandboxPermissions::UseDefault,
justification: None,

View File

@@ -14,7 +14,7 @@ use tokio_util::sync::CancellationToken;
use crate::exec_env::create_env;
use crate::exec_policy::ExecApprovalRequest;
use crate::protocol::ExecCommandSource;
use crate::sandboxing::ExecEnv;
use crate::sandboxing::ExecRequest;
use crate::tools::events::ToolEmitter;
use crate::tools::events::ToolEventCtx;
use crate::tools::events::ToolEventStage;
@@ -460,7 +460,7 @@ impl UnifiedExecProcessManager {
pub(crate) async fn open_session_with_exec_env(
&self,
env: &ExecEnv,
env: &ExecRequest,
tty: bool,
) -> Result<UnifiedExecProcess, UnifiedExecError> {
let (program, args) = env
@@ -520,7 +520,7 @@ impl UnifiedExecProcessManager {
command: request.command.clone(),
cwd,
env,
network: context.turn.config.network.clone(),
network: request.network.clone(),
tty: request.tty,
sandbox_permissions: request.sandbox_permissions,
justification: request.justification.clone(),