use serde::{Serialize, Deserialize};
use log::debug;
use std::env;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RedactionMatch {
pub rule_name: String,
pub original_string: String,
pub sanitized_string: String,
}
pub fn redact_sensitive(s: &str) -> String {
const MAX_LEN: usize = 8;
if s.len() <= MAX_LEN {
"[REDACTED]".to_string()
} else {
format!("[REDACTED: {} chars]", s.len())
}
}
fn is_pii_debug_allowed() -> bool {
env::var("CLEANSH_ALLOW_DEBUG_PII")
.map(|s| s.eq_ignore_ascii_case("true"))
.unwrap_or(false)
}
pub fn log_redaction_match_debug(
module_path: &str,
rule_name: &str,
original_sensitive_content: &str,
sanitized_content: &str,
) {
let content_to_log: &str = if is_pii_debug_allowed() {
original_sensitive_content
} else {
&*redact_sensitive(original_sensitive_content)
};
debug!("{} Found RedactionMatch: Rule='{}', Original='{}', Sanitized='{}'",
module_path,
rule_name,
content_to_log,
sanitized_content
);
}
pub fn log_captured_match_debug(
module_path: &str,
rule_name: &str,
original_sensitive_content: &str,
) {
let content_to_log: &str = if is_pii_debug_allowed() {
original_sensitive_content
} else {
&*redact_sensitive(original_sensitive_content)
};
debug!("{} Captured match (original): '{}' for rule '{}'", module_path, content_to_log, rule_name);
}
pub fn log_redaction_action_debug(
module_path: &str,
original_sensitive_content: &str,
sanitized_replacement: &str,
rule_name: &str,
) {
let original_for_log: &str = if is_pii_debug_allowed() {
original_sensitive_content
} else {
&*redact_sensitive(original_sensitive_content)
};
debug!(
"{} Redaction action: Original='{}', Redacted='{}' for rule '{}'",
module_path,
original_for_log,
sanitized_replacement,
rule_name
);
}
#[cfg(test)]
mod tests {
use super::*;
use std::env;
use test_log::test;
#[test]
fn test_redact_sensitive_short_string() {
assert_eq!(redact_sensitive("abc"), "[REDACTED]".to_string());
assert_eq!(redact_sensitive("12345678"), "[REDACTED]".to_string());
}
#[test]
fn test_redact_sensitive_long_string() {
assert_eq!(redact_sensitive("123456789"), "[REDACTED: 9 chars]".to_string());
assert_eq!(redact_sensitive("long_sensitive_data"), "[REDACTED: 19 chars]".to_string());
}
#[test]
#[test_log::test]
fn test_log_redaction_match_debug_pii_allowed() {
unsafe { env::set_var("CLEANSH_ALLOW_DEBUG_PII", "true"); }
log_redaction_match_debug(
"[test::redaction]", "email", "test@example.com", "[EMAIL_REDACTED]"
);
unsafe { env::remove_var("CLEANSH_ALLOW_DEBUG_PII"); }
}
#[test]
#[test_log::test]
fn test_log_redaction_match_debug_pii_not_allowed() {
unsafe { env::remove_var("CLEANSH_ALLOW_DEBUG_PII"); } log_redaction_match_debug(
"[test::redaction]", "email", "test@example.com", "[EMAIL_REDACTED]"
);
}
#[test]
#[test_log::test]
fn test_log_captured_match_debug_pii_allowed() {
unsafe { env::set_var("CLEANSH_ALLOW_DEBUG_PII", "true"); }
log_captured_match_debug("[test::redaction]", "ssn", "123-45-6789");
unsafe { env::remove_var("CLEANSH_ALLOW_DEBUG_PII"); }
}
#[test]
#[test_log::test]
fn test_log_captured_match_debug_pii_not_allowed() {
unsafe { env::remove_var("CLEANSH_ALLOW_DEBUG_PII"); } log_captured_match_debug("[test::redaction]", "ssn", "123-45-6789");
}
#[test]
#[test_log::test]
fn test_log_redaction_action_debug_pii_allowed() {
unsafe { env::set_var("CLEANSH_ALLOW_DEBUG_PII", "true"); }
log_redaction_action_debug("[test::redaction]", "original_token", "REDACTED_TOKEN", "generic_token");
unsafe { env::remove_var("CLEANSH_ALLOW_DEBUG_PII"); }
}
#[test]
#[test_log::test]
fn test_log_redaction_action_debug_pii_not_allowed() {
unsafe { env::remove_var("CLEANSH_ALLOW_DEBUG_PII"); } log_redaction_action_debug("[test::redaction]", "original_token", "REDACTED_TOKEN", "generic_token");
}
}