Skip to main content

sigil_protocol/
policy.rs

1//! Policy — permission and rate-limiting enforcement.
2//!
3//! SIGIL defines the risk model and policy trait.
4//! Implementations configure their own rules (allowlists, rate limits,
5//! confirmation requirements, etc.).
6
7use serde::{Deserialize, Serialize};
8
9/// Risk level classification for actions.
10///
11/// Every action in a SIGIL-protected system is classified into one
12/// of these three levels. The policy then decides what to do.
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
14pub enum RiskLevel {
15    /// Safe actions (read-only, within workspace).
16    Low,
17    /// Actions that modify state but are recoverable.
18    Medium,
19    /// Destructive, external, or irreversible actions.
20    High,
21}
22
23impl std::fmt::Display for RiskLevel {
24    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25        match self {
26            RiskLevel::Low => write!(f, "low"),
27            RiskLevel::Medium => write!(f, "medium"),
28            RiskLevel::High => write!(f, "high"),
29        }
30    }
31}
32
33/// Trait for security policy enforcement.
34///
35/// Implementations define their own rules for what actions are allowed,
36/// what requires confirmation, and how rate limiting works.
37///
38/// # Protocol Requirements
39///
40/// A conforming implementation MUST:
41/// 1. Classify all actions by `RiskLevel`
42/// 2. Enforce rate limiting via `record_action()`
43/// 3. Gate high-risk actions through `requires_confirmation()`
44///
45/// # Example
46///
47/// ```rust,no_run
48/// use sigil_protocol::{SecurityPolicy, RiskLevel};
49///
50/// struct StrictPolicy;
51///
52/// impl SecurityPolicy for StrictPolicy {
53///     fn is_action_allowed(&self, action: &str) -> bool { false }
54///     fn risk_level(&self, action: &str) -> RiskLevel { RiskLevel::High }
55///     fn requires_confirmation(&self, action: &str) -> bool { true }
56///     fn record_action(&self) -> bool { true }
57///     fn is_rate_limited(&self) -> bool { false }
58/// }
59/// ```
60pub trait SecurityPolicy: Send + Sync {
61    /// Check if an action (tool/command name) is allowed to execute.
62    fn is_action_allowed(&self, action: &str) -> bool;
63
64    /// Classify the risk level of an action.
65    fn risk_level(&self, action: &str) -> RiskLevel;
66
67    /// Check if an action requires explicit user confirmation.
68    fn requires_confirmation(&self, action: &str) -> bool;
69
70    /// Record an action execution for rate limiting.
71    /// Returns `true` if the action is within rate limits, `false` if exceeded.
72    fn record_action(&self) -> bool;
73
74    /// Check if the rate limit would be exceeded (without recording).
75    fn is_rate_limited(&self) -> bool;
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[test]
83    fn risk_level_display() {
84        assert_eq!(format!("{}", RiskLevel::Low), "low");
85        assert_eq!(format!("{}", RiskLevel::Medium), "medium");
86        assert_eq!(format!("{}", RiskLevel::High), "high");
87    }
88
89    #[test]
90    fn risk_level_serializes() {
91        let json = serde_json::to_string(&RiskLevel::High).unwrap();
92        let parsed: RiskLevel = serde_json::from_str(&json).unwrap();
93        assert_eq!(parsed, RiskLevel::High);
94    }
95}