Skip to main content

crue_dsl/
lib.rs

1//! CRUE DSL - Domain Specific Language for Zero-Trust Rules
2//!
3//! This module provides a compiled DSL for defining access control rules
4//! that are signed, versioned, and cannot be bypassed at runtime.
5//!
6//! ## DSL Syntax Example
7//!
8//! ```crue
9//! RULE CRUE_001 VERSION 1.2.0 SIGNED
10//! WHEN
11//!     agent.requests_last_hour >= 50
12//! THEN
13//!     BLOCK WITH CODE "VOLUME_EXCEEDED"
14//!     ALERT SOC
15//! ```
16
17pub mod ast;
18pub mod compiler;
19pub mod error;
20pub mod parser;
21pub mod signature;
22
23use serde::{Deserialize, Serialize};
24
25/// CRUE Rule identifier following the specification
26pub const RULE_PREFIX: &str = "CRUE";
27pub const RULE_VERSION: &str = "1.0.0";
28
29/// Rule severity levels
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
31#[serde(rename_all = "UPPERCASE")]
32pub enum Severity {
33    /// Critical - Immediate block
34    Critical,
35    /// High - Block with alert
36    #[default]
37    High,
38    /// Medium - Warning
39    Medium,
40    /// Low - Log only
41    Low,
42}
43
44/// Rule action types
45#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
46#[serde(rename_all = "UPPERCASE")]
47pub enum Action {
48    /// Block the request
49    Block {
50        #[serde(rename = "code")]
51        error_code: String,
52        #[serde(rename = "message")]
53        error_message: Option<String>,
54    },
55    /// Allow with warning
56    Warn {
57        #[serde(rename = "code")]
58        warning_code: String,
59    },
60    /// Require approval from supervisor
61    RequireApproval {
62        #[serde(rename = "code")]
63        approval_code: String,
64        #[serde(rename = "timeout_minutes")]
65        timeout: u32,
66    },
67    /// Log the event
68    Log,
69    /// Alert SOC team
70    AlertSoc,
71}
72
73impl Default for Action {
74    fn default() -> Self {
75        Action::Block {
76            error_code: "UNKNOWN_ERROR".to_string(),
77            error_message: None,
78        }
79    }
80}
81
82/// Compiled rule binary representation
83#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct CompiledRule {
85    /// Rule ID (e.g., "CRUE_001")
86    pub id: String,
87    /// Semantic version
88    pub version: String,
89    /// SHA-256 hash of the rule source
90    pub source_hash: String,
91    /// Cryptographic signature (RSA-PSS)
92    pub signature: Vec<u8>,
93    /// Signer key ID
94    pub signer_key_id: String,
95    /// Compiled bytecode
96    pub bytecode: Vec<u8>,
97    /// Timestamp of compilation
98    pub compiled_at: i64,
99    /// Validity period
100    pub valid_from: Option<i64>,
101    pub valid_until: Option<i64>,
102}
103
104/// DSL source code representation
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct RuleSource {
107    /// Rule ID
108    pub id: String,
109    /// Version
110    pub version: String,
111    /// WHEN conditions (AST)
112    pub conditions: ast::Expression,
113    /// THEN actions
114    pub actions: Vec<Action>,
115    /// Optional metadata
116    pub metadata: RuleMetadata,
117}
118
119/// Rule metadata
120#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct RuleMetadata {
122    /// Human-readable name
123    pub name: String,
124    /// Description
125    pub description: String,
126    /// Severity level
127    pub severity: Severity,
128    /// Category
129    pub category: String,
130    /// Author
131    pub author: String,
132    /// Creation date
133    pub created_at: String,
134    /// Validated by
135    pub validated_by: Option<String>,
136}
137
138#[cfg(test)]
139mod tests {
140    use super::*;
141
142    #[test]
143    fn test_action_serialization() {
144        let action = Action::Block {
145            error_code: "VOLUME_EXCEEDED".to_string(),
146            error_message: Some("Quota dépassé".to_string()),
147        };
148
149        let json = serde_json::to_string(&action).unwrap();
150        assert!(json.contains("VOLUME_EXCEEDED"));
151    }
152
153    #[test]
154    fn test_severity_default() {
155        let severity: Severity = serde_json::from_str("\"HIGH\"").unwrap();
156        assert_eq!(severity, Severity::High);
157    }
158}