fix(sandboxing): scope detached children to shell tools

Co-authored-by: Codex noreply@openai.com
This commit is contained in:
viyatb-oai
2026-06-01 09:46:29 -07:00
parent e9c66b523a
commit 81bc6ca867
13 changed files with 50 additions and 1 deletions

View File

@@ -320,6 +320,7 @@ impl CommandExecRequestProcessor {
&sandbox_cwd,
&codex_linux_sandbox_exe,
use_legacy_landlock,
codex_core::exec::SandboxProcessLifetime::TerminateWithParent,
)
.map_err(|err| internal_error(format!("exec failed: {err}")))?;
self.command_exec_manager

View File

@@ -17,6 +17,7 @@ use codex_core::spawn::CODEX_SANDBOX_ENV_VAR;
use codex_core::spawn::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR;
use codex_protocol::config_types::SandboxMode;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_sandboxing::SandboxProcessLifetime;
use codex_sandboxing::landlock::allow_network_for_proxy;
use codex_sandboxing::landlock::create_linux_sandbox_command_args_for_permission_profile;
#[cfg(target_os = "macos")]
@@ -301,6 +302,7 @@ async fn run_command_under_sandbox(
permission_profile_cwd.as_path(),
use_legacy_landlock,
allow_network_for_proxy(managed_network_requirements_enabled),
SandboxProcessLifetime::TerminateWithParent,
);
spawn_debug_sandbox_child(
codex_linux_sandbox_exe,

View File

@@ -41,6 +41,7 @@ use codex_protocol::protocol::ExecOutputStream;
use codex_protocol::protocol::SandboxPolicy;
use codex_sandboxing::SandboxCommand;
use codex_sandboxing::SandboxManager;
pub use codex_sandboxing::SandboxProcessLifetime;
use codex_sandboxing::SandboxTransformRequest;
use codex_sandboxing::SandboxType;
use codex_sandboxing::SandboxablePreference;
@@ -307,6 +308,7 @@ pub async fn process_exec_tool_call(
sandbox_cwd,
codex_linux_sandbox_exe,
use_legacy_landlock,
SandboxProcessLifetime::AllowDetachedChildren,
)?;
// Route through the sandboxing module for a single, unified execution path.
@@ -321,6 +323,7 @@ pub fn build_exec_request(
sandbox_cwd: &AbsolutePathBuf,
codex_linux_sandbox_exe: &Option<PathBuf>,
use_legacy_landlock: bool,
process_lifetime: SandboxProcessLifetime,
) -> Result<ExecRequest> {
let ExecParams {
command,
@@ -382,6 +385,7 @@ pub fn build_exec_request(
sandbox_policy_cwd: sandbox_cwd,
codex_linux_sandbox_exe: codex_linux_sandbox_exe.as_deref(),
use_legacy_landlock,
process_lifetime,
windows_sandbox_level,
windows_sandbox_private_desktop,
})

View File

@@ -3,6 +3,7 @@ use crate::spawn::StdioPolicy;
use crate::spawn::spawn_child_async;
use codex_network_proxy::NetworkProxy;
use codex_protocol::models::PermissionProfile;
use codex_sandboxing::SandboxProcessLifetime;
use codex_sandboxing::landlock::CODEX_LINUX_SANDBOX_ARG0;
use codex_sandboxing::landlock::allow_network_for_proxy;
use codex_sandboxing::landlock::create_linux_sandbox_command_args_for_permission_profile;
@@ -41,6 +42,7 @@ where
sandbox_policy_cwd,
use_legacy_landlock,
allow_network_for_proxy(/*enforce_managed_network*/ false),
SandboxProcessLifetime::AllowDetachedChildren,
);
let codex_linux_sandbox_exe = codex_linux_sandbox_exe.as_ref();
// Preserve the helper alias when we already have it; otherwise force argv0

View File

@@ -43,6 +43,7 @@ use codex_protocol::protocol::NetworkPolicyRuleAction;
use codex_protocol::protocol::ReviewDecision;
use codex_sandboxing::SandboxCommand;
use codex_sandboxing::SandboxManager;
use codex_sandboxing::SandboxProcessLifetime;
use codex_sandboxing::SandboxTransformRequest;
use codex_sandboxing::SandboxType;
use codex_sandboxing::SandboxablePreference;
@@ -925,6 +926,7 @@ impl CoreShellCommandExecutor {
sandbox_policy_cwd: &self.sandbox_policy_cwd,
codex_linux_sandbox_exe: self.codex_linux_sandbox_exe.as_deref(),
use_legacy_landlock: self.use_legacy_landlock,
process_lifetime: SandboxProcessLifetime::AllowDetachedChildren,
windows_sandbox_level: self.windows_sandbox_level,
windows_sandbox_private_desktop: false,
})?;

