use crate::event::{EventValue, SecurityEvent};
use security_core::classification::DataClassification;
use sha2::{Digest, Sha256};
use std::collections::BTreeMap;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum RedactionStrategy {
Allow,
Redact,
Hash,
Drop,
Pseudonymize,
}
#[derive(Clone, Debug)]
pub struct RedactionPolicy {
pub public: RedactionStrategy,
pub internal: RedactionStrategy,
pub confidential: RedactionStrategy,
pub pii: RedactionStrategy,
pub regulated: RedactionStrategy,
pub secret: RedactionStrategy,
pub credentials: RedactionStrategy,
}
impl Default for RedactionPolicy {
fn default() -> Self {
Self {
public: RedactionStrategy::Allow,
internal: RedactionStrategy::Allow,
confidential: RedactionStrategy::Redact,
pii: RedactionStrategy::Hash,
regulated: RedactionStrategy::Hash,
secret: RedactionStrategy::Redact,
credentials: RedactionStrategy::Drop,
}
}
}
impl RedactionPolicy {
#[must_use]
pub fn strategy_for(&self, classification: DataClassification) -> RedactionStrategy {
match classification {
DataClassification::Public => self.public,
DataClassification::Internal => self.internal,
DataClassification::Confidential => self.confidential,
DataClassification::PII => self.pii,
DataClassification::Regulated => self.regulated,
DataClassification::Secret => self.secret,
DataClassification::Credentials => self.credentials,
_ => RedactionStrategy::Redact,
}
}
}
#[derive(Clone, Debug)]
pub struct RedactionEngine {
pub policy: RedactionPolicy,
}
impl RedactionEngine {
#[must_use]
pub fn new(policy: RedactionPolicy) -> Self {
Self { policy }
}
#[must_use]
pub fn with_default_policy() -> Self {
Self {
policy: RedactionPolicy::default(),
}
}
#[must_use]
pub fn process_event(&self, mut event: SecurityEvent) -> SecurityEvent {
let mut new_labels = BTreeMap::new();
for (key, value) in event.labels {
match value {
EventValue::Classified {
value: v,
classification,
} => {
let strategy = self.policy.strategy_for(classification);
match strategy {
RedactionStrategy::Drop => {}
RedactionStrategy::Redact => {
new_labels.insert(
key,
EventValue::Classified {
value: "[REDACTED]".to_string(),
classification,
},
);
}
RedactionStrategy::Hash => {
let mut hasher = Sha256::new();
hasher.update(v.as_bytes());
let hash = hex::encode(hasher.finalize());
new_labels.insert(
key,
EventValue::Classified {
value: format!("SHA256:{hash}"),
classification,
},
);
}
RedactionStrategy::Allow | RedactionStrategy::Pseudonymize => {
new_labels.insert(
key,
EventValue::Classified {
value: v,
classification,
},
);
}
}
}
}
}
event.labels = new_labels;
event
}
}