1use serde::{Deserialize, Serialize};
4
5use crate::precedence::{PolicyVerdict, VerdictKind};
6
7#[derive(Debug, Clone, Serialize, Deserialize, Default)]
9pub struct ToolAllowlist {
10 pub allowed_tools: Vec<String>,
12
13 pub approval_required: Vec<String>,
15
16 pub denied_tools: Vec<String>,
18}
19
20impl ToolAllowlist {
21 pub fn check(&self, tool_name: &str) -> ToolAllowlistResult {
26 if self.denied_tools.iter().any(|t| t == tool_name) {
28 return ToolAllowlistResult::Denied;
29 }
30
31 if self.approval_required.iter().any(|t| t == tool_name) {
33 return ToolAllowlistResult::RequiresApproval;
34 }
35
36 if self.allowed_tools.iter().any(|t| t == tool_name) {
38 return ToolAllowlistResult::Allowed;
39 }
40
41 ToolAllowlistResult::Denied
43 }
44
45 pub fn matches(&self, tool_name: &str) -> Vec<PolicyVerdict> {
50 let mut out = Vec::new();
51 if self.denied_tools.iter().any(|t| t == tool_name) {
52 out.push(PolicyVerdict::new(
53 VerdictKind::Deny,
54 "allowlist:denied",
55 format!("tool '{}' is on the explicit denylist", tool_name),
56 ));
57 }
58 if self.approval_required.iter().any(|t| t == tool_name) {
59 out.push(PolicyVerdict::new(
60 VerdictKind::RequiresApproval,
61 "allowlist:approval",
62 format!("tool '{}' requires approval before execution", tool_name),
63 ));
64 }
65 if self.allowed_tools.iter().any(|t| t == tool_name) {
66 out.push(PolicyVerdict::new(
67 VerdictKind::Allow,
68 "allowlist:allowed",
69 format!("tool '{}' is in the allowlist", tool_name),
70 ));
71 }
72 out
73 }
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub enum ToolAllowlistResult {
79 Allowed,
80 RequiresApproval,
81 Denied,
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
86#[serde(rename_all = "snake_case")]
87#[derive(Default)]
88pub enum ToolRiskLevel {
89 Low,
91 #[default]
93 Medium,
94 High,
96 Critical,
98}