Skip to main content

vtcode_safety/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    fn requires_approval(&self) -> bool {
31        matches!(self, Self::RequireEscalated | Self::WithAdditionalPermissions | Self::BypassSandbox)
32    }
33
34    /// Check if this permission requests full unsandboxed execution.
35    pub fn requires_escalated_permissions(&self) -> bool {
36        matches!(self, Self::RequireEscalated | Self::BypassSandbox)
37    }
38
39    /// Check if this permission requests any additional privileges.
40    fn requires_additional_permissions(&self) -> bool {
41        !matches!(self, Self::UseDefault)
42    }
43
44    /// Check if this permission requests additional sandboxed permissions.
45    pub fn uses_additional_permissions(&self) -> bool {
46        matches!(self, Self::WithAdditionalPermissions)
47    }
48
49    /// Check if this permission bypasses the sandbox.
50    fn bypasses_sandbox(&self) -> bool {
51        matches!(self, Self::BypassSandbox)
52    }
53
54    /// Merge with another permission, taking the more permissive one.
55    fn merge(&self, other: &Self) -> Self {
56        use SandboxPermissions::*;
57        match (self, other) {
58            (BypassSandbox, _) | (_, BypassSandbox) => BypassSandbox,
59            (RequireEscalated, _) | (_, RequireEscalated) => RequireEscalated,
60            (WithAdditionalPermissions, _) | (_, WithAdditionalPermissions) => WithAdditionalPermissions,
61            (UseDefault, UseDefault) => UseDefault,
62        }
63    }
64}
65
66/// Additional per-command filesystem permissions.
67#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
68pub struct AdditionalPermissions {
69    /// Additional filesystem paths to grant read access.
70    #[serde(default, skip_serializing_if = "Vec::is_empty")]
71    pub fs_read: Vec<PathBuf>,
72    /// Additional filesystem paths to grant write access.
73    #[serde(default, skip_serializing_if = "Vec::is_empty")]
74    pub fs_write: Vec<PathBuf>,
75}
76
77impl AdditionalPermissions {
78    pub fn is_empty(&self) -> bool {
79        self.fs_read.is_empty() && self.fs_write.is_empty()
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    #[test]
88    fn test_default_permissions() {
89        let perm = SandboxPermissions::default();
90        assert!(!perm.requires_approval());
91        assert!(!perm.bypasses_sandbox());
92    }
93
94    #[test]
95    fn test_escalated_permissions() {
96        let perm = SandboxPermissions::RequireEscalated;
97        assert!(perm.requires_approval());
98        assert!(!perm.bypasses_sandbox());
99        assert!(perm.requires_escalated_permissions());
100    }
101
102    #[test]
103    fn test_bypass_permissions() {
104        let perm = SandboxPermissions::BypassSandbox;
105        assert!(perm.requires_approval());
106        assert!(perm.bypasses_sandbox());
107        assert!(perm.requires_escalated_permissions());
108    }
109
110    #[test]
111    fn test_with_additional_permissions() {
112        let perm = SandboxPermissions::WithAdditionalPermissions;
113        assert!(perm.requires_approval());
114        assert!(perm.requires_additional_permissions());
115        assert!(perm.uses_additional_permissions());
116        assert!(!perm.requires_escalated_permissions());
117        assert!(!perm.bypasses_sandbox());
118    }
119
120    #[test]
121    fn test_merge_permissions() {
122        use SandboxPermissions::*;
123
124        assert_eq!(UseDefault.merge(&UseDefault), UseDefault);
125        assert_eq!(UseDefault.merge(&WithAdditionalPermissions), WithAdditionalPermissions);
126        assert_eq!(UseDefault.merge(&RequireEscalated), RequireEscalated);
127        assert_eq!(RequireEscalated.merge(&BypassSandbox), BypassSandbox);
128    }
129}