macOS permission profiles cannot re-allow child paths under a denied parent path
What version of the Codex App are you using (From “About Codex” dialog)?
Version 26.429.30905 (2345)
What subscription do you have?
Pro
What platform is your computer?
Darwin 24.6.0 arm64 arm
What issue are you seeing?
On macOS, permission profiles appear unable to preserve most-specific filesystem precedence when a parent path is denied and a child path is explicitly allowed.
Example desired policy:
[permissions.locked.filesystem]
":project_roots" = { "." = "write" }
"/tmp" = "write"
"/private/tmp" = "write"
"/private/var/folders" = "write"
"/Users/me" = "none"
"/Users/me/.gitconfig" = "read"
"/Users/me/.cache/uv" = "write"
Desired behavior:
ls ~ fails
ls ~/.gitconfig >/dev/null succeeds
ls ~/.cache/uv >/dev/null succeeds
wc -c < ~/.config/my-app/.env >/dev/null fails
Observed behavior: the broad home deny also blocks the nested allow entries.
This looks like a macOS Seatbelt projection issue. Codex’s permission resolver appears to support most-specific path precedence, but the generated Seatbelt policy seems to turn exact deny roots into broad carveouts that shadow more-specific child allow rules.
What steps can reproduce the bug?
Use a profile like this, replacing /Users/me as needed:
approval_policy = "never"
sandbox_mode = "workspace-write"
default_permissions = "locked"
[permissions.locked.filesystem]
":project_roots" = { "." = "write" }
"/tmp" = "write"
"/private/tmp" = "write"
"/private/var/folders" = "write"
"/etc/ssl" = "read"
"/System/Library/OpenSSL" = "read"
"/Library/Developer/CommandLineTools" = "read"
"/Users/me" = "none"
"/Users/me/.gitconfig" = "read"
"/Users/me/.cache/uv" = "write"
[permissions.locked.network]
enabled = true
mode = "full"
Then run:
codex sandbox macos --permissions-profile locked -- /bin/zsh -lc 'ls ~ >/dev/null'
codex sandbox macos --permissions-profile locked -- /bin/zsh -lc 'ls ~/.gitconfig >/dev/null'
codex sandbox macos --permissions-profile locked -- /bin/zsh -lc 'ls ~/.cache/uv >/dev/null'
codex sandbox macos --permissions-profile locked -- /bin/zsh -lc 'wc -c < ~/.config/my-app/.env >/dev/null 2>&1'
What is the expected behavior?
Most-specific path rules should win consistently in the macOS runtime sandbox.
Expected result:
ls ~ fails
ls ~/.gitconfig >/dev/null succeeds
ls ~/.cache/uv >/dev/null succeeds
wc -c < ~/.config/my-app/.env >/dev/null fails
Additional information
Possible fix without new config syntax:
- In
codex-rs/protocol/src/permissions.rs, expose readable roots that are more-specific descendants of unreadable roots, for exampleget_readable_roots_under_unreadable_roots_with_cwd(...).
- In
codex-rs/sandboxing/src/seatbelt.rs, when generating the read policy:
- keep the existing broad allow with denied roots carved out;
- add a second explicit
allow file-read*block for more-specific child exception roots; - add metadata-only traversal for denied ancestors so the child path can be reached without enabling
ls ~.
- Update
ReadDenyMatcherso exact path denies use the same most-specific precedence asresolve_access_with_cwd, instead of treating deny roots as unconditional prefix masks. Deny globs can stay hard-deny unless glob exception semantics are added separately.
Related: #2847, but this issue is narrower: it is specifically about parent-deny / child-allow behavior in macOS permission-profile enforcement.