do not send allow_prefix if execpolicy is disabled

This commit is contained in:
kevin zhao
2025-11-21 15:26:22 -05:00
parent 2a4e833e91
commit d747d1d492
4 changed files with 52 additions and 4 deletions

View File

@@ -1116,6 +1116,15 @@ impl Session {
.enabled(feature)
}
pub(crate) async fn features(&self) -> Features {
self.state
.lock()
.await
.session_configuration
.features
.clone()
}
async fn send_raw_response_items(&self, turn_context: &TurnContext, items: &[ResponseItem]) {
for item in items {
self.send_event(

View File

@@ -157,12 +157,13 @@ fn requirement_from_decision(
/// Return an allow-prefix option when a single plain command needs approval without
/// any matching policy rule. We only surface the prefix opt-in when execpolicy did
/// not already drive the decision (NoMatch) and when the command is a single
/// unrolled command (multi-part scripts shouldnt be whitelisted via prefix).
/// unrolled command (multi-part scripts shouldnt be whitelisted via prefix) and
/// when execpolicy feature is enabled.
fn allow_prefix_if_applicable(
commands: &[Vec<String>],
evaluation: &Evaluation,
features: &Features,
) -> Option<Vec<String>> {
if matches!(evaluation, Evaluation::NoMatch) && commands.len() == 1 {
if features.enabled(Feature::ExecPolicy) && commands.len() == 1 {
return Some(commands[0].clone());
}
@@ -173,6 +174,7 @@ pub(crate) fn create_approval_requirement_for_command(
policy: &Policy,
command: &[String],
approval_policy: AskForApproval,
features: &Features,
sandbox_policy: &SandboxPolicy,
sandbox_permissions: SandboxPermissions,
) -> ApprovalRequirement {
@@ -190,7 +192,7 @@ pub(crate) fn create_approval_requirement_for_command(
) {
ApprovalRequirement::NeedsApproval {
reason: None,
allow_prefix: allow_prefix_if_applicable(&commands, &evaluation),
allow_prefix: allow_prefix_if_applicable(&commands, features),
}
} else {
ApprovalRequirement::Skip {
@@ -350,6 +352,7 @@ prefix_rule(pattern=["rm"], decision="forbidden")
&policy,
&forbidden_script,
AskForApproval::OnRequest,
&Features::with_defaults(),
&SandboxPolicy::DangerFullAccess,
SandboxPermissions::UseDefault,
);
@@ -376,6 +379,7 @@ prefix_rule(pattern=["rm"], decision="forbidden")
&policy,
&command,
AskForApproval::OnRequest,
&Features::with_defaults(),
&SandboxPolicy::DangerFullAccess,
SandboxPermissions::UseDefault,
);
@@ -403,6 +407,7 @@ prefix_rule(pattern=["rm"], decision="forbidden")
&policy,
&command,
AskForApproval::Never,
&Features::with_defaults(),
&SandboxPolicy::DangerFullAccess,
SandboxPermissions::UseDefault,
);
@@ -424,6 +429,7 @@ prefix_rule(pattern=["rm"], decision="forbidden")
&empty_policy,
&command,
AskForApproval::UnlessTrusted,
&Features::with_defaults(),
&SandboxPolicy::ReadOnly,
SandboxPermissions::UseDefault,
);
@@ -494,6 +500,7 @@ prefix_rule(pattern=["rm"], decision="forbidden")
&empty_policy,
&command,
AskForApproval::UnlessTrusted,
&Features::with_defaults(),
&SandboxPolicy::ReadOnly,
SandboxPermissions::UseDefault,
);
@@ -507,6 +514,31 @@ prefix_rule(pattern=["rm"], decision="forbidden")
);
}
#[test]
fn allow_prefix_is_disabled_when_execpolicy_feature_disabled() {
let command = vec!["python".to_string()];
let mut features = Features::with_defaults();
features.disable(Feature::ExecPolicy);
let requirement = create_approval_requirement_for_command(
&Policy::empty(),
&command,
AskForApproval::UnlessTrusted,
&features,
&SandboxPolicy::ReadOnly,
SandboxPermissions::UseDefault,
);
assert_eq!(
requirement,
ApprovalRequirement::NeedsApproval {
reason: None,
allow_prefix: None,
}
);
}
#[test]
fn allow_prefix_is_omitted_when_policy_prompts() {
let policy_src = r#"prefix_rule(pattern=["rm"], decision="prompt")"#;
@@ -521,6 +553,7 @@ prefix_rule(pattern=["rm"], decision="forbidden")
&policy,
&command,
AskForApproval::OnRequest,
&Features::with_defaults(),
&SandboxPolicy::DangerFullAccess,
SandboxPermissions::UseDefault,
);
@@ -545,6 +578,7 @@ prefix_rule(pattern=["rm"], decision="forbidden")
&Policy::empty(),
&command,
AskForApproval::UnlessTrusted,
&Features::with_defaults(),
&SandboxPolicy::ReadOnly,
SandboxPermissions::UseDefault,
);

View File

@@ -231,6 +231,7 @@ impl ShellHandler {
let event_ctx = ToolEventCtx::new(session.as_ref(), turn.as_ref(), &call_id, None);
emitter.begin(event_ctx).await;
let features = session.features().await;
let exec_policy = session.current_exec_policy().await;
let exec_policy = exec_policy.read().await;
let req = ShellRequest {
@@ -244,6 +245,7 @@ impl ShellHandler {
&exec_policy,
&exec_params.command,
turn.approval_policy,
&features,
&turn.sandbox_policy,
SandboxPermissions::from(exec_params.with_escalated_permissions.unwrap_or(false)),
),

View File

@@ -552,6 +552,8 @@ impl UnifiedExecSessionManager {
context: &UnifiedExecContext,
) -> Result<UnifiedExecSession, UnifiedExecError> {
let env = apply_unified_exec_env(create_env(&context.turn.shell_environment_policy));
let features = context.session.features().await;
let features = context.session.features().await;
let mut orchestrator = ToolOrchestrator::new();
let mut runtime = UnifiedExecRuntime::new(self);
let exec_policy = context.session.current_exec_policy().await;
@@ -566,6 +568,7 @@ impl UnifiedExecSessionManager {
&exec_policy,
command,
context.turn.approval_policy,
&features,
&context.turn.sandbox_policy,
SandboxPermissions::from(with_escalated_permissions.unwrap_or(false)),
),