use regex::Regex;
use std::sync::LazyLock;
pub struct HighConfidencePattern {
pub id: &'static str,
pub name: &'static str,
pub regex: &'static str,
}
pub static HIGH_CONFIDENCE_PATTERNS: &[HighConfidencePattern] = &[
HighConfidencePattern {
id: "secrets/aws-access-key",
name: "AWS Access Key",
regex: r"AKIA[0-9A-Z]{16}",
},
HighConfidencePattern {
id: "secrets/private-key",
name: "Private Key Block",
regex: r"-----BEGIN (?:RSA |EC |DSA |OPENSSH |PGP )?PRIVATE KEY-----",
},
HighConfidencePattern {
id: "secrets/github-token",
name: "GitHub Token",
regex: r"gh[pousr]_[A-Za-z0-9]{36,}",
},
HighConfidencePattern {
id: "secrets/stripe-live-key",
name: "Stripe Live Key",
regex: r"sk_live_[A-Za-z0-9]{24,}",
},
HighConfidencePattern {
id: "secrets/slack-token",
name: "Slack Token",
regex: r"xox[baprs]-[A-Za-z0-9-]{10,}",
},
];
static COMPILED: LazyLock<Vec<(&'static HighConfidencePattern, Regex)>> = LazyLock::new(|| {
HIGH_CONFIDENCE_PATTERNS
.iter()
.map(|p| (p, Regex::new(p.regex).expect("valid high-confidence regex")))
.collect()
});
pub fn is_placeholder(matched: &str) -> bool {
matched.to_uppercase().contains("EXAMPLE")
|| (matched.len() >= 12 && matched.chars().rev().take(12).all(|c| c == 'x' || c == 'X'))
}
pub fn find_high_confidence(line: &str) -> Option<(&'static HighConfidencePattern, &str)> {
COMPILED.iter().find_map(|(p, re)| {
re.find_iter(line)
.map(|m| m.as_str())
.find(|m| !is_placeholder(m))
.map(|m| (*p, m))
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn detects_each_shape_and_ignores_placeholders() {
let aws = format!("{}{}", "AKIA", "QWERTYUIOPASDFGH");
let gh = format!("{}{}", "ghp_", "a".repeat(36));
let stripe = format!("{}{}", "sk_live_", "b".repeat(24));
let slack = format!("{}{}", "xoxb-", "1234567890-abc");
let pem = format!("-----BEGIN {} KEY-----", "RSA PRIVATE");
for s in [&aws, &gh, &stripe, &slack, &pem] {
assert!(find_high_confidence(s).is_some(), "{s}");
}
assert!(find_high_confidence("AKIAIOSFODNN7EXAMPLE").is_none());
let filler = format!("{}{}", "ghp_", "x".repeat(36));
assert!(find_high_confidence(&filler).is_none());
assert!(find_high_confidence("password = \"hunter2\"").is_none());
let mixed = format!("AKIAIOSFODNN7EXAMPLE {aws}");
assert_eq!(find_high_confidence(&mixed).unwrap().1, aws);
}
}