Compare commits

...

7 Commits

Author SHA1 Message Date
pakrym-oai
5754a6c145 Increase short linux sandbox test timeout 2026-03-23 09:30:37 -07:00
pakrym-oai
fb5e86e8fc Increase linux sandbox test timeouts 2026-03-23 09:28:58 -07:00
pakrym-oai
dc6abde998 Run linux sandbox tests against helper binary 2026-03-23 09:24:45 -07:00
pakrym-oai
f0124c0f0d Simplify linux sandbox test arg0 dispatch 2026-03-23 09:21:40 -07:00
pakrym-oai
1b5636e0fb Dispatch linux sandbox integration tests by arg0 2026-03-23 09:17:58 -07:00
pakrym-oai
6182298401 Fix Linux sandbox helper signature 2026-03-23 08:50:12 -07:00
pakrym-oai
aca877b2d4 Use current_exe for linux sandbox launches 2026-03-22 18:37:47 -07:00
46 changed files with 168 additions and 304 deletions

2
codex-rs/Cargo.lock generated
View File

@@ -1985,6 +1985,7 @@ dependencies = [
"codex-utils-oss",
"codex-utils-sandbox-summary",
"core_test_support",
"ctor 0.6.3",
"libc",
"opentelemetry",
"opentelemetry_sdk",
@@ -2170,6 +2171,7 @@ dependencies = [
"codex-core",
"codex-protocol",
"codex-utils-absolute-path",
"ctor 0.6.3",
"landlock",
"libc",
"pkg-config",

View File

@@ -518,7 +518,6 @@ impl CodexMessageProcessor {
message: format!("failed to reload config: {err}"),
data: None,
})?;
config.codex_linux_sandbox_exe = self.arg0_paths.codex_linux_sandbox_exe.clone();
config.main_execve_wrapper_exe = self.arg0_paths.main_execve_wrapper_exe.clone();
Ok(config)
}
@@ -1731,7 +1730,6 @@ impl CodexMessageProcessor {
),
};
let codex_linux_sandbox_exe = self.arg0_paths.codex_linux_sandbox_exe.clone();
let outgoing = self.outgoing.clone();
let request_for_task = request.clone();
let started_network_proxy_for_task = started_network_proxy;
@@ -1751,7 +1749,6 @@ impl CodexMessageProcessor {
&effective_file_system_sandbox_policy,
effective_network_sandbox_policy,
sandbox_cwd.as_path(),
&codex_linux_sandbox_exe,
use_legacy_landlock,
) {
Ok(exec_request) => {
@@ -2153,7 +2150,6 @@ impl CodexMessageProcessor {
approvals_reviewer: approvals_reviewer
.map(codex_app_server_protocol::ApprovalsReviewer::to_core),
sandbox_mode: sandbox.map(SandboxMode::to_core),
codex_linux_sandbox_exe: self.arg0_paths.codex_linux_sandbox_exe.clone(),
main_execve_wrapper_exe: self.arg0_paths.main_execve_wrapper_exe.clone(),
base_instructions,
developer_instructions,

View File

@@ -19,7 +19,6 @@ const TOKIO_WORKER_STACK_SIZE_BYTES: usize = 16 * 1024 * 1024;
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Arg0DispatchPaths {
pub codex_linux_sandbox_exe: Option<PathBuf>,
pub main_execve_wrapper_exe: Option<PathBuf>,
}
@@ -133,9 +132,7 @@ pub fn arg0_dispatch() -> Option<Arg0PathEntryGuard> {
///
/// 1. Load `.env` values from `~/.codex/.env` before creating any threads.
/// 2. Construct a Tokio multi-thread runtime.
/// 3. Derive the path to the current executable (so children can re-invoke the
/// sandbox) when running on Linux.
/// 4. Execute the provided async `main_fn` inside that runtime, forwarding any
/// 3. Execute the provided async `main_fn` inside that runtime, forwarding any
/// error. Note that `main_fn` receives [`Arg0DispatchPaths`], which
/// contains the helper executable paths needed to construct
/// [`codex_core::config::Config`].
@@ -156,17 +153,7 @@ where
// async entry-point.
let runtime = build_runtime()?;
runtime.block_on(async move {
let current_exe = std::env::current_exe().ok();
let paths = Arg0DispatchPaths {
codex_linux_sandbox_exe: if cfg!(target_os = "linux") {
current_exe.or_else(|| {
path_entry
.as_ref()
.and_then(|path_entry| path_entry.paths().codex_linux_sandbox_exe.clone())
})
} else {
None
},
main_execve_wrapper_exe: path_entry
.as_ref()
.and_then(|path_entry| path_entry.paths().main_execve_wrapper_exe.clone()),
@@ -324,16 +311,6 @@ pub fn prepend_path_entry_for_codex_aliases() -> std::io::Result<Arg0PathEntryGu
}
let paths = Arg0DispatchPaths {
codex_linux_sandbox_exe: {
#[cfg(target_os = "linux")]
{
Some(path.join(LINUX_SANDBOX_ARG0))
}
#[cfg(not(target_os = "linux"))]
{
None
}
},
main_execve_wrapper_exe: {
#[cfg(unix)]
{

View File

@@ -33,10 +33,7 @@ use crate::exit_status::handle_exit_status;
use seatbelt::DenialLogger;
#[cfg(target_os = "macos")]
pub async fn run_command_under_seatbelt(
command: SeatbeltCommand,
codex_linux_sandbox_exe: Option<PathBuf>,
) -> anyhow::Result<()> {
pub async fn run_command_under_seatbelt(command: SeatbeltCommand) -> anyhow::Result<()> {
let SeatbeltCommand {
full_auto,
log_denials,
@@ -47,7 +44,6 @@ pub async fn run_command_under_seatbelt(
full_auto,
command,
config_overrides,
codex_linux_sandbox_exe,
SandboxType::Seatbelt,
log_denials,
)
@@ -55,17 +51,11 @@ pub async fn run_command_under_seatbelt(
}
#[cfg(not(target_os = "macos"))]
pub async fn run_command_under_seatbelt(
_command: SeatbeltCommand,
_codex_linux_sandbox_exe: Option<PathBuf>,
) -> anyhow::Result<()> {
pub async fn run_command_under_seatbelt(_command: SeatbeltCommand) -> anyhow::Result<()> {
anyhow::bail!("Seatbelt sandbox is only available on macOS");
}
pub async fn run_command_under_landlock(
command: LandlockCommand,
codex_linux_sandbox_exe: Option<PathBuf>,
) -> anyhow::Result<()> {
pub async fn run_command_under_landlock(command: LandlockCommand) -> anyhow::Result<()> {
let LandlockCommand {
full_auto,
config_overrides,
@@ -75,17 +65,13 @@ pub async fn run_command_under_landlock(
full_auto,
command,
config_overrides,
codex_linux_sandbox_exe,
SandboxType::Landlock,
/*log_denials*/ false,
)
.await
}
pub async fn run_command_under_windows(
command: WindowsCommand,
codex_linux_sandbox_exe: Option<PathBuf>,
) -> anyhow::Result<()> {
pub async fn run_command_under_windows(command: WindowsCommand) -> anyhow::Result<()> {
let WindowsCommand {
full_auto,
config_overrides,
@@ -95,7 +81,6 @@ pub async fn run_command_under_windows(
full_auto,
command,
config_overrides,
codex_linux_sandbox_exe,
SandboxType::Windows,
/*log_denials*/ false,
)
@@ -113,7 +98,6 @@ async fn run_command_under_sandbox(
full_auto: bool,
command: Vec<String>,
config_overrides: CliConfigOverrides,
codex_linux_sandbox_exe: Option<PathBuf>,
sandbox_type: SandboxType,
log_denials: bool,
) -> anyhow::Result<()> {
@@ -121,7 +105,6 @@ async fn run_command_under_sandbox(
config_overrides
.parse_overrides()
.map_err(anyhow::Error::msg)?,
codex_linux_sandbox_exe,
full_auto,
)
.await?;
@@ -273,10 +256,6 @@ async fn run_command_under_sandbox(
.await?
}
SandboxType::Landlock => {
#[expect(clippy::expect_used)]
let codex_linux_sandbox_exe = config
.codex_linux_sandbox_exe
.expect("codex-linux-sandbox executable not found");
let use_legacy_landlock = config.features.use_legacy_landlock();
let args = create_linux_sandbox_command_args_for_policies(
command,
@@ -290,7 +269,7 @@ async fn run_command_under_sandbox(
);
let network_policy = config.permissions.network_sandbox_policy;
spawn_debug_sandbox_child(
codex_linux_sandbox_exe,
std::env::current_exe()?,
args,
Some("codex-linux-sandbox"),
cwd,
@@ -373,30 +352,19 @@ async fn spawn_debug_sandbox_child(
async fn load_debug_sandbox_config(
cli_overrides: Vec<(String, TomlValue)>,
codex_linux_sandbox_exe: Option<PathBuf>,
full_auto: bool,
) -> anyhow::Result<Config> {
load_debug_sandbox_config_with_codex_home(
cli_overrides,
codex_linux_sandbox_exe,
full_auto,
/*codex_home*/ None,
)
.await
load_debug_sandbox_config_with_codex_home(cli_overrides, full_auto, /*codex_home*/ None).await
}
async fn load_debug_sandbox_config_with_codex_home(
cli_overrides: Vec<(String, TomlValue)>,
codex_linux_sandbox_exe: Option<PathBuf>,
full_auto: bool,
codex_home: Option<PathBuf>,
) -> anyhow::Result<Config> {
let config = build_debug_sandbox_config(
cli_overrides.clone(),
ConfigOverrides {
codex_linux_sandbox_exe: codex_linux_sandbox_exe.clone(),
..Default::default()
},
ConfigOverrides::default(),
codex_home.clone(),
)
.await?;
@@ -414,7 +382,6 @@ async fn load_debug_sandbox_config_with_codex_home(
cli_overrides,
ConfigOverrides {
sandbox_mode: Some(create_sandbox_mode(full_auto)),
codex_linux_sandbox_exe,
..Default::default()
},
codex_home,
@@ -503,13 +470,9 @@ mod tests {
)
.await?;
let config = load_debug_sandbox_config_with_codex_home(
Vec::new(),
None,
false,
Some(codex_home_path),
)
.await?;
let config =
load_debug_sandbox_config_with_codex_home(Vec::new(), false, Some(codex_home_path))
.await?;
assert!(config_uses_permission_profiles(&config));
assert!(
@@ -539,7 +502,6 @@ mod tests {
let err = load_debug_sandbox_config_with_codex_home(
Vec::new(),
None,
true,
Some(codex_home.path().to_path_buf()),
)

View File

@@ -783,8 +783,7 @@ async fn cli_main(arg0_paths: Arg0DispatchPaths) -> anyhow::Result<()> {
&mut cloud_cli.config_overrides,
root_config_overrides.clone(),
);
codex_cloud_tasks::run_main(cloud_cli, arg0_paths.codex_linux_sandbox_exe.clone())
.await?;
codex_cloud_tasks::run_main(cloud_cli).await?;
}
Some(Subcommand::Sandbox(sandbox_args)) => match sandbox_args.cmd {
SandboxCommand::Macos(mut seatbelt_cli) => {
@@ -793,11 +792,7 @@ async fn cli_main(arg0_paths: Arg0DispatchPaths) -> anyhow::Result<()> {
&mut seatbelt_cli.config_overrides,
root_config_overrides.clone(),
);
codex_cli::debug_sandbox::run_command_under_seatbelt(
seatbelt_cli,
arg0_paths.codex_linux_sandbox_exe.clone(),
)
.await?;
codex_cli::debug_sandbox::run_command_under_seatbelt(seatbelt_cli).await?;
}
SandboxCommand::Linux(mut landlock_cli) => {
reject_remote_mode_for_subcommand(root_remote.as_deref(), "sandbox linux")?;
@@ -805,11 +800,7 @@ async fn cli_main(arg0_paths: Arg0DispatchPaths) -> anyhow::Result<()> {
&mut landlock_cli.config_overrides,
root_config_overrides.clone(),
);
codex_cli::debug_sandbox::run_command_under_landlock(
landlock_cli,
arg0_paths.codex_linux_sandbox_exe.clone(),
)
.await?;
codex_cli::debug_sandbox::run_command_under_landlock(landlock_cli).await?;
}
SandboxCommand::Windows(mut windows_cli) => {
reject_remote_mode_for_subcommand(root_remote.as_deref(), "sandbox windows")?;
@@ -817,11 +808,7 @@ async fn cli_main(arg0_paths: Arg0DispatchPaths) -> anyhow::Result<()> {
&mut windows_cli.config_overrides,
root_config_overrides.clone(),
);
codex_cli::debug_sandbox::run_command_under_windows(
windows_cli,
arg0_paths.codex_linux_sandbox_exe.clone(),
)
.await?;
codex_cli::debug_sandbox::run_command_under_windows(windows_cli).await?;
}
},
Some(Subcommand::Debug(DebugCommand { subcommand })) => match subcommand {

View File

@@ -15,7 +15,6 @@ use owo_colors::Stream;
use std::cmp::Ordering;
use std::io::IsTerminal;
use std::io::Read;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
use std::time::Instant;
@@ -729,7 +728,7 @@ fn spawn_apply(
// (no standalone patch summarizer needed UI displays raw diffs)
/// Entry point for the `codex cloud` subcommand.
pub async fn run_main(cli: Cli, _codex_linux_sandbox_exe: Option<PathBuf>) -> anyhow::Result<()> {
pub async fn run_main(cli: Cli) -> anyhow::Result<()> {
if let Some(command) = cli.command {
return match command {
crate::cli::Command::Exec(args) => run_exec_command(args).await,

View File

@@ -254,7 +254,6 @@ mod reload {
ConfigOverrides {
cwd: Some(config.cwd.clone()),
model_provider: preserve_current_provider.then(|| config.model_provider_id.clone()),
codex_linux_sandbox_exe: config.codex_linux_sandbox_exe.clone(),
main_execve_wrapper_exe: config.main_execve_wrapper_exe.clone(),
js_repl_node_path: config.js_repl_node_path.clone(),
..Default::default()

View File

@@ -160,7 +160,6 @@ async fn apply_role_preserves_unspecified_keys() {
TomlValue::String("base-model".to_string()),
)])
.await;
config.codex_linux_sandbox_exe = Some(PathBuf::from("/tmp/codex-linux-sandbox"));
config.main_execve_wrapper_exe = Some(PathBuf::from("/tmp/codex-execve-wrapper"));
let role_path = write_role_config(
&home,
@@ -183,10 +182,6 @@ async fn apply_role_preserves_unspecified_keys() {
assert_eq!(config.model.as_deref(), Some("base-model"));
assert_eq!(config.model_reasoning_effort, Some(ReasoningEffort::High));
assert_eq!(
config.codex_linux_sandbox_exe,
Some(PathBuf::from("/tmp/codex-linux-sandbox"))
);
assert_eq!(
config.main_execve_wrapper_exe,
Some(PathBuf::from("/tmp/codex-execve-wrapper"))

View File

@@ -823,7 +823,6 @@ pub(crate) struct TurnContext {
pub(crate) features: ManagedFeatures,
pub(crate) ghost_snapshot: GhostSnapshotConfig,
pub(crate) final_output_json_schema: Option<Value>,
pub(crate) codex_linux_sandbox_exe: Option<PathBuf>,
pub(crate) tool_call_gate: Arc<ReadinessFlag>,
pub(crate) truncation_policy: TruncationPolicy,
pub(crate) js_repl: Arc<JsReplHandle>,
@@ -930,7 +929,6 @@ impl TurnContext {
features,
ghost_snapshot: self.ghost_snapshot.clone(),
final_output_json_schema: self.final_output_json_schema.clone(),
codex_linux_sandbox_exe: self.codex_linux_sandbox_exe.clone(),
tool_call_gate: Arc::new(ReadinessFlag::new()),
truncation_policy,
js_repl: Arc::clone(&self.js_repl),
@@ -1378,7 +1376,6 @@ impl Session {
features: per_turn_config.features.clone(),
ghost_snapshot: per_turn_config.ghost_snapshot.clone(),
final_output_json_schema: None,
codex_linux_sandbox_exe: per_turn_config.codex_linux_sandbox_exe.clone(),
tool_call_gate: Arc::new(ReadinessFlag::new()),
truncation_policy: model_info.truncation_policy.into(),
js_repl,
@@ -1906,7 +1903,6 @@ impl Session {
// MCP server immediately after it becomes ready (avoiding blocking).
let sandbox_state = SandboxState {
sandbox_policy: session_configuration.sandbox_policy.get().clone(),
codex_linux_sandbox_exe: config.codex_linux_sandbox_exe.clone(),
sandbox_cwd: session_configuration.cwd.clone(),
use_legacy_landlock: config.features.use_legacy_landlock(),
};
@@ -2379,7 +2375,6 @@ impl Session {
if sandbox_policy_changed {
let sandbox_state = SandboxState {
sandbox_policy: per_turn_config.permissions.sandbox_policy.get().clone(),
codex_linux_sandbox_exe: per_turn_config.codex_linux_sandbox_exe.clone(),
sandbox_cwd: per_turn_config.cwd.clone(),
use_legacy_landlock: per_turn_config.features.use_legacy_landlock(),
};
@@ -4050,7 +4045,6 @@ impl Session {
let auth_statuses = compute_auth_statuses(mcp_servers.iter(), store_mode).await;
let sandbox_state = SandboxState {
sandbox_policy: turn_context.sandbox_policy.get().clone(),
codex_linux_sandbox_exe: turn_context.codex_linux_sandbox_exe.clone(),
sandbox_cwd: turn_context.cwd.clone(),
use_legacy_landlock: turn_context.features.use_legacy_landlock(),
};
@@ -5276,7 +5270,6 @@ async fn spawn_review_thread(
shell_environment_policy: parent_turn_context.shell_environment_policy.clone(),
cwd: parent_turn_context.cwd.clone(),
final_output_json_schema: None,
codex_linux_sandbox_exe: parent_turn_context.codex_linux_sandbox_exe.clone(),
tool_call_gate: Arc::new(ReadinessFlag::new()),
js_repl: Arc::clone(&sess.js_repl),
dynamic_tools: parent_turn_context.dynamic_tools.clone(),

View File

@@ -24,7 +24,6 @@ use codex_protocol::models::function_call_output_content_items_to_text;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_utils_absolute_path::AbsolutePathBuf;
use core_test_support::codex_linux_sandbox_exe_or_skip;
use core_test_support::responses::ev_assistant_message;
use core_test_support::responses::ev_completed;
use core_test_support::responses::ev_response_created;
@@ -68,7 +67,6 @@ async fn guardian_allows_shell_additional_permissions_requests_past_policy_valid
.await;
let (mut session, mut turn_context_raw) = make_session_and_context().await;
turn_context_raw.codex_linux_sandbox_exe = codex_linux_sandbox_exe_or_skip!();
turn_context_raw
.approval_policy
.set(AskForApproval::OnRequest)

View File

@@ -4298,7 +4298,6 @@ fn test_precedence_fixture_with_o3_profile() -> std::io::Result<()> {
history: History::default(),
ephemeral: false,
file_opener: UriBasedFileOpener::VsCode,
codex_linux_sandbox_exe: None,
main_execve_wrapper_exe: None,
js_repl_node_path: None,
js_repl_node_module_dirs: Vec::new(),
@@ -4441,7 +4440,6 @@ fn test_precedence_fixture_with_gpt3_profile() -> std::io::Result<()> {
history: History::default(),
ephemeral: false,
file_opener: UriBasedFileOpener::VsCode,
codex_linux_sandbox_exe: None,
main_execve_wrapper_exe: None,
js_repl_node_path: None,
js_repl_node_module_dirs: Vec::new(),
@@ -4582,7 +4580,6 @@ fn test_precedence_fixture_with_zdr_profile() -> std::io::Result<()> {
history: History::default(),
ephemeral: false,
file_opener: UriBasedFileOpener::VsCode,
codex_linux_sandbox_exe: None,
main_execve_wrapper_exe: None,
js_repl_node_path: None,
js_repl_node_module_dirs: Vec::new(),
@@ -4709,7 +4706,6 @@ fn test_precedence_fixture_with_gpt5_profile() -> std::io::Result<()> {
history: History::default(),
ephemeral: false,
file_opener: UriBasedFileOpener::VsCode,
codex_linux_sandbox_exe: None,
main_execve_wrapper_exe: None,
js_repl_node_path: None,
js_repl_node_module_dirs: Vec::new(),

View File

@@ -450,14 +450,6 @@ pub struct Config {
/// output will be hyperlinked using the specified URI scheme.
pub file_opener: UriBasedFileOpener,
/// Path to the `codex-linux-sandbox` executable. This must be set if
/// [`crate::exec::SandboxType::LinuxSeccomp`] is used. Note that this
/// cannot be set in the config file: it must be set in code via
/// [`ConfigOverrides`].
///
/// When this program is invoked, arg0 will be set to `codex-linux-sandbox`.
pub codex_linux_sandbox_exe: Option<PathBuf>,
/// Path to the `codex-execve-wrapper` executable used for shell
/// escalation. This cannot be set in the config file: it must be set in
/// code via [`ConfigOverrides`].
@@ -846,8 +838,7 @@ impl Config {
/// designed to use [AskForApproval::Never] exclusively.
///
/// Further, [ConfigOverrides] contains some options that are not supported
/// in [ConfigToml], such as `cwd`, `codex_linux_sandbox_exe`, and
/// `main_execve_wrapper_exe`.
/// in [ConfigToml], such as `cwd` and `main_execve_wrapper_exe`.
pub async fn load_with_cli_overrides_and_harness_overrides(
cli_overrides: Vec<(String, TomlValue)>,
harness_overrides: ConfigOverrides,
@@ -1931,7 +1922,6 @@ pub struct ConfigOverrides {
pub model_provider: Option<String>,
pub service_tier: Option<Option<ServiceTier>>,
pub config_profile: Option<String>,
pub codex_linux_sandbox_exe: Option<PathBuf>,
pub main_execve_wrapper_exe: Option<PathBuf>,
pub js_repl_node_path: Option<PathBuf>,
pub js_repl_node_module_dirs: Option<Vec<PathBuf>>,
@@ -2129,7 +2119,6 @@ impl Config {
model_provider,
service_tier: service_tier_override,
config_profile: config_profile_key,
codex_linux_sandbox_exe,
main_execve_wrapper_exe,
js_repl_node_path: js_repl_node_path_override,
js_repl_node_module_dirs: js_repl_node_module_dirs_override,
@@ -2730,7 +2719,6 @@ impl Config {
history,
ephemeral: ephemeral.unwrap_or_default(),
file_opener: cfg.file_opener.unwrap_or(UriBasedFileOpener::VsCode),
codex_linux_sandbox_exe,
main_execve_wrapper_exe,
js_repl_node_path,
js_repl_node_module_dirs,

View File

@@ -211,7 +211,6 @@ pub async fn list_accessible_connectors_from_mcp_tools_with_options_and_status(
let sandbox_state = SandboxState {
sandbox_policy: SandboxPolicy::new_read_only_policy(),
codex_linux_sandbox_exe: config.codex_linux_sandbox_exe.clone(),
sandbox_cwd: env::current_dir().unwrap_or_else(|_| PathBuf::from("/")),
use_legacy_landlock: config.features.use_legacy_landlock(),
};

View File

@@ -151,9 +151,6 @@ pub enum CodexErr {
#[error("sandbox error: {0}")]
Sandbox(#[from] SandboxErr),
#[error("codex-linux-sandbox was required but not provided")]
LandlockSandboxExecutableNotProvided,
#[error("unsupported operation: {0}")]
UnsupportedOperation(String),
@@ -207,7 +204,6 @@ impl CodexErr {
| CodexErr::RefreshTokenFailed(_)
| CodexErr::UnsupportedOperation(_)
| CodexErr::Sandbox(_)
| CodexErr::LandlockSandboxExecutableNotProvided
| CodexErr::RetryLimit(_)
| CodexErr::ContextWindowExceeded
| CodexErr::ThreadNotFound(_)

View File

@@ -217,7 +217,6 @@ pub async fn process_exec_tool_call(
file_system_sandbox_policy: &FileSystemSandboxPolicy,
network_sandbox_policy: NetworkSandboxPolicy,
sandbox_cwd: &Path,
codex_linux_sandbox_exe: &Option<PathBuf>,
use_legacy_landlock: bool,
stdout_stream: Option<StdoutStream>,
) -> Result<ExecToolCallOutput> {
@@ -227,7 +226,6 @@ pub async fn process_exec_tool_call(
file_system_sandbox_policy,
network_sandbox_policy,
sandbox_cwd,
codex_linux_sandbox_exe,
use_legacy_landlock,
)?;
@@ -243,7 +241,6 @@ pub fn build_exec_request(
file_system_sandbox_policy: &FileSystemSandboxPolicy,
network_sandbox_policy: NetworkSandboxPolicy,
sandbox_cwd: &Path,
codex_linux_sandbox_exe: &Option<PathBuf>,
use_legacy_landlock: bool,
) -> Result<ExecRequest> {
let windows_sandbox_level = params.windows_sandbox_level;
@@ -304,7 +301,6 @@ pub fn build_exec_request(
sandbox_policy_cwd: sandbox_cwd,
#[cfg(target_os = "macos")]
macos_seatbelt_profile_extensions: None,
codex_linux_sandbox_exe: codex_linux_sandbox_exe.as_ref(),
use_legacy_landlock,
windows_sandbox_level,
windows_sandbox_private_desktop,
@@ -625,9 +621,7 @@ pub(crate) mod errors {
impl From<SandboxTransformError> for CodexErr {
fn from(err: SandboxTransformError) -> Self {
match err {
SandboxTransformError::MissingLinuxSandboxExecutable => {
CodexErr::LandlockSandboxExecutableNotProvided
}
SandboxTransformError::CurrentExecutableUnavailable(err) => CodexErr::Io(err),
#[cfg(not(target_os = "macos"))]
SandboxTransformError::SeatbeltUnavailable => CodexErr::UnsupportedOperation(
"seatbelt sandbox is only available on macOS".to_string(),

View File

@@ -358,7 +358,6 @@ async fn process_exec_tool_call_preserves_full_buffer_capture_policy() -> Result
&FileSystemSandboxPolicy::from(&sandbox_policy),
NetworkSandboxPolicy::Enabled,
cwd.as_path(),
&None,
false,
None,
)
@@ -662,7 +661,6 @@ async fn process_exec_tool_call_respects_cancellation_token() -> Result<()> {
&FileSystemSandboxPolicy::from(&SandboxPolicy::DangerFullAccess),
NetworkSandboxPolicy::Enabled,
cwd.as_path(),
&None,
false,
None,
)

View File

@@ -115,7 +115,6 @@ struct GuardianReviewSessionReuseKey {
compact_prompt: Option<String>,
cwd: PathBuf,
mcp_servers: Constrained<HashMap<String, McpServerConfig>>,
codex_linux_sandbox_exe: Option<PathBuf>,
main_execve_wrapper_exe: Option<PathBuf>,
js_repl_node_path: Option<PathBuf>,
js_repl_node_module_dirs: Vec<PathBuf>,
@@ -142,7 +141,6 @@ impl GuardianReviewSessionReuseKey {
compact_prompt: spawn_config.compact_prompt.clone(),
cwd: spawn_config.cwd.clone(),
mcp_servers: spawn_config.mcp_servers.clone(),
codex_linux_sandbox_exe: spawn_config.codex_linux_sandbox_exe.clone(),
main_execve_wrapper_exe: spawn_config.main_execve_wrapper_exe.clone(),
js_repl_node_path: spawn_config.js_repl_node_path.clone(),
js_repl_node_module_dirs: spawn_config.js_repl_node_module_dirs.clone(),

View File

@@ -19,8 +19,7 @@ use tokio::process::Child;
/// split filesystem/network policies as JSON so the helper can migrate
/// incrementally without breaking older call sites.
#[allow(clippy::too_many_arguments)]
pub async fn spawn_command_under_linux_sandbox<P>(
codex_linux_sandbox_exe: P,
pub async fn spawn_command_under_linux_sandbox(
command: Vec<String>,
command_cwd: PathBuf,
sandbox_policy: &SandboxPolicy,
@@ -29,10 +28,7 @@ pub async fn spawn_command_under_linux_sandbox<P>(
stdio_policy: StdioPolicy,
network: Option<&NetworkProxy>,
env: HashMap<String, String>,
) -> std::io::Result<Child>
where
P: AsRef<Path>,
{
) -> std::io::Result<Child> {
let file_system_sandbox_policy =
FileSystemSandboxPolicy::from_legacy_sandbox_policy(sandbox_policy, sandbox_policy_cwd);
let network_sandbox_policy = NetworkSandboxPolicy::from(sandbox_policy);
@@ -48,7 +44,7 @@ where
);
let arg0 = Some("codex-linux-sandbox");
spawn_child_async(SpawnChildRequest {
program: codex_linux_sandbox_exe.as_ref().to_path_buf(),
program: std::env::current_exe()?,
args,
arg0,
cwd: command_cwd,

View File

@@ -277,7 +277,6 @@ pub async fn collect_mcp_snapshot(config: &Config) -> McpListToolsResponseEvent
// Use ReadOnly sandbox policy for MCP snapshot collection (safest default)
let sandbox_state = SandboxState {
sandbox_policy: SandboxPolicy::new_read_only_policy(),
codex_linux_sandbox_exe: config.codex_linux_sandbox_exe.clone(),
sandbox_cwd: env::current_dir().unwrap_or_else(|_| PathBuf::from("/")),
use_legacy_landlock: config.features.use_legacy_landlock(),
};

View File

@@ -588,7 +588,6 @@ pub const MCP_SANDBOX_STATE_METHOD: &str = "codex/sandbox-state/update";
#[serde(rename_all = "camelCase")]
pub struct SandboxState {
pub sandbox_policy: SandboxPolicy,
pub codex_linux_sandbox_exe: Option<PathBuf>,
pub sandbox_cwd: PathBuf,
#[serde(default)]
pub use_legacy_landlock: bool,

View File

@@ -97,7 +97,6 @@ pub(crate) struct SandboxTransformRequest<'a> {
pub sandbox_policy_cwd: &'a Path,
#[cfg(target_os = "macos")]
pub macos_seatbelt_profile_extensions: Option<&'a MacOsSeatbeltProfileExtensions>,
pub codex_linux_sandbox_exe: Option<&'a PathBuf>,
pub use_legacy_landlock: bool,
pub windows_sandbox_level: WindowsSandboxLevel,
pub windows_sandbox_private_desktop: bool,
@@ -111,8 +110,8 @@ pub enum SandboxPreference {
#[derive(Debug, thiserror::Error)]
pub(crate) enum SandboxTransformError {
#[error("missing codex-linux-sandbox executable path")]
MissingLinuxSandboxExecutable,
#[error("failed to resolve current executable for linux sandbox launch: {0}")]
CurrentExecutableUnavailable(std::io::Error),
#[cfg(not(target_os = "macos"))]
#[error("seatbelt sandbox is only available on macOS")]
SeatbeltUnavailable,
@@ -595,7 +594,6 @@ impl SandboxManager {
sandbox_policy_cwd,
#[cfg(target_os = "macos")]
macos_seatbelt_profile_extensions,
codex_linux_sandbox_exe,
use_legacy_landlock,
windows_sandbox_level,
windows_sandbox_private_desktop,
@@ -670,8 +668,8 @@ impl SandboxManager {
#[cfg(not(target_os = "macos"))]
SandboxType::MacosSeatbelt => return Err(SandboxTransformError::SeatbeltUnavailable),
SandboxType::LinuxSeccomp => {
let exe = codex_linux_sandbox_exe
.ok_or(SandboxTransformError::MissingLinuxSandboxExecutable)?;
let exe = std::env::current_exe()
.map_err(SandboxTransformError::CurrentExecutableUnavailable)?;
let allow_proxy_network = allow_network_for_proxy(enforce_managed_network);
let mut args = create_linux_sandbox_command_args_for_policies(
command.clone(),

View File

@@ -174,7 +174,6 @@ fn transform_preserves_unrestricted_file_system_policy_for_restricted_network()
sandbox_policy_cwd: cwd.as_path(),
#[cfg(target_os = "macos")]
macos_seatbelt_profile_extensions: None,
codex_linux_sandbox_exe: None,
use_legacy_landlock: false,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
windows_sandbox_private_desktop: false,
@@ -544,7 +543,6 @@ fn transform_additional_permissions_enable_network_for_external_sandbox() {
sandbox_policy_cwd: cwd.as_path(),
#[cfg(target_os = "macos")]
macos_seatbelt_profile_extensions: None,
codex_linux_sandbox_exe: None,
use_legacy_landlock: false,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
windows_sandbox_private_desktop: false,
@@ -618,7 +616,6 @@ fn transform_additional_permissions_preserves_denied_entries() {
sandbox_policy_cwd: cwd.as_path(),
#[cfg(target_os = "macos")]
macos_seatbelt_profile_extensions: None,
codex_linux_sandbox_exe: None,
use_legacy_landlock: false,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
windows_sandbox_private_desktop: false,

View File

@@ -206,7 +206,6 @@ impl ToolHandler for ApplyPatchHandler {
permissions_preapproved: effective_additional_permissions
.permissions_preapproved,
timeout_ms: None,
codex_exe: turn.codex_linux_sandbox_exe.clone(),
};
let mut orchestrator = ToolOrchestrator::new();
@@ -310,7 +309,6 @@ pub(crate) async fn intercept_apply_patch(
permissions_preapproved: effective_additional_permissions
.permissions_preapproved,
timeout_ms,
codex_exe: turn.codex_linux_sandbox_exe.clone(),
};
let mut orchestrator = ToolOrchestrator::new();

View File

@@ -305,7 +305,6 @@ fn apply_spawn_agent_runtime_overrides(
FunctionCallError::RespondToModel(format!("approval_policy is invalid: {err}"))
})?;
config.permissions.shell_environment_policy = turn.shell_environment_policy.clone();
config.codex_linux_sandbox_exe = turn.codex_linux_sandbox_exe.clone();
config.cwd = turn.cwd.clone();
config
.permissions

View File

@@ -28,7 +28,6 @@ use pretty_assertions::assert_eq;
use serde::Deserialize;
use serde_json::json;
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::Mutex;
@@ -1552,7 +1551,6 @@ async fn build_agent_spawn_config_uses_turn_context_values() {
};
let temp_dir = tempfile::tempdir().expect("temp dir");
turn.cwd = temp_dir.path().to_path_buf();
turn.codex_linux_sandbox_exe = Some(PathBuf::from("/bin/echo"));
let sandbox_policy = pick_allowed_sandbox_policy(
&turn.config.permissions.sandbox_policy,
turn.config.permissions.sandbox_policy.get().clone(),
@@ -1579,7 +1577,6 @@ async fn build_agent_spawn_config_uses_turn_context_values() {
expected.developer_instructions = turn.developer_instructions.clone();
expected.compact_prompt = turn.compact_prompt.clone();
expected.permissions.shell_environment_policy = turn.shell_environment_policy.clone();
expected.codex_linux_sandbox_exe = turn.codex_linux_sandbox_exe.clone();
expected.cwd = turn.cwd.clone();
expected
.permissions
@@ -1633,7 +1630,6 @@ async fn build_agent_resume_config_clears_base_instructions() {
expected.developer_instructions = turn.developer_instructions.clone();
expected.compact_prompt = turn.compact_prompt.clone();
expected.permissions.shell_environment_policy = turn.shell_environment_policy.clone();
expected.codex_linux_sandbox_exe = turn.codex_linux_sandbox_exe.clone();
expected.cwd = turn.cwd.clone();
expected
.permissions

View File

@@ -1070,7 +1070,6 @@ impl JsReplManager {
sandbox_policy_cwd: &turn.cwd,
#[cfg(target_os = "macos")]
macos_seatbelt_profile_extensions: None,
codex_linux_sandbox_exe: turn.codex_linux_sandbox_exe.as_ref(),
use_legacy_landlock: turn.features.use_legacy_landlock(),
windows_sandbox_level: turn.windows_sandbox_level,
windows_sandbox_private_desktop: turn

View File

@@ -199,7 +199,6 @@ impl ToolOrchestrator {
enforce_managed_network: has_managed_network_requirements,
manager: &self.sandbox,
sandbox_cwd: &turn_ctx.cwd,
codex_linux_sandbox_exe: turn_ctx.codex_linux_sandbox_exe.as_ref(),
use_legacy_landlock,
windows_sandbox_level: turn_ctx.windows_sandbox_level,
windows_sandbox_private_desktop: turn_ctx
@@ -330,7 +329,6 @@ impl ToolOrchestrator {
enforce_managed_network: has_managed_network_requirements,
manager: &self.sandbox,
sandbox_cwd: &turn_ctx.cwd,
codex_linux_sandbox_exe: None,
use_legacy_landlock,
windows_sandbox_level: turn_ctx.windows_sandbox_level,
windows_sandbox_private_desktop: turn_ctx

View File

@@ -43,7 +43,6 @@ pub struct ApplyPatchRequest {
pub additional_permissions: Option<PermissionProfile>,
pub permissions_preapproved: bool,
pub timeout_ms: Option<u64>,
pub codex_exe: Option<PathBuf>,
}
#[derive(Default)]
@@ -71,20 +70,11 @@ impl ApplyPatchRuntime {
req: &ApplyPatchRequest,
_codex_home: &std::path::Path,
) -> Result<CommandSpec, ToolError> {
let exe = if let Some(path) = &req.codex_exe {
path.clone()
} else {
#[cfg(target_os = "windows")]
{
codex_windows_sandbox::resolve_current_exe_for_launch(_codex_home, "codex.exe")
}
#[cfg(not(target_os = "windows"))]
{
std::env::current_exe().map_err(|e| {
ToolError::Rejected(format!("failed to determine codex exe: {e}"))
})?
}
};
#[cfg(target_os = "windows")]
let exe = codex_windows_sandbox::resolve_current_exe_for_launch(_codex_home, "codex.exe");
#[cfg(not(target_os = "windows"))]
let exe = std::env::current_exe()
.map_err(|e| ToolError::Rejected(format!("failed to determine codex exe: {e}")))?;
let program = exe.to_string_lossy().to_string();
Ok(CommandSpec {
program,

View File

@@ -52,7 +52,6 @@ fn guardian_review_request_includes_patch_context() {
additional_permissions: None,
permissions_preapproved: false,
timeout_ms: None,
codex_exe: None,
};
let guardian_request = ApplyPatchRuntime::build_guardian_review_request(&request, "call-1");

View File

@@ -164,7 +164,6 @@ pub(super) async fn try_run_zsh_fork(
.permissions
.macos_seatbelt_profile_extensions
.clone(),
codex_linux_sandbox_exe: ctx.turn.codex_linux_sandbox_exe.clone(),
use_legacy_landlock: ctx.turn.features.use_legacy_landlock(),
};
let main_execve_wrapper_exe = ctx
@@ -270,7 +269,6 @@ pub(crate) async fn prepare_unified_exec_zsh_fork(
.permissions
.macos_seatbelt_profile_extensions
.clone(),
codex_linux_sandbox_exe: ctx.turn.codex_linux_sandbox_exe.clone(),
use_legacy_landlock: ctx.turn.features.use_legacy_landlock(),
};
let escalation_policy = CoreShellActionProvider {
@@ -863,7 +861,6 @@ struct CoreShellCommandExecutor {
sandbox_policy_cwd: PathBuf,
#[cfg_attr(not(target_os = "macos"), allow(dead_code))]
macos_seatbelt_profile_extensions: Option<MacOsSeatbeltProfileExtensions>,
codex_linux_sandbox_exe: Option<PathBuf>,
use_legacy_landlock: bool,
}
@@ -1063,7 +1060,6 @@ impl CoreShellCommandExecutor {
sandbox_policy_cwd: &self.sandbox_policy_cwd,
#[cfg(target_os = "macos")]
macos_seatbelt_profile_extensions,
codex_linux_sandbox_exe: self.codex_linux_sandbox_exe.as_ref(),
use_legacy_landlock: self.use_legacy_landlock,
windows_sandbox_level: self.windows_sandbox_level,
windows_sandbox_private_desktop: false,

View File

@@ -668,7 +668,6 @@ async fn prepare_escalated_exec_turn_default_preserves_macos_seatbelt_extensions
macos_preferences: MacOsPreferencesPermission::ReadWrite,
..Default::default()
}),
codex_linux_sandbox_exe: None,
use_legacy_landlock: false,
};
@@ -717,7 +716,6 @@ async fn prepare_escalated_exec_permissions_preserve_macos_seatbelt_extensions()
arg0: None,
sandbox_policy_cwd: cwd.to_path_buf(),
macos_seatbelt_profile_extensions: None,
codex_linux_sandbox_exe: None,
use_legacy_landlock: false,
};
@@ -795,7 +793,6 @@ async fn prepare_escalated_exec_permission_profile_unions_turn_and_requested_mac
macos_preferences: MacOsPreferencesPermission::ReadOnly,
..Default::default()
}),
codex_linux_sandbox_exe: None,
use_legacy_landlock: false,
};

View File

@@ -330,7 +330,6 @@ pub(crate) struct SandboxAttempt<'a> {
pub enforce_managed_network: bool,
pub(crate) manager: &'a SandboxManager,
pub(crate) sandbox_cwd: &'a Path,
pub codex_linux_sandbox_exe: Option<&'a std::path::PathBuf>,
pub use_legacy_landlock: bool,
pub windows_sandbox_level: codex_protocol::config_types::WindowsSandboxLevel,
pub windows_sandbox_private_desktop: bool,
@@ -354,7 +353,6 @@ impl<'a> SandboxAttempt<'a> {
sandbox_policy_cwd: self.sandbox_cwd,
#[cfg(target_os = "macos")]
macos_seatbelt_profile_extensions: None,
codex_linux_sandbox_exe: self.codex_linux_sandbox_exe,
use_legacy_landlock: self.use_legacy_landlock,
windows_sandbox_level: self.windows_sandbox_level,
windows_sandbox_private_desktop: self.windows_sandbox_private_desktop,

View File

@@ -162,12 +162,7 @@ pub async fn load_default_config_for_test(codex_home: &TempDir) -> Config {
#[cfg(target_os = "linux")]
fn default_test_overrides() -> ConfigOverrides {
ConfigOverrides {
codex_linux_sandbox_exe: Some(
find_codex_linux_sandbox_exe().expect("should find binary for codex-linux-sandbox"),
),
..ConfigOverrides::default()
}
ConfigOverrides::default()
}
#[cfg(not(target_os = "linux"))]
@@ -175,23 +170,6 @@ fn default_test_overrides() -> ConfigOverrides {
ConfigOverrides::default()
}
#[cfg(target_os = "linux")]
pub fn find_codex_linux_sandbox_exe() -> Result<PathBuf, CargoBinError> {
if let Ok(path) = std::env::current_exe() {
return Ok(path);
}
if let Some(path) = TEST_ARG0_PATH_ENTRY
.get()
.and_then(Option::as_ref)
.and_then(|path_entry| path_entry.paths().codex_linux_sandbox_exe.clone())
{
return Ok(path);
}
codex_utils_cargo_bin::cargo_bin("codex-linux-sandbox")
}
/// Builds an SSE stream body from a JSON fixture.
///
/// The fixture must contain an array of objects where each object represents a
@@ -525,42 +503,6 @@ macro_rules! skip_if_no_network {
}};
}
#[macro_export]
macro_rules! codex_linux_sandbox_exe_or_skip {
() => {{
#[cfg(target_os = "linux")]
{
match $crate::find_codex_linux_sandbox_exe() {
Ok(path) => Some(path),
Err(err) => {
eprintln!("codex-linux-sandbox binary not available, skipping test: {err}");
return;
}
}
}
#[cfg(not(target_os = "linux"))]
{
None
}
}};
($return_value:expr $(,)?) => {{
#[cfg(target_os = "linux")]
{
match $crate::find_codex_linux_sandbox_exe() {
Ok(path) => Some(path),
Err(err) => {
eprintln!("codex-linux-sandbox binary not available, skipping test: {err}");
return $return_value;
}
}
}
#[cfg(not(target_os = "linux"))]
{
None
}
}};
}
#[macro_export]
macro_rules! skip_if_windows {
($return_value:expr $(,)?) => {{

View File

@@ -561,18 +561,6 @@ impl TestCodexBuilder {
for hook in self.pre_build_hooks.drain(..) {
hook(home.path());
}
if let Ok(path) = codex_utils_cargo_bin::cargo_bin("codex") {
config.codex_linux_sandbox_exe = Some(path);
} else if let Ok(exe) = std::env::current_exe()
&& let Some(path) = exe
.parent()
.and_then(|parent| parent.parent())
.map(|parent| parent.join("codex"))
&& path.is_file()
{
config.codex_linux_sandbox_exe = Some(path);
}
let mut mutators = vec![];
swap(&mut self.config_mutators, &mut mutators);
for mutator in mutators {

View File

@@ -56,7 +56,6 @@ async fn run_test_cmd(tmp: TempDir, cmd: Vec<&str>) -> Result<ExecToolCallOutput
&FileSystemSandboxPolicy::from(&policy),
NetworkSandboxPolicy::from(&policy),
tmp.path(),
&None,
false,
None,
)

View File

@@ -58,6 +58,7 @@ assert_cmd = { workspace = true }
codex-apply-patch = { workspace = true }
codex-utils-cargo-bin = { workspace = true }
core_test_support = { workspace = true }
ctor = { workspace = true }
libc = { workspace = true }
opentelemetry = { workspace = true }
opentelemetry_sdk = { workspace = true }

View File

@@ -345,7 +345,6 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result
cwd: resolved_cwd,
model_provider: model_provider.clone(),
service_tier: None,
codex_linux_sandbox_exe: arg0_paths.codex_linux_sandbox_exe.clone(),
main_execve_wrapper_exe: arg0_paths.main_execve_wrapper_exe.clone(),
js_repl_node_path: None,
js_repl_node_module_dirs: None,

View File

@@ -1,4 +1,49 @@
// Aggregates all former standalone integration tests as modules.
use codex_arg0::Arg0PathEntryGuard;
use codex_arg0::arg0_dispatch;
use ctor::ctor;
use tempfile::TempDir;
struct TestCodexAliasesGuard {
_codex_home: TempDir,
_arg0: Arg0PathEntryGuard,
}
const CODEX_HOME_ENV_VAR: &str = "CODEX_HOME";
// This code runs before any other tests are run.
// It allows the test binary to behave like codex-exec and dispatch to
// codex-linux-sandbox based on the arg0.
#[ctor]
pub static CODEX_ALIASES_TEMP_DIR: TestCodexAliasesGuard = unsafe {
#[allow(clippy::unwrap_used)]
let codex_home = tempfile::Builder::new()
.prefix("codex-exec-tests")
.tempdir()
.unwrap();
let previous_codex_home = std::env::var_os(CODEX_HOME_ENV_VAR);
// Safety: #[ctor] runs before test threads start.
unsafe {
std::env::set_var(CODEX_HOME_ENV_VAR, codex_home.path());
}
#[allow(clippy::unwrap_used)]
let arg0 = arg0_dispatch().unwrap();
match previous_codex_home.as_ref() {
Some(value) => unsafe {
std::env::set_var(CODEX_HOME_ENV_VAR, value);
},
None => unsafe {
std::env::remove_var(CODEX_HOME_ENV_VAR);
},
}
TestCodexAliasesGuard {
_codex_home: codex_home,
_arg0: arg0,
}
};
mod add_dir;
mod apply_patch;
mod auth_env;

View File

@@ -43,10 +43,7 @@ async fn spawn_command_under_sandbox(
env: HashMap<String, String>,
) -> std::io::Result<Child> {
use codex_core::landlock::spawn_command_under_linux_sandbox;
let codex_linux_sandbox_exe = codex_utils_cargo_bin::cargo_bin("codex-exec")
.map_err(|err| io::Error::new(io::ErrorKind::NotFound, err))?;
spawn_command_under_linux_sandbox(
codex_linux_sandbox_exe,
command,
command_cwd,
sandbox_policy,

View File

@@ -28,6 +28,7 @@ serde_json = { workspace = true }
url = { workspace = true }
[target.'cfg(target_os = "linux")'.dev-dependencies]
ctor = { workspace = true }
pretty_assertions = { workspace = true }
tempfile = { workspace = true }
tokio = { workspace = true, features = [

View File

@@ -1,3 +1,5 @@
#![cfg(target_os = "linux")]
// Single integration test binary that aggregates all test modules.
// The submodules live in `tests/suite/`.
mod suite;

View File

@@ -6,8 +6,11 @@ use codex_core::error::Result;
use codex_core::error::SandboxErr;
use codex_core::exec::ExecCapturePolicy;
use codex_core::exec::ExecParams;
use codex_core::exec::ExecToolCallOutput;
use codex_core::exec::StreamOutput;
use codex_core::exec::process_exec_tool_call;
use codex_core::exec_env::create_env;
use codex_core::landlock::create_linux_sandbox_command_args_for_policies;
use codex_core::sandboxing::SandboxPermissions;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::permissions::FileSystemAccessMode;
@@ -22,17 +25,21 @@ use codex_utils_absolute_path::AbsolutePathBuf;
use pretty_assertions::assert_eq;
use std::collections::HashMap;
use std::path::PathBuf;
use std::process::Stdio;
use std::time::Duration;
use std::time::Instant;
use tempfile::NamedTempFile;
use tokio::process::Command;
// At least on GitHub CI, the arm64 tests appear to need longer timeouts.
#[cfg(not(target_arch = "aarch64"))]
const SHORT_TIMEOUT_MS: u64 = 200;
const SHORT_TIMEOUT_MS: u64 = 2_000;
#[cfg(target_arch = "aarch64")]
const SHORT_TIMEOUT_MS: u64 = 5_000;
#[cfg(not(target_arch = "aarch64"))]
const LONG_TIMEOUT_MS: u64 = 1_000;
const LONG_TIMEOUT_MS: u64 = 5_000;
#[cfg(target_arch = "aarch64")]
const LONG_TIMEOUT_MS: u64 = 5_000;
@@ -112,34 +119,66 @@ async fn run_cmd_result_with_policies(
use_legacy_landlock: bool,
) -> Result<codex_core::exec::ExecToolCallOutput> {
let cwd = std::env::current_dir().expect("cwd should exist");
let sandbox_cwd = cwd.clone();
let params = ExecParams {
command: cmd.iter().copied().map(str::to_owned).collect(),
cwd,
expiration: timeout_ms.into(),
capture_policy: ExecCapturePolicy::ShellTool,
env: create_env_from_core_vars(),
network: None,
sandbox_permissions: SandboxPermissions::UseDefault,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
windows_sandbox_private_desktop: false,
justification: None,
arg0: None,
};
let sandbox_program = env!("CARGO_BIN_EXE_codex-linux-sandbox");
let codex_linux_sandbox_exe = Some(PathBuf::from(sandbox_program));
process_exec_tool_call(
params,
let args = create_linux_sandbox_command_args_for_policies(
cmd.iter().copied().map(str::to_owned).collect(),
cwd.as_path(),
&sandbox_policy,
&file_system_sandbox_policy,
network_sandbox_policy,
sandbox_cwd.as_path(),
&codex_linux_sandbox_exe,
cwd.as_path(),
use_legacy_landlock,
None,
)
.await
/*allow_network_for_proxy*/ false,
);
run_linux_sandbox_command(args, cwd, timeout_ms).await
}
async fn run_linux_sandbox_command(
args: Vec<String>,
cwd: PathBuf,
timeout_ms: u64,
) -> Result<ExecToolCallOutput> {
let start = Instant::now();
let mut command = Command::new(env!("CARGO_BIN_EXE_codex-linux-sandbox"));
command
.args(args)
.current_dir(cwd)
.env_clear()
.envs(create_env_from_core_vars())
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped());
let output =
match tokio::time::timeout(Duration::from_millis(timeout_ms), command.output()).await {
Ok(output) => output.map_err(CodexErr::Io)?,
Err(_) => {
let exec_output = ExecToolCallOutput {
exit_code: 124,
stdout: StreamOutput::new(String::new()),
stderr: StreamOutput::new(String::new()),
aggregated_output: StreamOutput::new(String::new()),
duration: start.elapsed(),
timed_out: true,
};
return Err(CodexErr::Sandbox(SandboxErr::Timeout {
output: Box::new(exec_output),
}));
}
};
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
let aggregated_output = format!("{stdout}{stderr}");
Ok(ExecToolCallOutput {
exit_code: output.status.code().unwrap_or(-1),
stdout: StreamOutput::new(stdout),
stderr: StreamOutput::new(stderr),
aggregated_output: StreamOutput::new(aggregated_output),
duration: start.elapsed(),
timed_out: false,
})
}
fn is_bwrap_unavailable_output(output: &codex_core::exec::ExecToolCallOutput) -> bool {
@@ -388,15 +427,12 @@ async fn assert_network_blocked(cmd: &[&str]) {
};
let sandbox_policy = SandboxPolicy::new_read_only_policy();
let sandbox_program = env!("CARGO_BIN_EXE_codex-linux-sandbox");
let codex_linux_sandbox_exe: Option<PathBuf> = Some(PathBuf::from(sandbox_program));
let result = process_exec_tool_call(
params,
&sandbox_policy,
&FileSystemSandboxPolicy::from(&sandbox_policy),
NetworkSandboxPolicy::from(&sandbox_policy),
sandbox_cwd.as_path(),
&codex_linux_sandbox_exe,
false,
None,
)

View File

@@ -1,3 +1,24 @@
// Aggregates all former standalone integration tests as modules.
use ctor::ctor;
use std::path::Path;
const LINUX_SANDBOX_ARG0: &str = "codex-linux-sandbox";
// This code runs before any other tests are run.
// It allows the test binary to behave like codex-linux-sandbox when re-execed
// via current_exe() with argv[0] overridden.
#[ctor]
fn dispatch_linux_sandbox_arg0() {
let argv0 = std::env::args_os().next().unwrap_or_default();
let exe_name = Path::new(&argv0)
.file_name()
.and_then(|s| s.to_str())
.unwrap_or("");
if exe_name == LINUX_SANDBOX_ARG0 {
codex_linux_sandbox::run_main();
}
}
mod landlock;
mod managed_proxy;

View File

@@ -176,7 +176,6 @@ impl CodexToolCallParam {
cwd: cwd.map(PathBuf::from),
approval_policy: approval_policy.map(Into::into),
sandbox_mode: sandbox.map(Into::into),
codex_linux_sandbox_exe: arg0_paths.codex_linux_sandbox_exe.clone(),
main_execve_wrapper_exe: arg0_paths.main_execve_wrapper_exe.clone(),
base_instructions,
developer_instructions,

View File

@@ -416,7 +416,6 @@ pub async fn run_main(
cwd,
model_provider: model_provider_override.clone(),
config_profile: cli.config_profile.clone(),
codex_linux_sandbox_exe: arg0_paths.codex_linux_sandbox_exe.clone(),
main_execve_wrapper_exe: arg0_paths.main_execve_wrapper_exe.clone(),
show_raw_agent_reasoning: cli.oss.then_some(true),
additional_writable_roots: additional_dirs,

View File

@@ -738,7 +738,6 @@ pub async fn run_main(
cwd,
model_provider: model_provider_override.clone(),
config_profile: cli.config_profile.clone(),
codex_linux_sandbox_exe: arg0_paths.codex_linux_sandbox_exe.clone(),
main_execve_wrapper_exe: arg0_paths.main_execve_wrapper_exe.clone(),
show_raw_agent_reasoning: cli.oss.then_some(true),
additional_writable_roots: additional_dirs,