mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
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:
committed by
GitHub
Unverified
parent
b61ea47e83
commit
3391e5ea86
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Module: sandboxing
|
||||
|
||||
Build platform wrappers and produce ExecEnv for execution. Owns low‑level
|
||||
Build platform wrappers and produce ExecRequest for execution. Owns low-level
|
||||
sandbox placement and transformation of portable CommandSpec into a
|
||||
ready‑to‑spawn environment.
|
||||
*/
|
||||
@@ -11,6 +11,7 @@ use crate::exec::ExecToolCallOutput;
|
||||
use crate::exec::SandboxType;
|
||||
use crate::exec::StdoutStream;
|
||||
use crate::exec::execute_exec_env;
|
||||
use crate::landlock::allow_network_for_proxy;
|
||||
use crate::landlock::create_linux_sandbox_command_args;
|
||||
use crate::protocol::SandboxPolicy;
|
||||
#[cfg(target_os = "macos")]
|
||||
@@ -40,10 +41,11 @@ pub struct CommandSpec {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ExecEnv {
|
||||
pub struct ExecRequest {
|
||||
pub command: Vec<String>,
|
||||
pub cwd: PathBuf,
|
||||
pub env: HashMap<String, String>,
|
||||
pub network: Option<NetworkProxy>,
|
||||
pub expiration: ExecExpiration,
|
||||
pub sandbox: SandboxType,
|
||||
pub windows_sandbox_level: WindowsSandboxLevel,
|
||||
@@ -59,6 +61,10 @@ pub(crate) struct SandboxTransformRequest<'a> {
|
||||
pub spec: CommandSpec,
|
||||
pub policy: &'a SandboxPolicy,
|
||||
pub sandbox: SandboxType,
|
||||
pub enforce_managed_network: bool,
|
||||
// TODO(viyatb): Evaluate switching this to Option<Arc<NetworkProxy>>
|
||||
// to make shared ownership explicit across runtime/sandbox plumbing.
|
||||
pub network: Option<&'a NetworkProxy>,
|
||||
pub sandbox_policy_cwd: &'a Path,
|
||||
pub codex_linux_sandbox_exe: Option<&'a PathBuf>,
|
||||
pub use_linux_sandbox_bwrap: bool,
|
||||
@@ -93,6 +99,7 @@ impl SandboxManager {
|
||||
policy: &SandboxPolicy,
|
||||
pref: SandboxablePreference,
|
||||
windows_sandbox_level: WindowsSandboxLevel,
|
||||
has_managed_network_requirements: bool,
|
||||
) -> SandboxType {
|
||||
match pref {
|
||||
SandboxablePreference::Forbid => SandboxType::None,
|
||||
@@ -106,7 +113,14 @@ impl SandboxManager {
|
||||
}
|
||||
SandboxablePreference::Auto => match policy {
|
||||
SandboxPolicy::DangerFullAccess | SandboxPolicy::ExternalSandbox { .. } => {
|
||||
SandboxType::None
|
||||
if has_managed_network_requirements {
|
||||
crate::safety::get_platform_sandbox(
|
||||
windows_sandbox_level != WindowsSandboxLevel::Disabled,
|
||||
)
|
||||
.unwrap_or(SandboxType::None)
|
||||
} else {
|
||||
SandboxType::None
|
||||
}
|
||||
}
|
||||
_ => crate::safety::get_platform_sandbox(
|
||||
windows_sandbox_level != WindowsSandboxLevel::Disabled,
|
||||
@@ -119,11 +133,13 @@ impl SandboxManager {
|
||||
pub(crate) fn transform(
|
||||
&self,
|
||||
request: SandboxTransformRequest<'_>,
|
||||
) -> Result<ExecEnv, SandboxTransformError> {
|
||||
) -> Result<ExecRequest, SandboxTransformError> {
|
||||
let SandboxTransformRequest {
|
||||
mut spec,
|
||||
policy,
|
||||
sandbox,
|
||||
enforce_managed_network,
|
||||
network,
|
||||
sandbox_policy_cwd,
|
||||
codex_linux_sandbox_exe,
|
||||
use_linux_sandbox_bwrap,
|
||||
@@ -147,8 +163,13 @@ impl SandboxManager {
|
||||
SandboxType::MacosSeatbelt => {
|
||||
let mut seatbelt_env = HashMap::new();
|
||||
seatbelt_env.insert(CODEX_SANDBOX_ENV_VAR.to_string(), "seatbelt".to_string());
|
||||
let mut args =
|
||||
create_seatbelt_command_args(command.clone(), policy, sandbox_policy_cwd);
|
||||
let mut args = create_seatbelt_command_args(
|
||||
command.clone(),
|
||||
policy,
|
||||
sandbox_policy_cwd,
|
||||
enforce_managed_network,
|
||||
network,
|
||||
);
|
||||
let mut full_command = Vec::with_capacity(1 + args.len());
|
||||
full_command.push(MACOS_PATH_TO_SEATBELT_EXECUTABLE.to_string());
|
||||
full_command.append(&mut args);
|
||||
@@ -159,11 +180,13 @@ impl SandboxManager {
|
||||
SandboxType::LinuxSeccomp => {
|
||||
let exe = codex_linux_sandbox_exe
|
||||
.ok_or(SandboxTransformError::MissingLinuxSandboxExecutable)?;
|
||||
let allow_proxy_network = allow_network_for_proxy(enforce_managed_network);
|
||||
let mut args = create_linux_sandbox_command_args(
|
||||
command.clone(),
|
||||
policy,
|
||||
sandbox_policy_cwd,
|
||||
use_linux_sandbox_bwrap,
|
||||
allow_proxy_network,
|
||||
);
|
||||
let mut full_command = Vec::with_capacity(1 + args.len());
|
||||
full_command.push(exe.to_string_lossy().to_string());
|
||||
@@ -186,10 +209,11 @@ impl SandboxManager {
|
||||
|
||||
env.extend(sandbox_env);
|
||||
|
||||
Ok(ExecEnv {
|
||||
Ok(ExecRequest {
|
||||
command,
|
||||
cwd: spec.cwd,
|
||||
env,
|
||||
network: network.cloned(),
|
||||
expiration: spec.expiration,
|
||||
sandbox,
|
||||
windows_sandbox_level,
|
||||
@@ -205,10 +229,44 @@ impl SandboxManager {
|
||||
}
|
||||
|
||||
pub async fn execute_env(
|
||||
env: ExecEnv,
|
||||
env: ExecRequest,
|
||||
policy: &SandboxPolicy,
|
||||
network: Option<NetworkProxy>,
|
||||
stdout_stream: Option<StdoutStream>,
|
||||
) -> crate::error::Result<ExecToolCallOutput> {
|
||||
execute_exec_env(env, policy, stdout_stream, network).await
|
||||
execute_exec_env(env, policy, stdout_stream).await
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::SandboxManager;
|
||||
use crate::exec::SandboxType;
|
||||
use crate::protocol::SandboxPolicy;
|
||||
use crate::tools::sandboxing::SandboxablePreference;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn danger_full_access_defaults_to_no_sandbox_without_network_requirements() {
|
||||
let manager = SandboxManager::new();
|
||||
let sandbox = manager.select_initial(
|
||||
&SandboxPolicy::DangerFullAccess,
|
||||
SandboxablePreference::Auto,
|
||||
WindowsSandboxLevel::Disabled,
|
||||
false,
|
||||
);
|
||||
assert_eq!(sandbox, SandboxType::None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn danger_full_access_uses_platform_sandbox_with_network_requirements() {
|
||||
let manager = SandboxManager::new();
|
||||
let expected = crate::safety::get_platform_sandbox(false).unwrap_or(SandboxType::None);
|
||||
let sandbox = manager.select_initial(
|
||||
&SandboxPolicy::DangerFullAccess,
|
||||
SandboxablePreference::Auto,
|
||||
WindowsSandboxLevel::Disabled,
|
||||
true,
|
||||
);
|
||||
assert_eq!(sandbox, expected);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user