Skip to content

execpolicy: add host_executable() path mappings#12964

Merged
bolinfest merged 1 commit intomainfrom
pr12964
Feb 27, 2026
Merged

execpolicy: add host_executable() path mappings#12964
bolinfest merged 1 commit intomainfrom
pr12964

Conversation

@bolinfest
Copy link
Collaborator

@bolinfest bolinfest commented Feb 27, 2026

Why

execpolicy currently keys prefix_rule() matching off the literal first token. That works for rules like ["/usr/bin/git"], but it means shared basename rules such as ["git"] do not help when a caller passes an absolute executable path like /usr/bin/git.

This PR lays the groundwork for basename-aware matching without changing existing callers yet. It adds typed host-executable metadata and an opt-in resolution path in codex-execpolicy, so a follow-up PR can adopt the new behavior in unix_escalation.rs and other call sites without having to redesign the policy layer first.

What Changed

  • added host_executable(name = ..., paths = [...]) to the execpolicy parser and validated it with AbsolutePathBuf
  • stored host executable mappings separately from prefix rules inside Policy
  • added MatchOptions and opt-in *_with_options() APIs that preserve existing behavior by default
  • implemented exact-first matching with optional basename fallback, gated by host_executable() allowlists when present
  • normalized executable names for cross-platform matching so Windows paths like git.exe can satisfy host_executable(name = "git", ...)
  • updated match / not_match example validation to exercise the host-executable resolution path instead of only raw prefix-rule matching
  • preserved source locations for deferred example-validation errors so policy load failures still point at the right file and line
  • surfaced resolvedProgram on RuleMatch so callers can tell when a basename rule matched an absolute executable path
  • preserved host executable metadata when requirements policies overlay file-based policies in core/src/exec_policy.rs
  • documented the new rule shape and CLI behavior in execpolicy/README.md

Verification

  • cargo test -p codex-execpolicy
  • added coverage in execpolicy/tests/basic.rs for parsing, precedence, empty allowlists, basename fallback, exact-match precedence, and host-executable-backed match / not_match examples
  • added a regression test in core/src/exec_policy.rs to verify requirements overlays preserve host_executable() metadata
  • verified cargo test -p codex-core --lib, including source-rendering coverage for deferred validation errors

@bolinfest bolinfest changed the title execpolicy: add host_executable() support execpolicy: add host_executable() path mappings Feb 27, 2026
@bolinfest bolinfest force-pushed the pr12964 branch 4 times, most recently from 0bfef31 to 93194bc Compare February 27, 2026 16:41
Copy link
Collaborator

@owenlin0 owenlin0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice design!

))
})?;
let path = parse_literal_absolute_path(raw)?;
if path.as_path().file_name().and_then(|value| value.to_str()) != Some(name) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This basename check makes the feature effectively unusable on Windows. Existing rules are typically written as git, but the actual resolved path is usually ...\\git.exe; host_executable(name = "git", paths = ["C:\\...\\git.exe"]) is rejected here, and runtime lookup later keys off the literal file_name() as well. If this API is meant to be cross-platform, we need Windows-specific normalization (file_stem()/PATHEXT-style handling) before comparing or looking up the executable name.

Copy link
Collaborator Author

@bolinfest bolinfest left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One other issue I noticed while reading through the new parser/matcher flow: match / not_match examples still validate a prefix_rule by calling rule.matches(example) on the raw prefix rules, so they never see the new host_executable() fallback. That means a rule like prefix_rule(pattern = ["git", "status"], match = ["/usr/bin/git status"]) together with host_executable(name = "git", paths = ["/usr/bin/git"]) will still be rejected at load time even though runtime matching in Policy::match_host_executable_rules() accepts it. Since these examples are documented as load-time unit tests, there is currently no way to validate the new absolute-path behavior.

Copy link
Collaborator Author

@bolinfest bolinfest left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another issue in the core wiring: the new host-executable fallback is only enabled in evaluate_intercepted_exec_policy() and in execpolicy check --resolve-host-executables. The normal preflight path in ExecPolicyManager::create_exec_approval_requirement_for_command() still calls exec_policy.check_multiple(...) with default options, so absolute commands like /usr/bin/git status remain unmatched during approval derivation. For the generic shell tool and the classic shell_command backend, that preflight path is the only execpolicy evaluation, so the new host_executable() rules do not apply there at all; even on zsh-fork backends, you now get different policy decisions before and after interception.

@bolinfest bolinfest merged commit b148d98 into main Feb 27, 2026
95 of 118 checks passed
@bolinfest bolinfest deleted the pr12964 branch February 27, 2026 20:59
@github-actions github-actions bot locked and limited conversation to collaborators Feb 27, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants