fix(core): respect reject policy by approval source for skill scripts (#13816)

## Summary
- distinguish reject-policy handling for prefix-rule approvals versus
sandbox approvals in Unix shell escalation
- keep prompting for skill-script execution when `rules=true` but
`sandbox_approval=false`, instead of denying the command up front
- add regression coverage for both skill-script reject-policy paths in
`codex-rs/core/tests/suite/skill_approval.rs`
This commit is contained in:
Celia Chen
2026-03-06 21:43:14 -08:00
committed by GitHub
parent b52c18e414
commit b0ce16c47a
3 changed files with 174 additions and 6 deletions

View File

@@ -104,7 +104,7 @@ fn is_policy_match(rule_match: &RuleMatch) -> bool {
/// `prompt_is_rule` distinguishes policy-rule prompts from sandbox/escalation
/// prompts so `Reject.rules` and `Reject.sandbox_approval` are honored
/// independently. When both are present, policy-rule prompts take precedence.
fn prompt_is_rejected_by_policy(
pub(crate) fn prompt_is_rejected_by_policy(
approval_policy: AskForApproval,
prompt_is_rule: bool,
) -> Option<&'static str> {

View File

@@ -5,6 +5,7 @@ use crate::exec::ExecExpiration;
use crate::exec::ExecToolCallOutput;
use crate::exec::SandboxType;
use crate::exec::is_likely_sandbox_denied;
use crate::exec_policy::prompt_is_rejected_by_policy;
use crate::features::Feature;
use crate::sandboxing::ExecRequest;
use crate::sandboxing::SandboxPermissions;
@@ -28,7 +29,6 @@ use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::NetworkPolicyRuleAction;
use codex_protocol::protocol::RejectConfig;
use codex_protocol::protocol::ReviewDecision;
use codex_protocol::protocol::SandboxPolicy;
use codex_shell_command::bash::parse_shell_lc_plain_commands;
@@ -451,11 +451,12 @@ impl CoreShellActionProvider {
EscalationDecision::deny(Some("Execution forbidden by policy".to_string()))
}
Decision::Prompt => {
if matches!(
if prompt_is_rejected_by_policy(
self.approval_policy,
AskForApproval::Never
| AskForApproval::Reject(RejectConfig { rules: true, .. })
) {
matches!(decision_source, DecisionSource::PrefixRule),
)
.is_some()
{
EscalationDecision::deny(Some("Execution forbidden by policy".to_string()))
} else {
match self