View File

@@ -21,6 +21,7 @@ use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::ReviewDecision;
use codex_sandboxing::SandboxCommand;
use codex_sandboxing::SandboxManager;
use codex_sandboxing::SandboxProcessLifetime;
use codex_sandboxing::SandboxTransformError;
use codex_sandboxing::SandboxTransformRequest;
use codex_sandboxing::SandboxType;
@@ -409,6 +410,7 @@ impl<'a> SandboxAttempt<'a> {
.codex_linux_sandbox_exe
.map(std::path::PathBuf::as_path),
use_legacy_landlock: self.use_legacy_landlock,
process_lifetime: SandboxProcessLifetime::AllowDetachedChildren,
windows_sandbox_level: self.windows_sandbox_level,
windows_sandbox_private_desktop: self.windows_sandbox_private_desktop,
})

View File

@@ -11,6 +11,7 @@ use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_sandboxing::SandboxCommand;
use codex_sandboxing::SandboxExecRequest;
use codex_sandboxing::SandboxManager;
use codex_sandboxing::SandboxProcessLifetime;
use codex_sandboxing::SandboxTransformRequest;
use codex_sandboxing::SandboxablePreference;
use codex_utils_absolute_path::AbsolutePathBuf;
@@ -111,6 +112,7 @@ impl FileSystemSandboxRunner {
sandbox_policy_cwd: cwd.as_path(),
codex_linux_sandbox_exe: self.runtime_paths.codex_linux_sandbox_exe.as_deref(),
use_legacy_landlock: sandbox_context.use_legacy_landlock,
process_lifetime: SandboxProcessLifetime::TerminateWithParent,
windows_sandbox_level: sandbox_context.windows_sandbox_level,
windows_sandbox_private_desktop: sandbox_context.windows_sandbox_private_desktop,
})

View File

