use super::{
approval_rules, external_roots, PolicyAction, PolicyEvaluation, ToolApprovalDecision,
ToolApprovalPolicy,
};
impl ToolApprovalPolicy {
pub fn evaluate_detailed(&self, tool_name: &str, args: &serde_json::Value) -> PolicyEvaluation {
approval_rules::evaluate_tool_approval_policy(self, tool_name, args, None)
}
pub fn evaluate_detailed_with_repeat(
&self,
tool_name: &str,
args: &serde_json::Value,
repeat_count: u64,
) -> PolicyEvaluation {
approval_rules::evaluate_tool_approval_policy(self, tool_name, args, Some(repeat_count))
}
pub fn evaluate(&self, tool_name: &str, args: &serde_json::Value) -> ToolApprovalDecision {
let decision = self.evaluate_detailed(tool_name, args);
if decision.is_deny() {
return ToolApprovalDecision::AutoDenied {
reason: decision.reason,
};
}
if decision.is_ask() {
return ToolApprovalDecision::RequiresHostApproval;
}
ToolApprovalDecision::AutoApproved
}
pub fn intersect(&self, other: &ToolApprovalPolicy) -> ToolApprovalPolicy {
let auto_approve = if self.auto_approve.is_empty() {
other.auto_approve.clone()
} else if other.auto_approve.is_empty() {
self.auto_approve.clone()
} else {
self.auto_approve
.iter()
.filter(|p| other.auto_approve.contains(p))
.cloned()
.collect()
};
let mut auto_deny = self.auto_deny.clone();
auto_deny.extend(other.auto_deny.iter().cloned());
let mut require_approval = self.require_approval.clone();
require_approval.extend(other.require_approval.iter().cloned());
let write_path_allowlist = if self.write_path_allowlist.is_empty() {
other.write_path_allowlist.clone()
} else if other.write_path_allowlist.is_empty() {
self.write_path_allowlist.clone()
} else {
self.write_path_allowlist
.iter()
.filter(|p| other.write_path_allowlist.contains(p))
.cloned()
.collect()
};
let mut rules = self.rules.clone();
rules.extend(other.rules.iter().cloned());
let mut sensitive_path_patterns = self.sensitive_path_patterns.clone();
sensitive_path_patterns.extend(other.sensitive_path_patterns.iter().cloned());
sensitive_path_patterns.sort();
sensitive_path_patterns.dedup();
let external_roots = external_roots::intersect(&self.external_roots, &other.external_roots);
ToolApprovalPolicy {
rules,
auto_approve,
auto_deny,
require_approval,
write_path_allowlist,
allow_sensitive_paths: self.allow_sensitive_paths && other.allow_sensitive_paths,
sensitive_path_patterns,
allow_external_paths: self.allow_external_paths && other.allow_external_paths,
external_roots,
repeat_limit: match (self.repeat_limit, other.repeat_limit) {
(Some(left), Some(right)) => Some(left.min(right)),
(Some(left), None) => Some(left),
(None, Some(right)) => Some(right),
(None, None) => None,
},
repeat_action: match (self.repeat_action, other.repeat_action) {
(Some(PolicyAction::Deny), _) | (_, Some(PolicyAction::Deny)) => {
Some(PolicyAction::Deny)
}
(Some(PolicyAction::Ask), _) | (_, Some(PolicyAction::Ask)) => {
Some(PolicyAction::Ask)
}
(Some(PolicyAction::Allow), Some(PolicyAction::Allow)) => Some(PolicyAction::Allow),
(Some(action), None) | (None, Some(action)) => Some(action),
(None, None) => None,
},
}
}
}