Skip to main content

symbi_runtime/types/
security.rs

1//! Security-related types and data structures
2
3use serde::{Deserialize, Serialize};
4use std::time::{Duration, SystemTime};
5
6use super::{AgentId, PolicyId};
7
8/// Security tiers for sandboxing.
9///
10/// `Tier1` → `Tier3` form a monotonically increasing host-isolation ladder
11/// (Docker → gVisor → Firecracker). `Hosted` is **not** a peer on that
12/// ladder — it represents execution on third-party infrastructure (e.g.
13/// E2B) where the operator does not run their own sandbox host. It carries
14/// no on-host isolation guarantees but is not outright `None`, so it sits
15/// between `None` and `Tier1` for ordering purposes.
16///
17/// Use `tier >= SecurityTier::Tier1` when policies require host isolation.
18#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default)]
19pub enum SecurityTier {
20    /// No isolation — native execution (⚠️ DEVELOPMENT ONLY)
21    None,
22    /// Hosted cloud execution (e.g. E2B) — code runs on third-party
23    /// infrastructure. No on-host isolation guarantees; trust assumption
24    /// is "you trust the hosting provider."
25    Hosted,
26    /// Docker-based isolation
27    #[default]
28    Tier1,
29    /// gVisor-based isolation
30    Tier2,
31    /// Firecracker-based isolation
32    Tier3,
33}
34
35impl std::fmt::Display for SecurityTier {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        match self {
38            SecurityTier::None => write!(f, "None (Native - No Isolation ⚠️)"),
39            SecurityTier::Hosted => write!(f, "Hosted (third-party cloud sandbox)"),
40            SecurityTier::Tier1 => write!(f, "Tier1 (Docker)"),
41            SecurityTier::Tier2 => write!(f, "Tier2 (gVisor)"),
42            SecurityTier::Tier3 => write!(f, "Tier3 (Firecracker)"),
43        }
44    }
45}
46
47impl SecurityTier {
48    /// Map a DSL `sandbox_tier` value to the runtime's `SecurityTier`.
49    ///
50    /// `dsl::SandboxTier::E2B` maps to `Hosted` — it is not a host-
51    /// isolation tier. The runner factory still picks the actual E2B
52    /// backend; this mapping only affects how the agent's security
53    /// requirements are categorised in policy decisions.
54    pub fn from_dsl_sandbox(tier: &dsl::SandboxTier) -> Self {
55        match tier {
56            dsl::SandboxTier::Docker => SecurityTier::Tier1,
57            dsl::SandboxTier::GVisor => SecurityTier::Tier2,
58            dsl::SandboxTier::Firecracker => SecurityTier::Tier3,
59            dsl::SandboxTier::E2B => SecurityTier::Hosted,
60        }
61    }
62}
63
64/// Risk assessment levels
65#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default)]
66pub enum RiskLevel {
67    Low,
68    #[default]
69    Medium,
70    High,
71    Critical,
72}
73
74/// Security configuration for the runtime
75#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct SecurityConfig {
77    pub default_security_tier: SecurityTier,
78    pub encryption_enabled: bool,
79    pub signature_required: bool,
80    pub policy_enforcement_strict: bool,
81    pub sandbox_isolation_level: IsolationLevel,
82    pub audit_all_operations: bool,
83    pub e2b_api_key: Option<String>,
84    /// Allow native execution without isolation (⚠️ DEVELOPMENT ONLY)
85    pub allow_native_execution: bool,
86}
87
88impl Default for SecurityConfig {
89    fn default() -> Self {
90        Self {
91            default_security_tier: SecurityTier::Tier1,
92            encryption_enabled: true,
93            signature_required: true,
94            policy_enforcement_strict: true,
95            sandbox_isolation_level: IsolationLevel::High,
96            audit_all_operations: true,
97            e2b_api_key: None,
98            allow_native_execution: false,
99        }
100    }
101}
102
103/// Isolation levels for sandboxing
104#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
105pub enum IsolationLevel {
106    None,
107    Low,
108    Medium,
109    #[default]
110    High,
111    Maximum,
112}
113
114/// Policy context for enforcement decisions
115#[derive(Debug, Clone, Serialize, Deserialize)]
116pub struct PolicyContext {
117    pub agent_id: AgentId,
118    pub operation: String,
119    pub resource: Option<String>,
120    pub timestamp: SystemTime,
121    pub security_tier: SecurityTier,
122    pub risk_level: RiskLevel,
123}
124
125/// Policy decision result
126#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
127pub enum PolicyDecision {
128    Allow,
129    Deny(String),
130    RequireApproval(String),
131}
132
133/// Policy enforcement result
134#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct PolicyResult {
136    pub decision: PolicyDecision,
137    pub policy_id: Option<PolicyId>,
138    pub reason: Option<String>,
139    pub timestamp: SystemTime,
140}
141
142impl PolicyResult {
143    pub fn allow() -> Self {
144        Self {
145            decision: PolicyDecision::Allow,
146            policy_id: None,
147            reason: None,
148            timestamp: SystemTime::now(),
149        }
150    }
151
152    pub fn deny(reason: String) -> Self {
153        Self {
154            decision: PolicyDecision::Deny(reason.clone()),
155            policy_id: None,
156            reason: Some(reason),
157            timestamp: SystemTime::now(),
158        }
159    }
160
161    pub fn require_approval(reason: String) -> Self {
162        Self {
163            decision: PolicyDecision::RequireApproval(reason.clone()),
164            policy_id: None,
165            reason: Some(reason),
166            timestamp: SystemTime::now(),
167        }
168    }
169}
170
171/// Types of security events
172#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
173pub enum SecurityEventType {
174    PolicyViolation,
175    UnauthorizedAccess,
176    EncryptionFailure,
177    SignatureVerificationFailure,
178    SandboxBreach,
179    ResourceExhaustion,
180    SuspiciousActivity,
181    CronJobDeadLettered,
182    AgentPinVerificationFailed,
183}
184
185/// Policy violation details
186#[derive(Debug, Clone, Serialize, Deserialize)]
187pub struct PolicyViolation {
188    pub policy_id: PolicyId,
189    pub violation_type: String,
190    pub description: String,
191    pub severity: ViolationSeverity,
192    pub timestamp: SystemTime,
193}
194
195/// Severity levels for policy violations
196#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default)]
197pub enum ViolationSeverity {
198    Info,
199    #[default]
200    Warning,
201    Error,
202    Critical,
203}
204
205/// Audit event types for the cryptographic audit trail
206#[derive(Debug, Clone, Serialize, Deserialize)]
207pub enum AuditEvent {
208    AgentCreated {
209        agent_id: AgentId,
210        config_hash: String,
211    },
212    AgentStarted {
213        agent_id: AgentId,
214        timestamp: SystemTime,
215    },
216    AgentTerminated {
217        agent_id: AgentId,
218        reason: super::agent::TerminationReason,
219    },
220    MessageSent {
221        from: AgentId,
222        to: Option<AgentId>,
223        message_id: super::MessageId,
224    },
225    PolicyViolation {
226        agent_id: AgentId,
227        violation: PolicyViolation,
228    },
229    ResourceAllocation {
230        agent_id: AgentId,
231        resources: super::resource::ResourceAllocation,
232    },
233    SecurityEvent {
234        event_type: SecurityEventType,
235        details: String,
236    },
237}
238
239/// Audit query for searching events
240#[derive(Debug, Clone, Serialize, Deserialize)]
241pub struct AuditQuery {
242    pub agent_id: Option<AgentId>,
243    pub event_types: Vec<String>,
244    pub start_time: Option<SystemTime>,
245    pub end_time: Option<SystemTime>,
246    pub limit: Option<usize>,
247}
248
249/// Audit configuration
250#[derive(Debug, Clone, Serialize, Deserialize)]
251pub struct AuditConfig {
252    pub enabled: bool,
253    pub sign_events: bool,
254    pub encrypt_events: bool,
255    pub retention_duration: Duration,
256    pub max_events_per_agent: usize,
257}
258
259impl Default for AuditConfig {
260    fn default() -> Self {
261        Self {
262            enabled: true,
263            sign_events: true,
264            encrypt_events: true,
265            retention_duration: Duration::from_secs(86400 * 365), // 1 year
266            max_events_per_agent: 10000,
267        }
268    }
269}
270
271/// Sandbox configuration for different security tiers
272#[derive(Debug, Clone, Serialize, Deserialize)]
273pub struct SandboxConfig {
274    pub security_tier: SecurityTier,
275    pub isolation_level: IsolationLevel,
276    pub network_isolation: bool,
277    pub filesystem_isolation: bool,
278    pub resource_limits: super::resource::ResourceLimits,
279    pub allowed_syscalls: Vec<String>,
280    pub environment_variables: std::collections::HashMap<String, String>,
281}
282
283impl Default for SandboxConfig {
284    fn default() -> Self {
285        Self {
286            security_tier: SecurityTier::Tier1,
287            isolation_level: IsolationLevel::High,
288            network_isolation: true,
289            filesystem_isolation: true,
290            resource_limits: super::resource::ResourceLimits::default(),
291            allowed_syscalls: vec![
292                "read".to_string(),
293                "write".to_string(),
294                "open".to_string(),
295                "close".to_string(),
296            ],
297            environment_variables: std::collections::HashMap::new(),
298        }
299    }
300}
301
302/// Sandbox status information
303#[derive(Debug, Clone, Serialize, Deserialize)]
304pub struct SandboxStatus {
305    pub id: String,
306    pub state: SandboxState,
307    pub security_tier: SecurityTier,
308    pub resource_usage: super::resource::ResourceUsage,
309    pub uptime: Duration,
310    pub last_activity: SystemTime,
311}
312
313/// Sandbox state
314#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
315pub enum SandboxState {
316    #[default]
317    Creating,
318    Ready,
319    Running,
320    Suspended,
321    Terminating,
322    Terminated,
323    Failed,
324}
325
326/// Represents different types of capabilities that agents can request
327#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
328pub enum Capability {
329    /// File system operations
330    FileRead(String),
331    FileWrite(String),
332    FileDelete(String),
333
334    /// Network operations
335    NetworkRequest(String),
336    NetworkListen(u16),
337
338    /// System operations
339    Execute(String),
340    EnvironmentRead(String),
341    EnvironmentWrite(String),
342
343    /// Agent operations
344    AgentCreate,
345    AgentDelete,
346    AgentModify,
347
348    /// Data operations
349    DataRead(String),
350    DataWrite(String),
351    DataDelete(String),
352}
353
354#[cfg(test)]
355mod from_dsl_sandbox_tests {
356    use super::*;
357
358    #[test]
359    fn dsl_sandbox_maps_to_runtime_tier() {
360        assert_eq!(
361            SecurityTier::from_dsl_sandbox(&dsl::SandboxTier::Docker),
362            SecurityTier::Tier1
363        );
364        assert_eq!(
365            SecurityTier::from_dsl_sandbox(&dsl::SandboxTier::GVisor),
366            SecurityTier::Tier2
367        );
368        assert_eq!(
369            SecurityTier::from_dsl_sandbox(&dsl::SandboxTier::Firecracker),
370            SecurityTier::Tier3
371        );
372        // E2B is hosted (third-party cloud), not a host-isolation tier.
373        assert_eq!(
374            SecurityTier::from_dsl_sandbox(&dsl::SandboxTier::E2B),
375            SecurityTier::Hosted
376        );
377    }
378
379    #[test]
380    fn hosted_orders_below_tier1() {
381        // Policies that require host isolation use `tier >= Tier1`. Hosted
382        // must sort below Tier1 so those checks reject it.
383        assert!(SecurityTier::Hosted < SecurityTier::Tier1);
384        assert!(SecurityTier::Hosted < SecurityTier::Tier2);
385        assert!(SecurityTier::Hosted < SecurityTier::Tier3);
386        assert!(SecurityTier::None < SecurityTier::Hosted);
387    }
388}