@@ -22,6 +22,7 @@ async fn spawn_command_under_sandbox(
) -> std::io::Result<Child> {
use codex_core::exec::ExecCapturePolicy;
use codex_core::exec::ExecParams;
use codex_core::exec::SandboxProcessLifetime;
use codex_core::exec::build_exec_request;
use codex_core::sandboxing::SandboxPermissions;
use codex_protocol::config_types::WindowsSandboxLevel;
@@ -46,6 +47,7 @@ async fn spawn_command_under_sandbox(
sandbox_cwd,
&codex_linux_sandbox_exe,
/*use_legacy_landlock*/ false,
SandboxProcessLifetime::AllowDetachedChildren,
)
.map_err(|err| io::Error::other(err.to_string()))?;

View File

@@ -1,3 +1,4 @@
use crate::SandboxProcessLifetime;
use codex_protocol::models::PermissionProfile;
use std::path::Path;
@@ -27,6 +28,7 @@ pub fn create_linux_sandbox_command_args_for_permission_profile(
sandbox_policy_cwd: &Path,
use_legacy_landlock: bool,
allow_network_for_proxy: bool,
process_lifetime: SandboxProcessLifetime,
) -> Vec<String> {
let permission_profile_json = serde_json::to_string(permission_profile)
.unwrap_or_else(|err| panic!("failed to serialize permission profile: {err}"));
@@ -53,7 +55,9 @@ pub fn create_linux_sandbox_command_args_for_permission_profile(
if allow_network_for_proxy {
linux_cmd.push("--allow-network-for-proxy".to_string());
}
linux_cmd.push("--allow-detached-children".to_string());
if process_lifetime == SandboxProcessLifetime::AllowDetachedChildren {
linux_cmd.push("--allow-detached-children".to_string());
}
linux_cmd.push("--".to_string());
linux_cmd.extend(command);
linux_cmd

View File

@@ -65,6 +65,7 @@ fn permission_profile_flag_is_included() {
cwd,
/*use_legacy_landlock*/ true,
/*allow_network_for_proxy*/ false,
SandboxProcessLifetime::TerminateWithParent,
);
assert_eq!(
@@ -77,6 +78,17 @@ fn permission_profile_flag_is_included() {
.any(|window| window[0] == "--command-cwd" && window[1] == "/tmp/link"),
true
);
assert!(!args.contains(&"--allow-detached-children".to_string()));
let args = create_linux_sandbox_command_args_for_permission_profile(
vec!["/bin/true".to_string()],
command_cwd,
&permission_profile,
cwd,
/*use_legacy_landlock*/ true,
/*allow_network_for_proxy*/ false,
SandboxProcessLifetime::AllowDetachedChildren,
);
assert!(args.contains(&"--allow-detached-children".to_string()));
}

View File

@@ -13,6 +13,7 @@ pub use bwrap::system_bwrap_warning;
pub use manager::SandboxCommand;
pub use manager::SandboxExecRequest;
pub use manager::SandboxManager;
pub use manager::SandboxProcessLifetime;
pub use manager::SandboxTransformError;
pub use manager::SandboxTransformRequest;
pub use manager::SandboxType;

View File

@@ -85,6 +85,13 @@ pub struct SandboxExecRequest {
pub arg0: Option<String>,
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum SandboxProcessLifetime {
#[default]
TerminateWithParent,
AllowDetachedChildren,
}
/// Bundled arguments for sandbox transformation.
///
/// This keeps call sites self-documenting when several fields are optional.
@@ -99,6 +106,7 @@ pub struct SandboxTransformRequest<'a> {
pub sandbox_policy_cwd: &'a Path,
pub codex_linux_sandbox_exe: Option<&'a Path>,
pub use_legacy_landlock: bool,
pub process_lifetime: SandboxProcessLifetime,
pub windows_sandbox_level: WindowsSandboxLevel,
pub windows_sandbox_private_desktop: bool,
}
@@ -178,6 +186,7 @@ impl SandboxManager {
sandbox_policy_cwd,
codex_linux_sandbox_exe,
use_legacy_landlock,
process_lifetime,
windows_sandbox_level,
windows_sandbox_private_desktop,
} = request;
@@ -232,6 +241,7 @@ impl SandboxManager {
sandbox_policy_cwd,
use_legacy_landlock,
allow_proxy_network,
process_lifetime,
);
let mut full_command = Vec::with_capacity(1 + args.len());
full_command.push(os_string_to_command_component(exe.as_os_str().to_owned()));

View File

@@ -1,5 +1,6 @@
use super::SandboxCommand;
use super::SandboxManager;
use super::SandboxProcessLifetime;
use super::SandboxTransformRequest;
use super::SandboxType;
use super::SandboxablePreference;
@@ -93,6 +94,7 @@ fn transform_preserves_unrestricted_file_system_policy_for_restricted_network()
sandbox_policy_cwd: cwd.as_path(),
codex_linux_sandbox_exe: None,
use_legacy_landlock: false,
process_lifetime: SandboxProcessLifetime::TerminateWithParent,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
windows_sandbox_private_desktop: false,
})
@@ -144,6 +146,7 @@ fn transform_additional_permissions_enable_network_for_external_sandbox() {
sandbox_policy_cwd: cwd.as_path(),
codex_linux_sandbox_exe: None,
use_legacy_landlock: false,
process_lifetime: SandboxProcessLifetime::TerminateWithParent,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
windows_sandbox_private_desktop: false,
})
@@ -212,6 +215,7 @@ fn transform_additional_permissions_preserves_denied_entries() {
sandbox_policy_cwd: cwd.as_path(),
codex_linux_sandbox_exe: None,
use_legacy_landlock: false,
process_lifetime: SandboxProcessLifetime::TerminateWithParent,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
windows_sandbox_private_desktop: false,
})
@@ -265,6 +269,7 @@ fn transform_linux_seccomp_request(
sandbox_policy_cwd: cwd.as_path(),
codex_linux_sandbox_exe: Some(codex_linux_sandbox_exe),
use_legacy_landlock: false,
process_lifetime: SandboxProcessLifetime::TerminateWithParent,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
windows_sandbox_private_desktop: false,
})