specfold_core/guards/
secrets.rs1use regex::Regex;
5use std::sync::OnceLock;
6
7pub struct SecretPattern {
8 pub name: &'static str,
9 pub re: Regex,
10}
11
12const PATTERNS: &[(&str, &str)] = &[
13 ("private key", r"-----BEGIN (?:RSA |EC |DSA |OPENSSH |PGP |ENCRYPTED )?PRIVATE KEY(?: BLOCK)?-----"),
14 ("AWS access key id", r"\b(?:AKIA|ASIA)[0-9A-Z]{16}\b"),
15 ("GitHub token", r"\b(?:gh[pousr]_[A-Za-z0-9]{36,}|github_pat_[A-Za-z0-9_]{40,})\b"),
16 ("GitLab token", r"\bglpat-[A-Za-z0-9_-]{20,}\b"),
17 ("Slack token", r"\bxox[abposr]-[A-Za-z0-9-]{10,}\b"),
18 ("Google API key", r"\bAIza[0-9A-Za-z_-]{35}\b"),
19 ("Stripe live key", r"\b(?:sk|rk)_live_[0-9A-Za-z]{20,}\b"),
20 ("npm token", r"\bnpm_[A-Za-z0-9]{36}\b"),
21 ("Anthropic API key", r"\bsk-ant-[A-Za-z0-9_-]{20,}\b"),
22 ("OpenAI API key", r"\bsk-(?:proj|svcacct|admin)-[A-Za-z0-9_-]{20,}\b"),
23 ("Hugging Face token", r"\bhf_[A-Za-z]{34}\b"),
24];
25
26pub fn all() -> &'static [SecretPattern] {
27 static P: OnceLock<Vec<SecretPattern>> = OnceLock::new();
28 P.get_or_init(|| {
29 PATTERNS.iter().filter_map(|(name, re)| Regex::new(re).ok().map(|re| SecretPattern { name, re })).collect()
30 })
31}
32
33#[cfg(test)]
34mod tests {
35 use super::*;
36
37 #[test]
40 fn detects_known_formats_only() {
41 let hit = |s: &str| all().iter().find(|p| p.re.is_match(s)).map(|p| p.name);
42 assert_eq!(hit(&format!("-----BEGIN OPENSSH {}-----", "PRIVATE KEY")), Some("private key"));
43 assert_eq!(hit(&format!("key {}{} here", "AK", "IA".to_string() + &"X".repeat(16))), Some("AWS access key id"));
44 assert_eq!(hit(&format!("{}_{}", "ghp", "a".repeat(36))), Some("GitHub token"));
45 assert_eq!(hit("the word secret and sk_test_123"), None);
46 }
47}