a3s_code_core/permissions/
risk.rs1use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
15#[serde(rename_all = "snake_case")]
16pub enum ToolRiskLevel {
17 Routine,
19 Bounded,
21 High,
23 Critical,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
29#[serde(rename_all = "snake_case")]
30pub enum ToolRiskAction {
31 Allow,
33 RequireConfirmation,
35 ReviewByLlm,
37 RuleDeny,
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
43#[serde(rename_all = "snake_case")]
44pub enum ToolRiskType {
45 ReadOnly,
46 WorkspaceMutation,
47 CommandExecution,
48 VersionControl,
49 NetworkRead,
50 Delegation,
51 DynamicExecution,
52 ExternalIntegration,
53 Composite,
54 Unknown,
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
59#[serde(rename_all = "snake_case")]
60pub enum OperationTarget {
61 Workspace,
62 OutsideWorkspace,
63 PublicNetwork,
64 ExternalService,
65 HostEnvironment,
66 PrivilegedSystem,
67 Multiple,
68 Unknown,
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
73#[serde(rename_all = "snake_case")]
74pub enum ImpactScope {
75 Observation,
76 SingleResource,
77 Workspace,
78 ExternalService,
79 Host,
80 SystemWide,
81 Unknown,
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
86#[serde(rename_all = "snake_case")]
87pub enum Reversibility {
88 NotApplicable,
89 Easy,
90 Conditional,
91 Difficult,
92 Irreversible,
93 Unknown,
94}
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
98#[serde(rename_all = "snake_case")]
99pub enum EnvironmentSensitivity {
100 None,
101 Workspace,
102 Network,
103 Host,
104 Credentials,
105 Privileged,
106 Mixed,
107 Unknown,
108}
109
110#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
112#[serde(rename_all = "snake_case")]
113pub enum ToolRiskReason {
114 KnownReadOnly,
115 BoundedWorkspaceMutation,
116 BoundedVersionControlMutation,
117 UnboundedCommandExecution,
118 DynamicOrDelegatedExecution,
119 ExternalOrUnknownIntegration,
120 EnvironmentSensitiveOperation,
121 ForceOrDestructiveOperation,
122 MalformedOrUnknownOperation,
123 WorkspaceBoundaryEscape,
124 SymlinkBoundaryEscape,
125 CatastrophicOperation,
126 CompositeInvocation,
127}
128
129#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
131pub struct ToolRiskDimensions {
132 pub tool_type: ToolRiskType,
133 pub operation_target: OperationTarget,
134 pub impact_scope: ImpactScope,
135 pub reversibility: Reversibility,
136 pub environment_sensitivity: EnvironmentSensitivity,
137}
138
139impl ToolRiskDimensions {
140 pub const fn new(
141 tool_type: ToolRiskType,
142 operation_target: OperationTarget,
143 impact_scope: ImpactScope,
144 reversibility: Reversibility,
145 environment_sensitivity: EnvironmentSensitivity,
146 ) -> Self {
147 Self {
148 tool_type,
149 operation_target,
150 impact_scope,
151 reversibility,
152 environment_sensitivity,
153 }
154 }
155}
156
157#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
159pub struct ToolRiskAssessment {
160 pub level: ToolRiskLevel,
161 pub dimensions: ToolRiskDimensions,
162 pub reasons: Vec<ToolRiskReason>,
163}
164
165impl ToolRiskAssessment {
166 pub fn new(
167 level: ToolRiskLevel,
168 dimensions: ToolRiskDimensions,
169 reasons: impl IntoIterator<Item = ToolRiskReason>,
170 ) -> Self {
171 let mut deduplicated = Vec::new();
172 for reason in reasons {
173 if !deduplicated.contains(&reason) {
174 deduplicated.push(reason);
175 }
176 }
177 Self {
178 level,
179 dimensions,
180 reasons: deduplicated,
181 }
182 }
183
184 pub const fn baseline_action(&self) -> ToolRiskAction {
189 match self.level {
190 ToolRiskLevel::Routine => ToolRiskAction::Allow,
191 ToolRiskLevel::Bounded => ToolRiskAction::RequireConfirmation,
192 ToolRiskLevel::High => ToolRiskAction::ReviewByLlm,
193 ToolRiskLevel::Critical => ToolRiskAction::RuleDeny,
194 }
195 }
196}