Allow trusted symlinks beneath CODEX_HOME on macOS (#42716)

## What changed

- Add the macOS-only `allow_symlinked_codex_home` user setting. When enabled in the execution host's `$CODEX_HOME/config.toml`, writable roots at or beneath that home may traverse symlinks.
- Keep the exception disabled by default and prevent project config, command-line overrides, and ignored user config from enabling it. Other writable roots remain subject to symlink checks.
- Propagate the resolved setting through local, interactive, and exec-server sandbox paths, including `CODEX_HOME` aliases.
- Explain the opt-out in symlink rejection errors, including that it trusts targets outside `CODEX_HOME` and targets that change between commands.

## Testing

- Add macOS coverage for shell commands, patches, filesystem helpers, process execution, and interactive startup with enabled, disabled, aliased, and out-of-scope homes.

GitOrigin-RevId: 99fcdf611200c9e1b7713cf06f7fdea5bfa7f089
This commit is contained in:
jif
2026-09-04 10:12:03 +00:00
committed by copyberry
parent 8e6a44b428
commit d13aeb77ea
24 changed files with 499 additions and 36 deletions
+4
View File
@@ -129,6 +129,10 @@ impl FileSystemSandboxRunner {
) -> Result<SandboxExecRequest, JSONRPCErrorError> {
let helper = &self.runtime_paths.codex_self_exe;
let sandbox_manager = SandboxManager::for_file_system_helpers();
#[cfg(target_os = "macos")]
let sandbox_manager = sandbox_manager.with_allowed_symlinked_codex_home(
self.runtime_paths.allowed_symlinked_codex_home.clone(),
);
let sandbox = sandbox_manager.select_initial(
permission_profile,
SandboxablePreference::Require,
@@ -185,6 +185,9 @@ pub(crate) async fn prepare_exec_request_with_telemetry(
network_policy,
);
let sandbox_manager = SandboxManager::new();
#[cfg(target_os = "macos")]
let sandbox_manager = sandbox_manager
.with_allowed_symlinked_codex_home(runtime_paths.allowed_symlinked_codex_home.clone());
let sandbox = sandbox_manager.select_initial(
&permissions,
SandboxablePreference::Require,
+16 -1
View File
@@ -2,7 +2,7 @@ use std::path::PathBuf;
use codex_utils_absolute_path::AbsolutePathBuf;
/// Runtime paths needed by exec-server child processes.
/// Paths and sandbox settings initialized when creating an executor.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ExecServerRuntimePaths {
/// Stable path to the Codex executable used to launch hidden helper modes.
@@ -10,6 +10,9 @@ pub struct ExecServerRuntimePaths {
/// Path to the Linux sandbox helper alias used when the platform sandbox
/// needs to re-enter Codex by argv0.
pub codex_linux_sandbox_exe: Option<AbsolutePathBuf>,
/// User-config opt-out of writable-root symlink checks beneath this host's home.
#[cfg(target_os = "macos")]
pub allowed_symlinked_codex_home: Option<AbsolutePathBuf>,
}
impl ExecServerRuntimePaths {
@@ -33,8 +36,20 @@ impl ExecServerRuntimePaths {
Ok(Self {
codex_self_exe: absolute_path(codex_self_exe)?,
codex_linux_sandbox_exe: codex_linux_sandbox_exe.map(absolute_path).transpose()?,
#[cfg(target_os = "macos")]
allowed_symlinked_codex_home: None,
})
}
/// Applies the symlink opt-in resolved by the execution host's config loader.
#[cfg(target_os = "macos")]
pub fn with_allowed_symlinked_codex_home(
mut self,
allowed_symlinked_codex_home: Option<AbsolutePathBuf>,
) -> Self {
self.allowed_symlinked_codex_home = allowed_symlinked_codex_home;
self
}
}
fn absolute_path(path: PathBuf) -> std::io::Result<AbsolutePathBuf> {