use regex::Regex;
use std::sync::OnceLock;
fn patterns() -> &'static [Regex] {
static P: OnceLock<Vec<Regex>> = OnceLock::new();
P.get_or_init(|| {
vec![
Regex::new(r"AKIA[0-9A-Z]{16,}").unwrap(), Regex::new(r"ghp_[A-Za-z0-9]{36,}").unwrap(), Regex::new(r"github_pat_[A-Za-z0-9_]{20,}").unwrap(), Regex::new(r"eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}").unwrap(), Regex::new(r"-----BEGIN [A-Z ]*PRIVATE KEY-----").unwrap(), Regex::new(r"xox[baprs]-[A-Za-z0-9-]{10,}").unwrap(), Regex::new(r"sk_live_[A-Za-z0-9]{16,}").unwrap(), Regex::new(r"AIza[A-Za-z0-9_-]{35}").unwrap(), ]
})
}
pub fn redact(text: &str) -> String {
let mut out = text.to_string();
for re in patterns() {
out = re.replace_all(&out, "[REDACTED]").into_owned();
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn redacts_aws_and_jwt() {
let s = "key=AKIAIOSFODNN7EXAMPLE tok=eyJhbGciOi.JzdWIiOiI.SflKxwRJ";
let r = redact(s);
assert!(!r.contains("AKIAIOSFODNN7EXAMPLE"));
assert!(r.contains("[REDACTED]"));
}
#[test]
fn github_pat_tail_not_leaked() {
let pat_36 = "A".repeat(36);
let extra = "EXTRA";
let input = format!("tok=ghp_{pat_36}{extra}");
let r = redact(&input);
assert!(
!r.contains(extra),
"tail of oversized ghp_ token must not leak; got: {r}"
);
assert!(r.contains("[REDACTED]"), "ghp_ token must be redacted");
}
#[test]
fn new_secret_prefixes_are_redacted() {
let slack = "xoxb-abc123def456";
let r = redact(slack);
assert!(
!r.contains("xoxb-abc123def456"),
"Slack token must be redacted"
);
assert!(r.contains("[REDACTED]"));
let stripe = "sk_live_abcdefghij123456";
let r = redact(stripe);
assert!(
!r.contains("sk_live_abcdefghij123456"),
"Stripe key must be redacted"
);
assert!(r.contains("[REDACTED]"));
let gkey = format!("AIza{}", "A".repeat(35));
let r = redact(&gkey);
assert!(!r.contains(&gkey), "Google API key must be redacted");
assert!(r.contains("[REDACTED]"));
let fgpat = format!("github_pat_{}", "A".repeat(20));
let r = redact(&fgpat);
assert!(
!r.contains(&fgpat),
"GitHub fine-grained PAT must be redacted"
);
assert!(r.contains("[REDACTED]"));
}
#[test]
fn benign_strings_are_not_redacted() {
let plain = "hello world, no secrets here";
assert_eq!(redact(plain), plain);
}
}