pub mod checker;
pub mod engine;
pub mod framework;
pub mod loader;
pub mod reporter;
pub mod rule;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
pub use checker::ComplianceChecker;
pub use engine::ComplianceEngine;
pub use framework::{ComplianceFramework, Requirement};
pub use loader::FrameworkLoader;
pub use reporter::Reporter;
pub use rule::{Rule, RuleType};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ComplianceStatus {
Pass,
Fail,
Warning,
}
impl std::fmt::Display for ComplianceStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ComplianceStatus::Pass => write!(f, "PASS"),
ComplianceStatus::Fail => write!(f, "FAIL"),
ComplianceStatus::Warning => write!(f, "WARNING"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum RequirementStatus {
Pass,
Fail,
Warning,
NotApplicable,
}
impl std::fmt::Display for RequirementStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
RequirementStatus::Pass => write!(f, "PASS"),
RequirementStatus::Fail => write!(f, "FAIL"),
RequirementStatus::Warning => write!(f, "WARNING"),
RequirementStatus::NotApplicable => write!(f, "N/A"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Severity {
Info,
Low,
Medium,
High,
Critical,
}
impl std::fmt::Display for Severity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Severity::Info => write!(f, "INFO"),
Severity::Low => write!(f, "LOW"),
Severity::Medium => write!(f, "MEDIUM"),
Severity::High => write!(f, "HIGH"),
Severity::Critical => write!(f, "CRITICAL"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Violation {
pub violation_type: String,
pub description: String,
pub evidence: String,
pub severity: Severity,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RequirementResult {
pub requirement_id: String,
pub name: String,
pub description: String,
pub category: String,
pub severity: Severity,
pub status: RequirementStatus,
pub violations: Vec<Violation>,
pub remediation: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplianceSummary {
pub total: usize,
pub passed: usize,
pub failed: usize,
pub warnings: usize,
pub not_applicable: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplianceReport {
pub framework: ComplianceFramework,
pub target: String,
pub scan_timestamp: DateTime<Utc>,
pub overall_status: ComplianceStatus,
pub requirements: Vec<RequirementResult>,
pub summary: ComplianceSummary,
}
impl ComplianceReport {
pub fn new(framework: &ComplianceFramework, target: String) -> Self {
Self {
framework: framework.clone(),
target,
scan_timestamp: Utc::now(),
overall_status: ComplianceStatus::Pass,
requirements: Vec::new(),
summary: ComplianceSummary {
total: 0,
passed: 0,
failed: 0,
warnings: 0,
not_applicable: 0,
},
}
}
pub fn add_requirement_result(&mut self, result: RequirementResult) {
match result.status {
RequirementStatus::Pass => self.summary.passed += 1,
RequirementStatus::Fail => self.summary.failed += 1,
RequirementStatus::Warning => self.summary.warnings += 1,
RequirementStatus::NotApplicable => self.summary.not_applicable += 1,
}
self.requirements.push(result);
}
pub fn finalize(&mut self) {
self.summary.total = self.requirements.len();
self.overall_status = if self.summary.failed > 0 {
ComplianceStatus::Fail
} else if self.summary.warnings > 0 {
ComplianceStatus::Warning
} else {
ComplianceStatus::Pass
};
}
pub fn failed_requirements(&self) -> Vec<&RequirementResult> {
self.requirements
.iter()
.filter(|r| r.status == RequirementStatus::Fail)
.collect()
}
pub fn warning_requirements(&self) -> Vec<&RequirementResult> {
self.requirements
.iter()
.filter(|r| r.status == RequirementStatus::Warning)
.collect()
}
pub fn requirements_by_severity(&self, severity: Severity) -> Vec<&RequirementResult> {
self.requirements
.iter()
.filter(|r| r.severity == severity)
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_compliance_status_display() {
assert_eq!(ComplianceStatus::Pass.to_string(), "PASS");
assert_eq!(ComplianceStatus::Fail.to_string(), "FAIL");
assert_eq!(ComplianceStatus::Warning.to_string(), "WARNING");
}
#[test]
fn test_severity_ordering() {
assert!(Severity::Critical > Severity::High);
assert!(Severity::High > Severity::Medium);
assert!(Severity::Medium > Severity::Low);
assert!(Severity::Low > Severity::Info);
}
#[test]
fn test_compliance_report_summary() {
let framework = ComplianceFramework {
id: "test".to_string(),
name: "Test Framework".to_string(),
version: "1.0".to_string(),
description: "Test".to_string(),
organization: "Test Org".to_string(),
effective_date: None,
requirements: vec![],
};
let mut report = ComplianceReport::new(&framework, "test.com:443".to_string());
report.add_requirement_result(RequirementResult {
requirement_id: "TEST-1".to_string(),
name: "Test Req".to_string(),
description: "".to_string(),
category: "Test".to_string(),
severity: Severity::High,
status: RequirementStatus::Pass,
violations: vec![],
remediation: "".to_string(),
});
report.add_requirement_result(RequirementResult {
requirement_id: "TEST-2".to_string(),
name: "Test Req 2".to_string(),
description: "".to_string(),
category: "Test".to_string(),
severity: Severity::Critical,
status: RequirementStatus::Fail,
violations: vec![],
remediation: "".to_string(),
});
report.finalize();
assert_eq!(report.summary.total, 2);
assert_eq!(report.summary.passed, 1);
assert_eq!(report.summary.failed, 1);
assert_eq!(report.overall_status, ComplianceStatus::Fail);
}
}