use regex::{Regex, RegexBuilder};
pub const EMAIL_PATTERN: &str = r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}";
pub const PHONE_PATTERN: &str =
r"\b(?:\+[0-9]{1,3}[-. \t]?)?\(?[0-9]{2,4}\)?[-. \t]?[0-9]{3,4}[-. \t]?[0-9]{3,4}\b";
pub const IPV4_PATTERN: &str = r"\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b";
pub const IPV6_PATTERN: &str = r"\b(?:[a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4}\b";
pub const UUID_PATTERN: &str =
r"\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\b";
pub const ID_PATTERN: &str =
r#"(?i)\b(user_?id|account_?id|client_?id|id)["']?[ \t]*[:=][ \t]*["']?([a-zA-Z0-9_\-]+)["']?"#;
pub const APIKEY_PATTERN: &str = r#"(?i)\b(api[_-]?key|secret[_-]?key|access[_-]?key)["']?[ \t]*[:=][ \t]*["']?([a-zA-Z0-9_\-\.]{16,})["']?"#;
pub const JWT_PATTERN: &str = r"\beyJ[a-zA-Z0-9_-]*\.[a-zA-Z0-9_-]*\.[a-zA-Z0-9_-]*\b";
pub const BEARER_PATTERN: &str = r"(?i)(Bearer)[ \t]+([a-zA-Z0-9_\-\.=]+)";
pub struct MaskRule {
pub pattern: Regex,
pub replacement: &'static str,
}
impl MaskRule {
fn new(pattern_str: &str, replacement: &'static str) -> Self {
let pattern = RegexBuilder::new(pattern_str)
.size_limit(10 * 1024 * 1024)
.unicode(false)
.build()
.unwrap_or_else(|e| panic!("Failed to compile regex '{}': {}", pattern_str, e));
Self {
pattern,
replacement,
}
}
pub fn rules() -> Vec<MaskRule> {
vec![
Self::new(UUID_PATTERN, "[REDACTED_UUID]"),
Self::new(JWT_PATTERN, "[REDACTED_TOKEN]"),
Self::new(EMAIL_PATTERN, "[REDACTED_EMAIL]"),
Self::new(IPV6_PATTERN, "[REDACTED_IPV6]"),
Self::new(IPV4_PATTERN, "[REDACTED_IPV4]"),
Self::new(APIKEY_PATTERN, "$1=[REDACTED_KEY]"),
Self::new(ID_PATTERN, "$1=[REDACTED_ID]"),
Self::new(BEARER_PATTERN, "$1 [REDACTED_TOKEN]"),
Self::new(PHONE_PATTERN, "[REDACTED_PHONE]"),
]
}
}