use std::sync::OnceLock;
use regex::Regex;
fn secret_patterns() -> &'static [Regex] {
static PATTERNS: OnceLock<Vec<Regex>> = OnceLock::new();
PATTERNS.get_or_init(|| {
[
r#"(?i)['"]?(?:api[-_]?key|apikey|token|secret|password|passwd|pwd)['"]?\s*[:=]\s*['"]?[^\s'"]{8,}['"]?"#,
r"sk-ant-[A-Za-z0-9\-_]{20,}",
r"sk-[A-Za-z0-9]{40,}",
r"(?i)Bearer\s+[A-Za-z0-9\-._~+/]{20,}",
r"(?i)Basic\s+[A-Za-z0-9+/]{20,}",
]
.iter()
.map(|pattern| Regex::new(pattern).expect("secret pattern compiles"))
.collect()
})
}
pub fn contains_secrets(content: &str) -> bool {
secret_patterns()
.iter()
.any(|pattern| pattern.is_match(content))
}
pub fn mask_secrets(content: &str) -> String {
let mut masked = content.to_string();
for pattern in secret_patterns() {
masked = pattern
.replace_all(&masked, |caps: ®ex::Captures<'_>| {
let matched = &caps[0];
match matched.find([':', '=']) {
Some(sep) => format!("{}=[REDACTED]", &matched[..sep]),
None => "[REDACTED]".to_string(),
}
})
.into_owned();
}
masked
}