pub mod ccpa;
pub mod consent;
pub mod gdpr;
pub mod google_consent;
pub mod iab_tcf;
pub mod regulations;
use crate::types::{ComplianceIssue, ComplianceResult, Cookie, Regulation, Severity};
use chrono::Utc;
pub struct ComplianceChecker {
regulations: Vec<Regulation>,
strict: bool,
}
impl ComplianceChecker {
#[must_use]
pub fn new() -> Self {
Self {
regulations: vec![Regulation::GDPR, Regulation::CCPA],
strict: false,
}
}
#[must_use]
pub fn with_regulations(regulations: Vec<Regulation>) -> Self {
Self {
regulations,
strict: false,
}
}
#[must_use]
pub fn strict(mut self) -> Self {
self.strict = true;
self
}
#[must_use]
pub fn check(&self, cookie: &Cookie, regulation: Regulation) -> ComplianceResult {
match regulation {
Regulation::GDPR => gdpr::check_gdpr_compliance(cookie),
Regulation::CCPA => ccpa::check_ccpa_compliance(cookie),
Regulation::LGPD => regulations::check_lgpd_compliance(cookie),
Regulation::POPIA => regulations::check_popia_compliance(cookie),
Regulation::PIPEDA => regulations::check_pipeda_compliance(cookie),
Regulation::PDPA => regulations::check_pdpa_compliance(cookie),
Regulation::EPrivacy => regulations::check_eprivacy_compliance(cookie),
Regulation::UKGDPR => regulations::check_ukgdpr_compliance(cookie),
Regulation::CPRA => regulations::check_cpra_compliance(cookie),
Regulation::PrivacyAct => regulations::check_privacy_act_compliance(cookie),
Regulation::APPI => regulations::check_appi_compliance(cookie),
Regulation::COPPA => regulations::check_coppa_compliance(cookie),
Regulation::HIPAA => regulations::check_hipaa_compliance(cookie),
Regulation::VCDPA => regulations::check_vcdpa_compliance(cookie),
Regulation::CPA => regulations::check_cpa_compliance(cookie),
Regulation::CTDPA => regulations::check_ctdpa_compliance(cookie),
Regulation::UCPA => regulations::check_ucpa_compliance(cookie),
Regulation::PCIDSS
| Regulation::PIPL
| Regulation::PIPA
| Regulation::THPDPA
| Regulation::IDPDP
| Regulation::PHDPA
| Regulation::NZPA
| Regulation::FADP => generic_compliance_check(cookie, regulation),
}
}
#[must_use]
pub fn check_batch(&self, cookies: &[Cookie]) -> Vec<ComplianceResult> {
let mut results = Vec::new();
for regulation in &self.regulations {
let mut result = ComplianceResult {
id: uuid::Uuid::new_v4().to_string(),
regulation: *regulation,
checked_at: Utc::now(),
compliant: true,
score: 100.0,
issues: Vec::new(),
compliant_cookies: 0,
non_compliant_cookies: 0,
warnings: Vec::new(),
recommendations: Vec::new(),
consent_analysis: None,
};
for cookie in cookies {
let cookie_result = self.check(cookie, *regulation);
if cookie_result.compliant {
result.compliant_cookies += 1;
} else {
result.non_compliant_cookies += 1;
result.compliant = false;
}
result.issues.extend(cookie_result.issues);
result.warnings.extend(cookie_result.warnings);
result.recommendations.extend(cookie_result.recommendations);
}
if !cookies.is_empty() {
#[allow(clippy::cast_precision_loss)]
let total = cookies.len() as f32;
#[allow(clippy::cast_precision_loss)]
let compliant_ratio = result.compliant_cookies as f32 / total;
result.score = compliant_ratio * 100.0;
}
results.push(result);
}
results
}
#[must_use]
pub fn analyze_consent(&self, cookies: &[Cookie]) -> consent::ConsentAnalysis {
consent::analyze_consent_mechanism(cookies)
}
#[must_use]
pub fn generate_recommendations(&self, issues: &[ComplianceIssue]) -> Vec<String> {
let mut recommendations = Vec::new();
for issue in issues {
match issue.issue.severity {
Severity::Critical | Severity::High => {
recommendations.push(format!(
"URGENT: Fix {} violation: {}",
issue.regulation, issue.issue.description
));
}
Severity::Medium => {
recommendations.push(format!(
"Recommended: Address {} issue: {}",
issue.regulation, issue.issue.description
));
}
_ => {}
}
}
recommendations
}
}
fn generic_compliance_check(cookie: &Cookie, regulation: Regulation) -> ComplianceResult {
let mut result = ComplianceResult {
id: uuid::Uuid::new_v4().to_string(),
regulation,
checked_at: Utc::now(),
compliant: true,
score: 100.0,
issues: Vec::new(),
compliant_cookies: 0,
non_compliant_cookies: 0,
warnings: Vec::new(),
recommendations: Vec::new(),
consent_analysis: None,
};
if !cookie.secure {
result.warnings.push(format!(
"Cookie lacks Secure flag - may not meet {regulation} security requirements"
));
}
if !cookie.http_only && is_session_like(cookie) {
result.warnings.push(format!(
"Session cookie lacks HttpOnly flag - may not meet {regulation} security requirements"
));
}
result.recommendations.push(format!(
"Verify cookie compliance with {regulation} requirements"
));
result
}
fn is_session_like(cookie: &Cookie) -> bool {
let name_lower = cookie.name.to_lowercase();
name_lower.contains("session") || name_lower.contains("auth") || name_lower.contains("token")
}
impl Default for ComplianceChecker {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_compliance_checker_creation() {
let checker = ComplianceChecker::new();
assert_eq!(checker.regulations.len(), 2);
assert!(!checker.strict);
}
#[test]
fn test_compliance_checker_with_regulations() {
let regulations = vec![Regulation::GDPR, Regulation::CCPA, Regulation::LGPD];
let checker = ComplianceChecker::with_regulations(regulations);
assert_eq!(checker.regulations.len(), 3);
}
#[test]
fn test_compliance_checker_strict() {
let checker = ComplianceChecker::new().strict();
assert!(checker.strict);
}
#[test]
fn test_check_single_cookie() {
let checker = ComplianceChecker::new();
let cookie = Cookie::new("test".to_string(), "value".to_string());
let result = checker.check(&cookie, Regulation::GDPR);
assert!(!result.id.is_empty());
assert_eq!(result.regulation, Regulation::GDPR);
}
#[test]
fn test_check_batch() {
let checker = ComplianceChecker::new();
let cookies = vec![
Cookie::new("test1".to_string(), "value1".to_string()),
Cookie::new("test2".to_string(), "value2".to_string()),
];
let results = checker.check_batch(&cookies);
assert!(!results.is_empty());
assert_eq!(results.len(), 2); }
#[test]
fn test_generic_compliance_check() {
let cookie = Cookie::new("test".to_string(), "value".to_string());
let result = generic_compliance_check(&cookie, Regulation::PIPL);
assert!(result.compliant);
assert!(!result.warnings.is_empty());
}
}