//! Compliance Checking Module //! //! Provides compliance validation for various frameworks including SOC2 and //! HIPAA. pub mod frameworks; pub mod hipaa; pub mod reports; pub mod soc2; use std::collections::HashMap; use std::sync::Arc; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use crate::error::{policy, ControlCenterError, Result}; use crate::policies::{PolicyCategory, PolicyMetadata, PolicyRequestContext, PolicyResult}; use crate::storage::PolicyMetadata as StoragePolicyMetadata; use crate::storage::{ComplianceCheckResult, PolicyStorage}; /// Compliance framework types #[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)] pub enum ComplianceFramework { SOC2, HIPAA, GDPR, ISO27001, NIST, #[serde(rename = "PCI_DSS")] PciDss, } /// Compliance check result #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ComplianceResult { pub framework: ComplianceFramework, pub overall_compliant: bool, pub control_results: Vec, pub violations: Vec, pub recommendations: Vec, pub score: f64, // 0.0 to 100.0 pub timestamp: DateTime, pub next_review_date: DateTime, } /// Individual control check result #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ControlResult { pub control_id: String, pub control_name: String, pub category: String, pub compliant: bool, pub evidence: Vec, pub gaps: Vec, pub risk_rating: RiskRating, pub remediation_timeline: Option, } /// Compliance violation details #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ComplianceViolation { pub violation_id: String, pub control_id: String, pub severity: ViolationSeverity, pub description: String, pub policy_id: Option, pub resource_id: Option, pub detected_at: DateTime, pub remediation_required: bool, pub remediation_deadline: Option>, } /// Compliance recommendation #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ComplianceRecommendation { pub recommendation_id: String, pub control_id: String, pub priority: RecommendationPriority, pub title: String, pub description: String, pub implementation_effort: EffortLevel, pub expected_impact: ImpactLevel, pub resources_required: Vec, } /// Risk rating levels #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum RiskRating { Low, Medium, High, Critical, } /// Violation severity levels #[derive(Debug, Clone, Serialize, Deserialize)] pub enum ViolationSeverity { Informational, Low, Medium, High, Critical, } /// Recommendation priority levels #[derive(Debug, Clone, Serialize, Deserialize)] pub enum RecommendationPriority { Low, Medium, High, Urgent, } /// Implementation effort levels #[derive(Debug, Clone, Serialize, Deserialize)] pub enum EffortLevel { Minimal, Low, Medium, High, Extensive, } /// Expected impact levels #[derive(Debug, Clone, Serialize, Deserialize)] pub enum ImpactLevel { Low, Medium, High, Transformational, } /// Main compliance checker pub struct ComplianceChecker { storage: Arc, frameworks: HashMap>, } /// Trait for compliance framework checkers #[async_trait::async_trait] pub trait ComplianceFrameworkChecker: Send + Sync { /// Get framework name fn framework_name(&self) -> &str; /// Check compliance for the framework async fn check_compliance( &self, policies: &[PolicyMetadata], storage: Arc, ) -> Result; /// Validate policy against framework requirements async fn validate_policy( &self, policy_content: &str, metadata: &PolicyMetadata, ) -> Result>; /// Get required controls for this framework fn get_required_controls(&self) -> Vec; /// Generate compliance report async fn generate_report(&self, result: &ComplianceResult) -> Result; } /// Compliance control definition #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ComplianceControl { pub control_id: String, pub control_name: String, pub category: String, pub description: String, pub required: bool, pub policy_patterns: Vec, // Regex patterns to match in policies pub evidence_requirements: Vec, pub test_procedures: Vec, } impl ComplianceChecker { /// Create new compliance checker pub async fn new(storage: Arc) -> Result { let mut checker = Self { storage: storage.clone(), frameworks: HashMap::new(), }; // Register compliance frameworks checker.frameworks.insert( ComplianceFramework::SOC2, Box::new(soc2::SOC2Checker::new()), ); checker.frameworks.insert( ComplianceFramework::HIPAA, Box::new(hipaa::HIPAAChecker::new()), ); Ok(checker) } /// Check compliance for specific framework pub async fn check_framework_compliance( &self, framework: &ComplianceFramework, ) -> Result { let checker = self.frameworks.get(framework).ok_or_else(|| { ControlCenterError::Policy(policy::PolicyError::Compliance(format!( "Unsupported compliance framework: {:?}", framework ))) })?; // Get all policies from storage let policies = self.storage.list_policies().await?; // Convert storage policies to compliance checker format let policies_for_check: Vec = policies .iter() .map(|p| PolicyMetadata { id: p.id.clone(), name: p.name.clone(), description: p.description.clone(), version: p.version.clone(), created_at: p.created_at, updated_at: p.updated_at, created_by: p.created_by.clone(), tags: p.tags.clone(), category: parse_category(&p.category), priority: p.priority, enabled: p.enabled, }) .collect(); // Run compliance check let result = checker .check_compliance(policies_for_check.as_slice(), self.storage.clone()) .await?; // Store result let check_result = ComplianceCheckResult { id: uuid::Uuid::new_v4().to_string(), framework: framework.to_string(), check_type: "full_assessment".to_string(), policy_id: None, compliant: result.overall_compliant, violations: result .violations .iter() .map(|v| v.description.clone()) .collect(), recommendations: result .recommendations .iter() .map(|r| r.description.clone()) .collect(), timestamp: Utc::now(), metadata: serde_json::to_value(&result)?, }; self.storage.store_compliance_check(&check_result).await?; Ok(result) } /// Check compliance for all supported frameworks pub async fn check_all_compliance( &self, ) -> Result> { let mut results = HashMap::new(); for framework in self.frameworks.keys() { match self.check_framework_compliance(framework).await { Ok(result) => { results.insert(framework.clone(), result); } Err(e) => { tracing::error!("Failed to check compliance for {:?}: {}", framework, e); } } } Ok(results) } /// Validate single policy against framework pub async fn validate_policy_compliance( &self, framework: &ComplianceFramework, policy_content: &str, metadata: &PolicyMetadata, ) -> Result> { let checker = self.frameworks.get(framework).ok_or_else(|| { ControlCenterError::Policy(policy::PolicyError::Compliance(format!( "Unsupported compliance framework: {:?}", framework ))) })?; checker.validate_policy(policy_content, metadata).await } /// Get compliance summary across all frameworks pub async fn get_compliance_summary(&self) -> Result { let all_results = self.check_all_compliance().await?; let mut total_controls = 0; let mut compliant_controls = 0; let mut total_violations = 0; let mut critical_violations = 0; for result in all_results.values() { total_controls += result.control_results.len(); compliant_controls += result .control_results .iter() .filter(|c| c.compliant) .count(); total_violations += result.violations.len(); critical_violations += result .violations .iter() .filter(|v| matches!(v.severity, ViolationSeverity::Critical)) .count(); } let overall_compliance_rate = if total_controls > 0 { (compliant_controls as f64 / total_controls as f64) * 100.0 } else { 0.0 }; Ok(ComplianceSummary { overall_compliance_rate, framework_results: all_results, total_controls, compliant_controls, total_violations, critical_violations, last_assessment: Utc::now(), next_assessment: Utc::now() + chrono::Duration::days(90), // Quarterly }) } /// Generate compliance report for framework pub async fn generate_compliance_report( &self, framework: &ComplianceFramework, format: &str, ) -> Result { let result = self.check_framework_compliance(framework).await?; let checker = self.frameworks.get(framework).ok_or_else(|| { ControlCenterError::Policy(policy::PolicyError::Compliance(format!( "Unsupported compliance framework: {:?}", framework ))) })?; match format.to_lowercase().as_str() { "json" => Ok(serde_json::to_string_pretty(&result)?), "html" | "pdf" => checker.generate_report(&result).await, _ => Err(ControlCenterError::Policy(policy::PolicyError::Compliance( format!("Unsupported report format: {}", format), ))), } } /// Monitor policy changes for compliance impact pub async fn assess_policy_change_impact( &self, old_policy: Option<&str>, new_policy: &str, metadata: &PolicyMetadata, ) -> Result { let mut impact = ComplianceImpactAssessment { policy_id: metadata.id.clone(), change_type: if old_policy.is_some() { "modification" } else { "creation" } .to_string(), framework_impacts: HashMap::new(), overall_risk_level: RiskRating::Low, requires_review: false, reviewer_notifications: Vec::new(), }; for (framework, checker) in &self.frameworks { let control_results = checker.validate_policy(new_policy, metadata).await?; let old_control_results = if let Some(old_content) = old_policy { checker.validate_policy(old_content, metadata).await.ok() } else { None }; let framework_impact = self.calculate_framework_impact(&control_results, old_control_results.as_ref()); impact .framework_impacts .insert(framework.clone(), framework_impact.clone()); // Update overall risk level if framework_impact.risk_level > impact.overall_risk_level { impact.overall_risk_level = framework_impact.risk_level.clone(); } // Determine if review is required if framework_impact.compliance_changes > 0 || matches!( framework_impact.risk_level, RiskRating::High | RiskRating::Critical ) { impact.requires_review = true; } } Ok(impact) } /// Calculate framework-specific impact fn calculate_framework_impact( &self, new_controls: &[ControlResult], old_controls: Option<&Vec>, ) -> FrameworkImpactAssessment { let (compliance_changes, new_violations, resolved_violations) = if let Some(old_results) = old_controls { self.count_control_changes(new_controls, old_results) } else { (0, 0, 0) }; let risk_level = if new_violations > 0 { RiskRating::High } else if compliance_changes > 0 { RiskRating::Medium } else { RiskRating::Low }; FrameworkImpactAssessment { compliance_changes, new_violations, resolved_violations, risk_level, affected_controls: new_controls .iter() .filter(|c| !c.compliant) .map(|c| c.control_id.clone()) .collect(), } } /// Count control compliance changes fn count_control_changes( &self, new_controls: &[ControlResult], old_controls: &[ControlResult], ) -> (usize, usize, usize) { let mut compliance_changes = 0; let mut new_violations = 0; let mut resolved_violations = 0; for new_control in new_controls { let old_control = old_controls .iter() .find(|c| c.control_id == new_control.control_id); if let Some(old) = old_control { match (old.compliant, new_control.compliant) { (true, false) => { compliance_changes += 1; new_violations += 1; } (false, true) => { compliance_changes += 1; resolved_violations += 1; } _ => {} } } } (compliance_changes, new_violations, resolved_violations) } } /// Helper function to parse category string to PolicyCategory enum fn parse_category(category: &str) -> PolicyCategory { match category { "Authentication" => PolicyCategory::Authentication, "Authorization" => PolicyCategory::Authorization, "Infrastructure" => PolicyCategory::Infrastructure, "Workflow" => PolicyCategory::Workflow, "Compliance" => PolicyCategory::Compliance, "Security" => PolicyCategory::Security, "Anomaly" => PolicyCategory::Anomaly, _ => PolicyCategory::Security, } } /// Compliance summary across all frameworks #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ComplianceSummary { pub overall_compliance_rate: f64, pub framework_results: HashMap, pub total_controls: usize, pub compliant_controls: usize, pub total_violations: usize, pub critical_violations: usize, pub last_assessment: DateTime, pub next_assessment: DateTime, } /// Policy change compliance impact assessment #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ComplianceImpactAssessment { pub policy_id: String, pub change_type: String, pub framework_impacts: HashMap, pub overall_risk_level: RiskRating, pub requires_review: bool, pub reviewer_notifications: Vec, } /// Framework-specific impact assessment #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FrameworkImpactAssessment { pub compliance_changes: usize, pub new_violations: usize, pub resolved_violations: usize, pub risk_level: RiskRating, pub affected_controls: Vec, } // Helper implementations impl std::fmt::Display for ComplianceFramework { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { ComplianceFramework::SOC2 => write!(f, "SOC2"), ComplianceFramework::HIPAA => write!(f, "HIPAA"), ComplianceFramework::GDPR => write!(f, "GDPR"), ComplianceFramework::ISO27001 => write!(f, "ISO27001"), ComplianceFramework::NIST => write!(f, "NIST"), ComplianceFramework::PciDss => write!(f, "PCI_DSS"), } } } impl PartialOrd for RiskRating { fn partial_cmp(&self, other: &Self) -> Option { let self_val = match self { RiskRating::Low => 1, RiskRating::Medium => 2, RiskRating::High => 3, RiskRating::Critical => 4, }; let other_val = match other { RiskRating::Low => 1, RiskRating::Medium => 2, RiskRating::High => 3, RiskRating::Critical => 4, }; self_val.partial_cmp(&other_val) } } impl std::fmt::Display for ViolationSeverity { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { ViolationSeverity::Informational => write!(f, "Informational"), ViolationSeverity::Low => write!(f, "Low"), ViolationSeverity::Medium => write!(f, "Medium"), ViolationSeverity::High => write!(f, "High"), ViolationSeverity::Critical => write!(f, "Critical"), } } } impl std::fmt::Display for RecommendationPriority { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { RecommendationPriority::Low => write!(f, "Low"), RecommendationPriority::Medium => write!(f, "Medium"), RecommendationPriority::High => write!(f, "High"), RecommendationPriority::Urgent => write!(f, "Urgent"), } } } impl std::fmt::Display for EffortLevel { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { EffortLevel::Minimal => write!(f, "Minimal"), EffortLevel::Low => write!(f, "Low"), EffortLevel::Medium => write!(f, "Medium"), EffortLevel::High => write!(f, "High"), EffortLevel::Extensive => write!(f, "Extensive"), } } } impl std::fmt::Display for ImpactLevel { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { ImpactLevel::Low => write!(f, "Low"), ImpactLevel::Medium => write!(f, "Medium"), ImpactLevel::High => write!(f, "High"), ImpactLevel::Transformational => write!(f, "Transformational"), } } }