#![allow(dead_code)]
use std::collections::HashMap;
use std::fmt;
use std::path::PathBuf;
use std::time::{Duration, SystemTime};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ComplianceStandard {
Gdpr,
Hipaa,
BroadcastContent,
Sox,
PciDss,
Custom,
}
impl fmt::Display for ComplianceStandard {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Gdpr => write!(f, "GDPR"),
Self::Hipaa => write!(f, "HIPAA"),
Self::BroadcastContent => write!(f, "Broadcast Content"),
Self::Sox => write!(f, "SOX"),
Self::PciDss => write!(f, "PCI-DSS"),
Self::Custom => write!(f, "Custom"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Severity {
Info,
Warning,
Minor,
Major,
Critical,
}
impl fmt::Display for Severity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Info => write!(f, "INFO"),
Self::Warning => write!(f, "WARNING"),
Self::Minor => write!(f, "MINOR"),
Self::Major => write!(f, "MAJOR"),
Self::Critical => write!(f, "CRITICAL"),
}
}
}
#[derive(Debug, Clone)]
pub struct ComplianceViolation {
pub standard: ComplianceStandard,
pub severity: Severity,
pub asset_path: Option<PathBuf>,
pub description: String,
pub remediation: String,
pub rule_id: String,
}
impl fmt::Display for ComplianceViolation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"[{}] {} ({}): {}",
self.severity, self.standard, self.rule_id, self.description
)
}
}
#[derive(Debug, Clone)]
pub struct RetentionPolicy {
pub name: String,
pub min_retention: Duration,
pub max_retention: Option<Duration>,
pub standard: ComplianceStandard,
pub legal_hold_override: bool,
}
impl RetentionPolicy {
pub fn new(name: &str, standard: ComplianceStandard, min_retention: Duration) -> Self {
Self {
name: name.to_string(),
min_retention,
max_retention: None,
standard,
legal_hold_override: true,
}
}
pub fn with_max_retention(mut self, max: Duration) -> Self {
self.max_retention = Some(max);
self
}
pub fn with_legal_hold_override(mut self, allow: bool) -> Self {
self.legal_hold_override = allow;
self
}
pub fn check_retention(&self, created_at: SystemTime) -> RetentionStatus {
let age = created_at.elapsed().unwrap_or(Duration::ZERO);
if age < self.min_retention {
RetentionStatus::WithinRetention
} else if let Some(max) = self.max_retention {
if age > max {
RetentionStatus::ExceededMaxRetention
} else {
RetentionStatus::WithinRetention
}
} else {
RetentionStatus::WithinRetention
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RetentionStatus {
WithinRetention,
ExceededMaxRetention,
LegalHold,
}
impl fmt::Display for RetentionStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::WithinRetention => write!(f, "Within Retention"),
Self::ExceededMaxRetention => write!(f, "Exceeded Max Retention"),
Self::LegalHold => write!(f, "Legal Hold"),
}
}
}
#[derive(Debug, Clone)]
pub struct ComplianceAsset {
pub path: PathBuf,
pub archived_at: SystemTime,
pub tags: Vec<String>,
pub legal_hold: bool,
pub size_bytes: u64,
pub encrypted: bool,
pub access_logged: bool,
}
impl ComplianceAsset {
pub fn new(path: PathBuf, archived_at: SystemTime, size_bytes: u64) -> Self {
Self {
path,
archived_at,
tags: Vec::new(),
legal_hold: false,
size_bytes,
encrypted: false,
access_logged: false,
}
}
pub fn with_tag(mut self, tag: &str) -> Self {
self.tags.push(tag.to_string());
self
}
pub fn with_legal_hold(mut self, hold: bool) -> Self {
self.legal_hold = hold;
self
}
pub fn with_encrypted(mut self, encrypted: bool) -> Self {
self.encrypted = encrypted;
self
}
pub fn with_access_logged(mut self, logged: bool) -> Self {
self.access_logged = logged;
self
}
}
#[derive(Debug, Clone)]
pub struct ComplianceReport {
pub checked_at: SystemTime,
pub violations: Vec<ComplianceViolation>,
pub assets_checked: u64,
pub standards_evaluated: Vec<ComplianceStandard>,
}
impl ComplianceReport {
pub fn new() -> Self {
Self {
checked_at: SystemTime::now(),
violations: Vec::new(),
assets_checked: 0,
standards_evaluated: Vec::new(),
}
}
pub fn violation_count(&self) -> usize {
self.violations.len()
}
pub fn violations_by_severity(&self, severity: Severity) -> Vec<&ComplianceViolation> {
self.violations
.iter()
.filter(|v| v.severity == severity)
.collect()
}
pub fn violations_by_standard(
&self,
standard: ComplianceStandard,
) -> Vec<&ComplianceViolation> {
self.violations
.iter()
.filter(|v| v.standard == standard)
.collect()
}
pub fn highest_severity(&self) -> Option<Severity> {
self.violations.iter().map(|v| v.severity).max()
}
pub fn is_compliant(&self) -> bool {
!self
.violations
.iter()
.any(|v| v.severity == Severity::Critical || v.severity == Severity::Major)
}
pub fn severity_summary(&self) -> HashMap<String, usize> {
let mut summary = HashMap::new();
for v in &self.violations {
*summary.entry(v.severity.to_string()).or_insert(0) += 1;
}
summary
}
}
impl Default for ComplianceReport {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct ComplianceChecker {
retention_policies: Vec<RetentionPolicy>,
require_hipaa_encryption: bool,
require_access_logging: bool,
}
impl ComplianceChecker {
pub fn new() -> Self {
Self {
retention_policies: Vec::new(),
require_hipaa_encryption: true,
require_access_logging: true,
}
}
pub fn add_retention_policy(&mut self, policy: RetentionPolicy) {
self.retention_policies.push(policy);
}
pub fn policy_count(&self) -> usize {
self.retention_policies.len()
}
pub fn set_require_hipaa_encryption(&mut self, require: bool) {
self.require_hipaa_encryption = require;
}
pub fn check_asset(
&self,
asset: &ComplianceAsset,
standards: &[ComplianceStandard],
) -> Vec<ComplianceViolation> {
let mut violations = Vec::new();
for standard in standards {
match standard {
ComplianceStandard::Gdpr => {
self.check_gdpr(asset, &mut violations);
}
ComplianceStandard::Hipaa => {
self.check_hipaa(asset, &mut violations);
}
ComplianceStandard::BroadcastContent => {
self.check_broadcast(asset, &mut violations);
}
ComplianceStandard::Sox => {
self.check_sox(asset, &mut violations);
}
ComplianceStandard::PciDss => {
self.check_pci_dss(asset, &mut violations);
}
ComplianceStandard::Custom => {}
}
}
violations
}
pub fn check_batch(
&self,
assets: &[ComplianceAsset],
standards: &[ComplianceStandard],
) -> ComplianceReport {
let mut report = ComplianceReport::new();
report.standards_evaluated = standards.to_vec();
report.assets_checked = assets.len() as u64;
for asset in assets {
let violations = self.check_asset(asset, standards);
report.violations.extend(violations);
}
report
}
fn check_gdpr(&self, asset: &ComplianceAsset, violations: &mut Vec<ComplianceViolation>) {
for policy in &self.retention_policies {
if policy.standard == ComplianceStandard::Gdpr {
let status = policy.check_retention(asset.archived_at);
if status == RetentionStatus::ExceededMaxRetention && !asset.legal_hold {
violations.push(ComplianceViolation {
standard: ComplianceStandard::Gdpr,
severity: Severity::Major,
asset_path: Some(asset.path.clone()),
description: format!(
"Asset exceeds GDPR max retention for policy '{}'",
policy.name
),
remediation: "Delete or anonymize the asset".to_string(),
rule_id: "GDPR-RET-001".to_string(),
});
}
}
}
if asset.tags.iter().any(|t| t == "personal_data") && !asset.access_logged {
violations.push(ComplianceViolation {
standard: ComplianceStandard::Gdpr,
severity: Severity::Major,
asset_path: Some(asset.path.clone()),
description: "Personal data asset lacks access logging".to_string(),
remediation: "Enable access logging for this asset".to_string(),
rule_id: "GDPR-LOG-001".to_string(),
});
}
}
fn check_hipaa(&self, asset: &ComplianceAsset, violations: &mut Vec<ComplianceViolation>) {
if self.require_hipaa_encryption && !asset.encrypted {
violations.push(ComplianceViolation {
standard: ComplianceStandard::Hipaa,
severity: Severity::Critical,
asset_path: Some(asset.path.clone()),
description: "HIPAA-regulated asset is not encrypted at rest".to_string(),
remediation: "Encrypt the asset using AES-256".to_string(),
rule_id: "HIPAA-ENC-001".to_string(),
});
}
if !asset.access_logged {
violations.push(ComplianceViolation {
standard: ComplianceStandard::Hipaa,
severity: Severity::Major,
asset_path: Some(asset.path.clone()),
description: "HIPAA asset lacks access audit logging".to_string(),
remediation: "Enable access logging".to_string(),
rule_id: "HIPAA-AUD-001".to_string(),
});
}
}
fn check_broadcast(&self, asset: &ComplianceAsset, violations: &mut Vec<ComplianceViolation>) {
if asset.tags.iter().any(|t| t == "unrated_content") {
violations.push(ComplianceViolation {
standard: ComplianceStandard::BroadcastContent,
severity: Severity::Warning,
asset_path: Some(asset.path.clone()),
description: "Broadcast asset has no content rating".to_string(),
remediation: "Assign a content rating before distribution".to_string(),
rule_id: "BCAST-RATE-001".to_string(),
});
}
}
fn check_sox(&self, asset: &ComplianceAsset, violations: &mut Vec<ComplianceViolation>) {
for policy in &self.retention_policies {
if policy.standard == ComplianceStandard::Sox {
let status = policy.check_retention(asset.archived_at);
if status == RetentionStatus::ExceededMaxRetention {
violations.push(ComplianceViolation {
standard: ComplianceStandard::Sox,
severity: Severity::Major,
asset_path: Some(asset.path.clone()),
description: format!(
"Asset exceeds SOX retention for policy '{}'",
policy.name
),
remediation: "Review and archive or delete".to_string(),
rule_id: "SOX-RET-001".to_string(),
});
}
}
}
}
fn check_pci_dss(&self, asset: &ComplianceAsset, violations: &mut Vec<ComplianceViolation>) {
if asset.tags.iter().any(|t| t == "payment_data") && !asset.encrypted {
violations.push(ComplianceViolation {
standard: ComplianceStandard::PciDss,
severity: Severity::Critical,
asset_path: Some(asset.path.clone()),
description: "Payment data asset is not encrypted".to_string(),
remediation: "Encrypt immediately per PCI-DSS requirement 3".to_string(),
rule_id: "PCI-ENC-001".to_string(),
});
}
}
}
impl Default for ComplianceChecker {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_asset(path: &str) -> ComplianceAsset {
ComplianceAsset::new(PathBuf::from(path), SystemTime::now(), 1024)
}
#[test]
fn test_compliance_standard_display() {
assert_eq!(ComplianceStandard::Gdpr.to_string(), "GDPR");
assert_eq!(ComplianceStandard::Hipaa.to_string(), "HIPAA");
assert_eq!(
ComplianceStandard::BroadcastContent.to_string(),
"Broadcast Content"
);
assert_eq!(ComplianceStandard::Sox.to_string(), "SOX");
assert_eq!(ComplianceStandard::PciDss.to_string(), "PCI-DSS");
}
#[test]
fn test_severity_ordering() {
assert!(Severity::Info < Severity::Warning);
assert!(Severity::Warning < Severity::Minor);
assert!(Severity::Minor < Severity::Major);
assert!(Severity::Major < Severity::Critical);
}
#[test]
fn test_retention_policy_within_bounds() {
let policy =
RetentionPolicy::new("test", ComplianceStandard::Gdpr, Duration::from_hours(24));
let status = policy.check_retention(SystemTime::now());
assert_eq!(status, RetentionStatus::WithinRetention);
}
#[test]
fn test_retention_policy_with_max() {
let policy = RetentionPolicy::new("test", ComplianceStandard::Gdpr, Duration::from_secs(0))
.with_max_retention(Duration::from_secs(1));
let status = policy.check_retention(SystemTime::now());
assert_eq!(status, RetentionStatus::WithinRetention);
}
#[test]
fn test_retention_status_display() {
assert_eq!(
RetentionStatus::WithinRetention.to_string(),
"Within Retention"
);
assert_eq!(
RetentionStatus::ExceededMaxRetention.to_string(),
"Exceeded Max Retention"
);
assert_eq!(RetentionStatus::LegalHold.to_string(), "Legal Hold");
}
#[test]
fn test_compliance_asset_builder() {
let asset = ComplianceAsset::new(PathBuf::from("/test"), SystemTime::now(), 500)
.with_tag("personal_data")
.with_legal_hold(true)
.with_encrypted(true)
.with_access_logged(true);
assert_eq!(asset.tags.len(), 1);
assert!(asset.legal_hold);
assert!(asset.encrypted);
assert!(asset.access_logged);
}
#[test]
fn test_hipaa_encryption_violation() {
let checker = ComplianceChecker::new();
let asset = make_asset("/hipaa/record.dat");
let violations = checker.check_asset(&asset, &[ComplianceStandard::Hipaa]);
assert!(violations.iter().any(|v| v.rule_id == "HIPAA-ENC-001"));
assert!(violations.iter().any(|v| v.rule_id == "HIPAA-AUD-001"));
}
#[test]
fn test_hipaa_compliant_asset() {
let checker = ComplianceChecker::new();
let asset = make_asset("/hipaa/ok.dat")
.with_encrypted(true)
.with_access_logged(true);
let violations = checker.check_asset(&asset, &[ComplianceStandard::Hipaa]);
assert!(violations.is_empty());
}
#[test]
fn test_broadcast_unrated_warning() {
let checker = ComplianceChecker::new();
let asset = make_asset("/broadcast/clip.mxf").with_tag("unrated_content");
let violations = checker.check_asset(&asset, &[ComplianceStandard::BroadcastContent]);
assert_eq!(violations.len(), 1);
assert_eq!(violations[0].severity, Severity::Warning);
}
#[test]
fn test_pci_dss_unencrypted_payment() {
let checker = ComplianceChecker::new();
let asset = make_asset("/payment/data.bin").with_tag("payment_data");
let violations = checker.check_asset(&asset, &[ComplianceStandard::PciDss]);
assert_eq!(violations.len(), 1);
assert_eq!(violations[0].severity, Severity::Critical);
}
#[test]
fn test_compliance_report_is_compliant() {
let checker = ComplianceChecker::new();
let asset = make_asset("/clean.dat")
.with_encrypted(true)
.with_access_logged(true);
let report = checker.check_batch(
&[asset],
&[
ComplianceStandard::Hipaa,
ComplianceStandard::BroadcastContent,
],
);
assert!(report.is_compliant());
assert_eq!(report.assets_checked, 1);
}
#[test]
fn test_compliance_report_severity_summary() {
let checker = ComplianceChecker::new();
let asset = make_asset("/test.dat").with_tag("unrated_content");
let report = checker.check_batch(
&[asset],
&[
ComplianceStandard::Hipaa,
ComplianceStandard::BroadcastContent,
],
);
let summary = report.severity_summary();
assert!(summary.contains_key("CRITICAL"));
assert!(summary.contains_key("WARNING"));
}
#[test]
fn test_gdpr_personal_data_no_logging() {
let checker = ComplianceChecker::new();
let asset = make_asset("/gdpr/user.dat").with_tag("personal_data");
let violations = checker.check_asset(&asset, &[ComplianceStandard::Gdpr]);
assert!(violations.iter().any(|v| v.rule_id == "GDPR-LOG-001"));
}
#[test]
fn test_violation_display() {
let violation = ComplianceViolation {
standard: ComplianceStandard::Hipaa,
severity: Severity::Critical,
asset_path: Some(PathBuf::from("/test")),
description: "Not encrypted".to_string(),
remediation: "Encrypt it".to_string(),
rule_id: "HIPAA-ENC-001".to_string(),
};
let display = format!("{}", violation);
assert!(display.contains("CRITICAL"));
assert!(display.contains("HIPAA"));
assert!(display.contains("HIPAA-ENC-001"));
}
}