Skip to main content

vtcode_core/sandboxing/
permissions.rs

1//! Sandbox permissions for fine-grained access control.
2
3use std::path::PathBuf;
4
5use serde::{Deserialize, Serialize};
6
7/// Fine-grained permissions for sandbox operations.
8///
9/// These permissions allow individual tool calls to request specific
10/// capabilities beyond the base sandbox policy.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
12#[serde(rename_all = "snake_case")]
13pub enum SandboxPermissions {
14    /// Use the default sandbox permissions from the policy.
15    #[default]
16    UseDefault,
17
18    /// Request escalated permissions (requires approval).
19    RequireEscalated,
20
21    /// Request additional per-command sandbox permissions.
22    WithAdditionalPermissions,
23
24    /// Bypass the sandbox entirely (requires explicit approval).
25    BypassSandbox,
26}
27
28impl SandboxPermissions {
29    /// Check if this permission requires approval.
30    pub fn requires_approval(&self) -> bool {
31        matches!(
32            self,
33            Self::RequireEscalated | Self::WithAdditionalPermissions | Self::BypassSandbox
34        )
35    }
36
37    /// Check if this permission requests full unsandboxed execution.
38    pub fn requires_escalated_permissions(&self) -> bool {
39        matches!(self, Self::RequireEscalated | Self::BypassSandbox)
40    }
41
42    /// Check if this permission requests any additional privileges.
43    pub fn requires_additional_permissions(&self) -> bool {
44        !matches!(self, Self::UseDefault)
45    }
46
47    /// Check if this permission requests additional sandboxed permissions.
48    pub fn uses_additional_permissions(&self) -> bool {
49        matches!(self, Self::WithAdditionalPermissions)
50    }
51
52    /// Check if this permission bypasses the sandbox.
53    pub fn bypasses_sandbox(&self) -> bool {
54        matches!(self, Self::BypassSandbox)
55    }
56
57    /// Merge with another permission, taking the more permissive one.
58    pub fn merge(&self, other: &Self) -> Self {
59        use SandboxPermissions::*;
60        match (self, other) {
61            (BypassSandbox, _) | (_, BypassSandbox) => BypassSandbox,
62            (RequireEscalated, _) | (_, RequireEscalated) => RequireEscalated,
63            (WithAdditionalPermissions, _) | (_, WithAdditionalPermissions) => {
64                WithAdditionalPermissions
65            }
66            (UseDefault, UseDefault) => UseDefault,
67        }
68    }
69}
70
71/// Additional per-command filesystem permissions.
72#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
73pub struct AdditionalPermissions {
74    /// Additional filesystem paths to grant read access.
75    #[serde(default, skip_serializing_if = "Vec::is_empty")]
76    pub fs_read: Vec<PathBuf>,
77    /// Additional filesystem paths to grant write access.
78    #[serde(default, skip_serializing_if = "Vec::is_empty")]
79    pub fs_write: Vec<PathBuf>,
80}
81
82impl AdditionalPermissions {
83    pub fn is_empty(&self) -> bool {
84        self.fs_read.is_empty() && self.fs_write.is_empty()
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91
92    #[test]
93    fn test_default_permissions() {
94        let perm = SandboxPermissions::default();
95        assert!(!perm.requires_approval());
96        assert!(!perm.bypasses_sandbox());
97    }
98
99    #[test]
100    fn test_escalated_permissions() {
101        let perm = SandboxPermissions::RequireEscalated;
102        assert!(perm.requires_approval());
103        assert!(!perm.bypasses_sandbox());
104        assert!(perm.requires_escalated_permissions());
105    }
106
107    #[test]
108    fn test_bypass_permissions() {
109        let perm = SandboxPermissions::BypassSandbox;
110        assert!(perm.requires_approval());
111        assert!(perm.bypasses_sandbox());
112        assert!(perm.requires_escalated_permissions());
113    }
114
115    #[test]
116    fn test_with_additional_permissions() {
117        let perm = SandboxPermissions::WithAdditionalPermissions;
118        assert!(perm.requires_approval());
119        assert!(perm.requires_additional_permissions());
120        assert!(perm.uses_additional_permissions());
121        assert!(!perm.requires_escalated_permissions());
122        assert!(!perm.bypasses_sandbox());
123    }
124
125    #[test]
126    fn test_merge_permissions() {
127        use SandboxPermissions::*;
128
129        assert_eq!(UseDefault.merge(&UseDefault), UseDefault);
130        assert_eq!(
131            UseDefault.merge(&WithAdditionalPermissions),
132            WithAdditionalPermissions
133        );
134        assert_eq!(UseDefault.merge(&RequireEscalated), RequireEscalated);
135        assert_eq!(RequireEscalated.merge(&BypassSandbox), BypassSandbox);
136    }
137}