Refactor execpolicy fallback evaluation (#7544)

## Refactor of the `execpolicy` crate

To illustrate why we need this refactor, consider an agent attempting to
run `apple | rm -rf ./`. Suppose `apple` is allowed by `execpolicy`.
Before this PR, `execpolicy` would consider `apple` and `pear` and only
render one rule match: `Allow`. We would skip any heuristics checks on
`rm -rf ./` and immediately approve `apple | rm -rf ./` to run.

To fix this, we now thread a `fallback` evaluation function into
`execpolicy` that runs when no `execpolicy` rules match a given command.
In our example, we would run `fallback` on `rm -rf ./` and prevent
`apple | rm -rf ./` from being run without approval.
This commit is contained in:
zhao-oai
2025-12-04 02:39:48 -05:00
committed by GitHub
parent e925a380dc
commit 3d35cb4619
27 changed files with 538 additions and 257 deletions

View File

@@ -17,6 +17,34 @@ pub enum SandboxRiskLevel {
High,
}
/// Proposed execpolicy change to allow commands starting with this prefix.
///
/// The `command` tokens form the prefix that would be added as an execpolicy
/// `prefix_rule(..., decision="allow")`, letting the agent bypass approval for
/// commands that start with this token sequence.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, JsonSchema, TS)]
#[serde(transparent)]
#[ts(type = "Array<string>")]
pub struct ExecPolicyAmendment {
pub command: Vec<String>,
}
impl ExecPolicyAmendment {
pub fn new(command: Vec<String>) -> Self {
Self { command }
}
pub fn command(&self) -> &[String] {
&self.command
}
}
impl From<Vec<String>> for ExecPolicyAmendment {
fn from(command: Vec<String>) -> Self {
Self { command }
}
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, JsonSchema, TS)]
pub struct SandboxCommandAssessment {
pub description: String,
@@ -51,10 +79,10 @@ pub struct ExecApprovalRequestEvent {
/// Optional model-provided risk assessment describing the blocked command.
#[serde(skip_serializing_if = "Option::is_none")]
pub risk: Option<SandboxCommandAssessment>,
/// Prefix rule that can be added to the user's execpolicy to allow future runs.
/// Proposed execpolicy amendment that can be applied to allow future runs.
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional, type = "Array<string>")]
pub allow_prefix: Option<Vec<String>>,
#[ts(optional)]
pub proposed_execpolicy_amendment: Option<ExecPolicyAmendment>,
pub parsed_cmd: Vec<ParsedCommand>,
}