vtcode_safety/sandboxing/
permissions.rs1use std::path::PathBuf;
4
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
12#[serde(rename_all = "snake_case")]
13pub enum SandboxPermissions {
14 #[default]
16 UseDefault,
17
18 RequireEscalated,
20
21 WithAdditionalPermissions,
23
24 BypassSandbox,
26}
27
28impl SandboxPermissions {
29 #[must_use]
33 pub fn normalized_for(self, additional_permissions: Option<&AdditionalPermissions>) -> Self {
34 if self == Self::UseDefault && additional_permissions.is_some_and(|permissions| !permissions.is_empty()) {
35 Self::WithAdditionalPermissions
36 } else {
37 self
38 }
39 }
40
41 fn requires_approval(&self) -> bool {
43 matches!(self, Self::RequireEscalated | Self::WithAdditionalPermissions | Self::BypassSandbox)
44 }
45
46 pub fn requires_escalated_permissions(&self) -> bool {
48 matches!(self, Self::RequireEscalated | Self::BypassSandbox)
49 }
50
51 fn requires_additional_permissions(&self) -> bool {
53 !matches!(self, Self::UseDefault)
54 }
55
56 pub fn uses_additional_permissions(&self) -> bool {
58 matches!(self, Self::WithAdditionalPermissions)
59 }
60
61 fn bypasses_sandbox(&self) -> bool {
63 matches!(self, Self::BypassSandbox)
64 }
65
66 fn merge(&self, other: &Self) -> Self {
68 use SandboxPermissions::*;
69 match (self, other) {
70 (BypassSandbox, _) | (_, BypassSandbox) => BypassSandbox,
71 (RequireEscalated, _) | (_, RequireEscalated) => RequireEscalated,
72 (WithAdditionalPermissions, _) | (_, WithAdditionalPermissions) => WithAdditionalPermissions,
73 (UseDefault, UseDefault) => UseDefault,
74 }
75 }
76}
77
78#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
80pub struct AdditionalPermissions {
81 #[serde(default, skip_serializing_if = "Vec::is_empty")]
83 pub fs_read: Vec<PathBuf>,
84 #[serde(default, skip_serializing_if = "Vec::is_empty")]
86 pub fs_write: Vec<PathBuf>,
87}
88
89impl AdditionalPermissions {
90 pub fn is_empty(&self) -> bool {
91 self.fs_read.is_empty() && self.fs_write.is_empty()
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 #[test]
100 fn test_default_permissions() {
101 let perm = SandboxPermissions::default();
102 assert!(!perm.requires_approval());
103 assert!(!perm.bypasses_sandbox());
104 }
105
106 #[test]
107 fn test_escalated_permissions() {
108 let perm = SandboxPermissions::RequireEscalated;
109 assert!(perm.requires_approval());
110 assert!(!perm.bypasses_sandbox());
111 assert!(perm.requires_escalated_permissions());
112 }
113
114 #[test]
115 fn test_bypass_permissions() {
116 let perm = SandboxPermissions::BypassSandbox;
117 assert!(perm.requires_approval());
118 assert!(perm.bypasses_sandbox());
119 assert!(perm.requires_escalated_permissions());
120 }
121
122 #[test]
123 fn test_with_additional_permissions() {
124 let perm = SandboxPermissions::WithAdditionalPermissions;
125 assert!(perm.requires_approval());
126 assert!(perm.requires_additional_permissions());
127 assert!(perm.uses_additional_permissions());
128 assert!(!perm.requires_escalated_permissions());
129 assert!(!perm.bypasses_sandbox());
130 }
131
132 #[test]
133 fn default_normalizes_for_non_empty_additional_permissions() {
134 let additional = AdditionalPermissions {
135 fs_read: vec![PathBuf::from("/tmp/reference")],
136 fs_write: Vec::new(),
137 };
138
139 assert_eq!(
140 SandboxPermissions::UseDefault.normalized_for(Some(&additional)),
141 SandboxPermissions::WithAdditionalPermissions
142 );
143 assert_eq!(
144 SandboxPermissions::RequireEscalated.normalized_for(Some(&additional)),
145 SandboxPermissions::RequireEscalated
146 );
147 assert_eq!(
148 SandboxPermissions::UseDefault.normalized_for(Some(&AdditionalPermissions::default())),
149 SandboxPermissions::UseDefault
150 );
151 }
152
153 #[test]
154 fn test_merge_permissions() {
155 use SandboxPermissions::*;
156
157 assert_eq!(UseDefault.merge(&UseDefault), UseDefault);
158 assert_eq!(UseDefault.merge(&WithAdditionalPermissions), WithAdditionalPermissions);
159 assert_eq!(UseDefault.merge(&RequireEscalated), RequireEscalated);
160 assert_eq!(RequireEscalated.merge(&BypassSandbox), BypassSandbox);
161 }
162}