Skip to content

core: resolve host_executable() rules during preflight#13065

Open
bolinfest wants to merge 1 commit intomainfrom
pr13065
Open

core: resolve host_executable() rules during preflight#13065
bolinfest wants to merge 1 commit intomainfrom
pr13065

Conversation

@bolinfest
Copy link
Collaborator

@bolinfest bolinfest commented Feb 27, 2026

Why

#12964 added host_executable() support to codex-execpolicy, and #13046 adopted it in the zsh-fork interception path.

The remaining gap was the preflight execpolicy check in core/src/exec_policy.rs. That path derives approval requirements before execution for shell, shell_command, and unified_exec, but it was still using the default exact-token matcher.

As a result, a command that already included an absolute executable path, such as /usr/bin/git status, could still miss a basename rule like prefix_rule(pattern = ["git"], ...) during preflight even when the policy also defined a matching host_executable(name = "git", ...) entry.

This PR brings the same opt-in host_executable() resolution to the preflight approval path when an absolute program path is already present in the parsed command.

What Changed

  • updated ExecPolicyManager::create_exec_approval_requirement_for_command() in core/src/exec_policy.rs to use check_multiple_with_options(...) with MatchOptions { resolve_host_executables: true }
  • kept the existing shell parsing flow for approval derivation, but now allow basename rules to match absolute executable paths during preflight when host_executable() permits it
  • updated requested-prefix amendment evaluation to use the same host-executable-aware matching mode, so suggested prefix_rule() amendments are checked consistently for absolute-path commands
  • added preflight coverage for:
    • absolute-path commands that should match basename rules through host_executable()
    • absolute-path commands whose paths are not in the allowed host_executable() mapping
    • requested prefix-rule amendments for absolute-path commands

Verification

  • just fix -p codex-core
  • cargo test -p codex-core --lib exec_policy::tests::

bolinfest added a commit that referenced this pull request Feb 28, 2026
## Why

[#12964](#12964) added
`host_executable()` support to `codex-execpolicy`, but the zsh-fork
interception path in `unix_escalation.rs` was still evaluating commands
with the default exact-token matcher.

That meant an intercepted absolute executable such as `/usr/bin/git
status` could still miss basename rules like `prefix_rule(pattern =
["git", "status"])`, even when the policy also defined a matching
`host_executable(name = "git", ...)` entry.

This PR adopts the new matching behavior in the zsh-fork runtime only.
That keeps the rollout intentionally narrow: zsh-fork already requires
explicit user opt-in, so it is a safer first caller to exercise the new
`host_executable()` scheme before expanding it to other execpolicy call
sites.

It also brings zsh-fork back in line with the current `prefix_rule()`
execution model. Until prefix rules can carry their own permission
profiles, a matched `prefix_rule()` is expected to rerun the intercepted
command unsandboxed on `allow`, or after the user accepts `prompt`,
instead of merely continuing inside the inherited shell sandbox.

## What Changed

- added `evaluate_intercepted_exec_policy()` in
`core/src/tools/runtimes/shell/unix_escalation.rs` to centralize
execpolicy evaluation for intercepted commands
- switched intercepted direct execs in the zsh-fork path to
`check_multiple_with_options(...)` with `MatchOptions {
resolve_host_executables: true }`
- added `commands_for_intercepted_exec_policy()` so zsh-fork policy
evaluation works from intercepted `(program, argv)` data instead of
reconstructing a synthetic command before matching
- left shell-wrapper parsing intentionally disabled by default behind
`ENABLE_INTERCEPTED_EXEC_POLICY_SHELL_WRAPPER_PARSING`, so
path-sensitive matching relies on later direct exec interception rather
than shell-script parsing
- made matched `prefix_rule()` decisions rerun intercepted commands with
`EscalationExecution::Unsandboxed`, while unmatched-command fallback
keeps the existing sandbox-preserving behavior
- extracted the zsh-fork test harness into
`core/tests/common/zsh_fork.rs` so both the skill-focused and
approval-focused integration suites can exercise the same runtime setup
- limited this change to the intercepted zsh-fork path rather than
changing every execpolicy caller at once
- added runtime coverage in
`core/src/tools/runtimes/shell/unix_escalation_tests.rs` for allowed and
disallowed `host_executable()` mappings and the wrapper-parsing modes
- added integration coverage in `core/tests/suite/approvals.rs` to
verify a saved `prefix_rule(pattern=["touch"], decision="allow")` reruns
under zsh-fork outside a restrictive `WorkspaceWrite` sandbox

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/13046).
* #13065
* __->__ #13046
Base automatically changed from pr13046 to main February 28, 2026 01:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants