1use std::collections::HashSet;
8use std::time::{Duration, Instant};
9use thiserror::Error;
10
11#[derive(Debug, Error)]
12pub enum SandboxError {
13 #[error("capability '{0}' not granted")]
14 CapabilityNotGranted(String),
15
16 #[error("resource limit exceeded: {0}")]
17 ResourceLimitExceeded(String),
18
19 #[error("operation timed out")]
20 Timeout,
21
22 #[error("operation rate limited")]
23 RateLimited,
24}
25
26#[derive(Debug, Clone, PartialEq, Eq, Hash)]
28pub enum Capability {
29 Voice,
31 Network,
33 FileSystem,
35 Display,
37 UserInteraction,
39}
40
41#[derive(Debug, Clone)]
43pub struct ResourceLimits {
44 pub max_memory: usize,
46 pub max_execution_time: Duration,
48 pub rate_limit: Option<u32>,
50}
51
52impl Default for ResourceLimits {
53 fn default() -> Self {
54 Self {
55 max_memory: 100 * 1024 * 1024, max_execution_time: Duration::from_secs(30),
57 rate_limit: Some(60), }
59 }
60}
61
62pub struct Sandbox {
64 granted_capabilities: HashSet<Capability>,
65 resource_limits: ResourceLimits,
66 start_time: Instant,
67}
68
69impl Sandbox {
70 pub fn new(capabilities: Vec<Capability>, limits: ResourceLimits) -> Self {
72 Self {
73 granted_capabilities: capabilities.into_iter().collect(),
74 resource_limits: limits,
75 start_time: Instant::now(),
76 }
77 }
78
79 pub fn check_capability(&self, capability: &Capability) -> Result<(), SandboxError> {
81 if self.granted_capabilities.contains(capability) {
82 Ok(())
83 } else {
84 Err(SandboxError::CapabilityNotGranted(format!("{capability:?}")))
85 }
86 }
87
88 pub fn check_timeout(&self) -> Result<(), SandboxError> {
90 if self.start_time.elapsed() > self.resource_limits.max_execution_time {
91 Err(SandboxError::Timeout)
92 } else {
93 Ok(())
94 }
95 }
96}