Files
codex/codex-rs/core
T
Michael Bolin e6c470957d fix: preserve approval sandbox decisions in unified exec (#24981)
## Why

This PR fixes approval sandbox semantics in the unified-exec path. The
zsh-fork runtime exposed the bug because the shell can do meaningful
work before any intercepted child `execv(2)` exists: redirections,
builtins, globbing, and pipeline setup all happen in the launch process.
If the model requested `sandbox_permissions=require_escalated`, or an
exec-policy `allow` rule explicitly bypassed the sandbox, that approved
sandbox decision needs to be preserved for the launch path and for
intercepted execs that use the same approval machinery.

The behavior is not only about zsh fork. The production changes are in
shared approval/escalation code, so they also affect non-zsh-fork
intercepted exec paths that go through the same sandbox decision logic.
The narrow intent is to preserve the approval decision while still
keeping denied-read profiles and bounded additional-permission requests
sandboxed.

## Production Changes

- `codex-rs/core/src/tools/runtimes/unified_exec.rs`: derives a
`launch_sandbox_permissions` value from the requested sandbox
permissions and the runtime filesystem policy, then uses that value for
managed-network/env setup and launch sandbox selection. This keeps full
approval or policy-bypass decisions visible to the first unified-exec
attempt, while still preventing a full sandbox override from discarding
denied-read restrictions. Direct unified exec keeps the same decision
surface; the important difference is that zsh-fork launch setup no
longer accidentally loses the approved parent sandbox decision.

- `codex-rs/core/src/tools/runtimes/shell/unix_escalation.rs`: makes
intercepted-exec escalation selection explicit for the three sandbox
permission modes. `UseDefault` only escalates when an exec-policy
decision allows sandbox bypass, `RequireEscalated` escalates when
unsandboxed execution is allowed, and `WithAdditionalPermissions`
escalates through the bounded additional-permissions path instead of
being treated as a full unsandboxed override. Unsandboxed intercepted
execs now also rebuild the environment as `RequireEscalated`, which
strips managed-network proxy variables consistently with other
unsandboxed execution.

## Test Coverage

Most of the PR is tests. The new coverage verifies:

- unified exec preserves parent approval and exec-policy sandbox
decisions for zsh-fork launch selection;
- bounded `with_additional_permissions` remains sandboxed and
permission-profile based;
- denied-read profiles are not weakened by parent approval;
- explicit prompt rules still prompt for intercepted execs after the
parent command is approved;
- unsandboxed intercepted execs strip managed-network env vars.

No documentation update is needed; this is an internal approval/sandbox
correctness fix.





---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/24981).
* #24982
* __->__ #24981
e6c470957d ยท 2026-06-07 11:33:16 -07:00
History
..

codex-core

This crate implements the business logic for Codex. It is designed to be used by the various Codex UIs written in Rust.

Dependencies

Note that codex-core makes some assumptions about certain helper utilities being available in the environment. Currently, this support matrix is:

macOS

Expects /usr/bin/sandbox-exec to be present.

When using the workspace-write sandbox policy, the Seatbelt profile allows writes under the configured writable roots while keeping .git (directory or pointer file), the resolved gitdir: target, and .codex read-only.

Network access and filesystem read/write roots are controlled by SandboxPolicy. Seatbelt consumes the resolved policy and enforces it.

Seatbelt also keeps the legacy default preferences read access (user-preference-read) needed for cfprefs-backed macOS behavior.

Linux

Expects the binary containing codex-core to run the equivalent of codex sandbox when arg0 is codex-linux-sandbox. See the codex-arg0 crate for details.

Legacy SandboxPolicy / sandbox_mode configs are still supported on Linux. They can continue to use the legacy Landlock path when the split filesystem policy is sandbox-equivalent to the legacy model after cwd resolution. Split filesystem policies that need direct FileSystemSandboxPolicy enforcement, such as read-only or denied carveouts under a broader writable root, automatically route through bubblewrap. The legacy Landlock path is used only when the split filesystem policy round-trips through the legacy SandboxPolicy model without changing semantics. That includes overlapping cases like /repo = write, /repo/a = none, /repo/a/b = write, where the more specific writable child must reopen under a denied parent.

The Linux sandbox helper prefers the first bwrap found on PATH outside the current working directory whenever it is available. If bwrap is present but too old to support --argv0, the helper keeps using system bubblewrap and switches to a no---argv0 compatibility path for the inner re-exec. If bwrap is missing, it falls back to the bundled codex-resources/bwrap binary shipped with Codex and Codex surfaces a startup warning through its normal notification path instead of printing directly from the sandbox helper. Codex also surfaces a startup warning when bubblewrap cannot create user namespaces. WSL2 uses the normal Linux bubblewrap path. WSL1 is not supported for bubblewrap sandboxing because it cannot create the required user namespaces, so Codex rejects sandboxed shell commands that would enter the bubblewrap path before invoking bwrap.

Windows

Legacy SandboxPolicy / sandbox_mode configs are still supported on Windows. Legacy read-only and workspace-write policies imply full filesystem read access; exact readable roots are represented by split filesystem policies instead.

The elevated Windows sandbox also supports:

  • legacy ReadOnly and WorkspaceWrite behavior
  • split filesystem policies that need exact readable roots, exact writable roots, or extra read-only carveouts under writable roots
  • backend-managed system read roots required for basic execution, such as C:\Windows, C:\Program Files, C:\Program Files (x86), and C:\ProgramData, when a split filesystem policy requests platform defaults

The unelevated restricted-token backend still supports the legacy full-read Windows model for legacy ReadOnly and WorkspaceWrite behavior. It also supports a narrow split-filesystem subset: full-read split policies whose writable roots still match the legacy WorkspaceWrite root set, but add extra read-only carveouts under those writable roots.

New [permissions] / split filesystem policies remain supported on Windows only when they can be enforced directly by the selected Windows backend or round-trip through the legacy SandboxPolicy model without changing semantics. Policies that would require direct explicit unreadable carveouts (none) or reopened writable descendants under read-only carveouts still fail closed instead of running with weaker enforcement.

All Platforms

Expects the binary containing codex-core to simulate the virtual apply_patch CLI when arg1 is --codex-run-as-apply-patch. See the codex-arg0 crate for details.