1use regex::RegexSet;
4use std::sync::LazyLock;
5
6pub enum TestValue<'a> {
7 Single(&'a str),
8 Multiple(&'a [&'a str]),
9}
10
11pub static SECRET_PATTERNS: &[(&str, &str, TestValue)] = &[
13 (
14 "AWS Access Key ID",
15 "AKIA[0-9A-Z]{16}",
16 TestValue::Single("AKIAIOSFODNN7EXAMPLE"),
17 ),
18 (
19 "AWS Secret Access Key env var",
20 "AWS_SECRET_ACCESS_KEY",
21 TestValue::Single("AWS_SECRET_ACCESS_KEY=KEYDATA"),
22 ),
23 (
24 "AWS Session Token env var",
25 "AWS_SESSION_TOKEN",
26 TestValue::Single("AWS_SESSION_TOKEN=KEYDATA"),
27 ),
28 (
29 "Microsoft Azure secret access key env var",
30 "AZURE_.*_KEY",
31 TestValue::Single("export AZURE_STORAGE_ACCOUNT_KEY=KEYDATA"),
32 ),
33 (
34 "Google cloud platform key env var",
35 "GOOGLE_SERVICE_ACCOUNT_KEY",
36 TestValue::Single("export GOOGLE_SERVICE_ACCOUNT_KEY=KEYDATA"),
37 ),
38 (
39 "Atuin login",
40 r"atuin\s+login",
41 TestValue::Single(
42 "atuin login -u mycoolusername -p mycoolpassword -k \"lots of random words\"",
43 ),
44 ),
45 (
46 "GitHub PAT (old)",
47 "ghp_[a-zA-Z0-9]{36}",
48 TestValue::Single("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"), ),
50 (
51 "GitHub PAT (new)",
52 "gh1_[A-Za-z0-9]{21}_[A-Za-z0-9]{59}|github_pat_[0-9][A-Za-z0-9]{21}_[A-Za-z0-9]{59}",
53 TestValue::Multiple(&[
54 "gh1_1234567890abcdefghijk_1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklm",
55 "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", ]),
57 ),
58 (
59 "GitHub OAuth Access Token",
60 "gho_[A-Za-z0-9]{36}",
61 TestValue::Single("gho_1234567890abcdefghijklmnopqrstuvwx000"), ),
63 (
64 "GitHub OAuth Access Token (user)",
65 "ghu_[A-Za-z0-9]{36}",
66 TestValue::Single("ghu_1234567890abcdefghijklmnopqrstuvwx000"), ),
68 (
69 "GitHub App Installation Access Token",
70 "ghs_[A-Za-z0-9]{36}",
71 TestValue::Single("ghs_1234567890abcdefghijklmnopqrstuvwx000"), ),
73 (
74 "GitHub Refresh Token",
75 "ghr_[A-Za-z0-9]{76}",
76 TestValue::Single(
77 "ghr_1234567890abcdefghijklmnopqrstuvwx1234567890abcdefghijklmnopqrstuvwx1234567890abcdefghijklmnopqrstuvwx",
78 ), ),
80 (
81 "GitHub App Installation Access Token v1",
82 "v1\\.[0-9A-Fa-f]{40}",
83 TestValue::Single("v1.1234567890abcdef1234567890abcdef12345678"), ),
85 (
86 "GitLab PAT",
87 "glpat-[a-zA-Z0-9_]{20}",
88 TestValue::Single("glpat-RkE_BG5p_bbjML21WSfy"),
89 ),
90 (
91 "Slack OAuth v2 bot",
92 "xoxb-[0-9]{11}-[0-9]{11}-[0-9a-zA-Z]{24}",
93 TestValue::Single("xoxb-17653672481-19874698323-pdFZKVeTuE8sk7oOcBrzbqgy"),
94 ),
95 (
96 "Slack OAuth v2 user token",
97 "xoxp-[0-9]{11}-[0-9]{11}-[0-9a-zA-Z]{24}",
98 TestValue::Single("xoxp-17653672481-19874698323-pdFZKVeTuE8sk7oOcBrzbqgy"),
99 ),
100 (
101 "Slack webhook",
102 "T[a-zA-Z0-9_]{8}/B[a-zA-Z0-9_]{8}/[a-zA-Z0-9_]{24}",
103 TestValue::Single(
104 "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX",
105 ),
106 ),
107 (
108 "Stripe test key",
109 "sk_test_[0-9a-zA-Z]{24}",
110 TestValue::Single("sk_test_1234567890abcdefghijklmnop"),
111 ),
112 (
113 "Stripe live key",
114 "sk_live_[0-9a-zA-Z]{24}",
115 TestValue::Single("sk_live_1234567890abcdefghijklmnop"),
116 ),
117 (
118 "Netlify authentication token",
119 "nf[pcoub]_[0-9a-zA-Z]{36}",
120 TestValue::Single("nfp_nBh7BdJxUwyaBBwFzpyD29MMFT6pZ9wq5634"),
121 ),
122 (
123 "npm token",
124 "npm_[A-Za-z0-9]{36}",
125 TestValue::Single("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"),
126 ),
127 (
128 "Pulumi personal access token",
129 "pul-[0-9a-f]{40}",
130 TestValue::Single("pul-683c2770662c51d960d72ec27613be7653c5cb26"),
131 ),
132];
133
134pub static SECRET_PATTERNS_RE: LazyLock<RegexSet> = LazyLock::new(|| {
136 let exprs = SECRET_PATTERNS.iter().map(|f| f.1);
137 RegexSet::new(exprs).expect("Failed to build secrets regex")
138});
139
140#[cfg(test)]
141mod tests {
142 use regex::Regex;
143
144 use crate::secrets::{SECRET_PATTERNS, TestValue};
145
146 #[test]
147 fn test_secrets() {
148 for (name, regex, test) in SECRET_PATTERNS {
149 let re =
150 Regex::new(regex).unwrap_or_else(|_| panic!("Failed to compile regex for {name}"));
151
152 match test {
153 TestValue::Single(test) => {
154 assert!(re.is_match(test), "{name} test failed!");
155 }
156 TestValue::Multiple(tests) => {
157 for test_str in tests.iter() {
158 assert!(
159 re.is_match(test_str),
160 "{name} test with value \"{test_str}\" failed!"
161 );
162 }
163 }
164 }
165 }
166 }
167}