Execpolicy allow rules silently stay sandboxed when any denied-read path is configured

Open 💬 2 comments Opened Aug 13, 2026 by liby

What variant of Codex are you using?

Codex Desktop on macOS.

What version of Codex is running?

  • Desktop: 26.803.81509
  • Bundled CLI: 0.147.0-alpha.6.6

What is the issue?

An execpolicy allow rule silently stops running its matching command outside the sandbox as soon as the active filesystem permission profile contains any denied-read restriction.

This conflicts with the Rules documentation, which describes rules as controlling which commands run outside the sandbox. codex execpolicy check still reports allow, but execution remains in the default sandbox and no diagnostic explains that the rule cannot provide the documented sandbox placement.

Minimal reproduction

Use a synthetic denied path so no credential or private path is involved:

approval_policy = "on-request"
default_permissions = "repro"

[permissions.repro.filesystem]
":root" = "write"
"/tmp/codex-denied-read-probe" = "deny"

Add a matching rule:

prefix_rule(
    pattern = ["/usr/bin/open"],
    decision = "allow",
    justification = "Synthetic sandbox-placement probe.",
    match = [
        "/usr/bin/open -Rb com.apple.TextEdit",
    ],
)

Verify the policy result:

$ codex execpolicy check --rules <CODEX_HOME>/rules/repro.rules --pretty /usr/bin/open -Rb com.apple.TextEdit
Decision: allow

Run the matching command directly from a Desktop task. It still receives the Seatbelt environment and LaunchServices access fails as it does for an ordinary sandboxed command.

The result is deterministic in the matching release source:

  • unsandboxed_execution_allowed() returns false whenever has_denied_read_restrictions() is true.
  • sandbox_override_for_first_attempt() therefore refuses BypassSandboxFirstAttempt even for Skip { bypass_sandbox: true }.
  • Unix rule escalation maps a policy-driven match to EscalationExecution::TurnDefault when unsandboxed execution is not allowed.

Sources:

Expected behavior

A single denied-read path should not silently disable every configured per-command sandbox escape.

The safety check must not simply be removed: running the current command process unsandboxed would discard the denied-read restrictions. Instead, Codex needs either:

  1. a trusted host-side command executor that can run an explicitly matched executable outside the agent sandbox while the agent and ordinary shell commands remain subject to denied reads; or
  2. an independent sandbox-placement policy with a security model that preserves the intended credential boundary.

At minimum, if the active permission model cannot honor an outside-sandbox rule, config validation and runtime output should report that incompatibility instead of returning allow and silently using TurnDefault.

Impact

The current behavior makes two documented safety mechanisms mutually exclusive:

  • denied reads protect credentials and other sensitive files from ordinary agent commands;
  • Rules are supposed to let selected trusted CLIs use host-only state outside the sandbox.

Configuring even one denied-read path removes the second capability globally. Users must then choose between dropping credential protection, using full access, or implementing a separate trusted broker.

This is a concrete interaction with the broader sandbox-placement/approval separation requested in #20917.

View original on GitHub ↗

2 Comments

dajiaohuang · 14 days ago

I revalidated this against origin/main at 72fa74fbc9c4b72d513304bfa0eda427d2402ed9.

The current behavior is internally consistent but user-visible semantics are ambiguous:

  • a fully matched execpolicy allow decision sets bypass_sandbox: true;
  • sandbox_override_for_first_attempt() deliberately suppresses that bypass when the active filesystem policy has denied reads;
  • Unix intercepted exec independently maps the same policy match to EscalationExecution::TurnDefault;
  • this preserves denied-read enforcement, but the command runs sandboxed without explaining why the documented outside-sandbox placement was not honored.

A previous attempt in #30120 built an otherwise-unrestricted sandbox profile that retained denied reads, but it was closed after remote-executor, managed-network, and Windows ACL-cost concerns. That makes a trusted host-side/independent-placement solution materially larger than a diagnostic-only change.

Could maintainers clarify the intended first-stage behavior when an allow rule matches but denied reads make unsandboxed execution unsafe?

  1. Emit one user-visible warning and continue inside the sandbox (smallest behavior-preserving option).
  2. Fail the invocation with an explicit incompatibility diagnostic.
  3. Pursue an independent sandbox-placement/trusted-executor design that preserves denied reads.

Any option should keep denied-read protections intact. If option 1 or 2 is preferred and an external PR would be useful, please state the intended warning/error semantics and explicitly invite the contribution per the repository policy.

dajiaohuang · 14 days ago

I did not open an upstream PR because the contribution policy says external PRs are invitation-only. I prepared a focused draft with regression coverage at https://github.com/dajiaohuang/codex/pull/3.

It preserves denied-read enforcement and takes the smallest reversible option from the earlier proposal: one deduplicated warning when an authoritative execpolicy allow match must remain sandboxed. The draft documents the alternative error/trusted-executor directions and the local Windows validation boundary for its Unix-only test.

If this direction fits the project's intended safety semantics, an invitation would let me submit it through the project's normal review process.