1use fancy_regex::Regex;
6use std::fmt::Display;
7use std::sync::OnceLock;
8
9fn get_expressions() -> &'static Vec<Regex> {
10 static EXPRESSIONS: OnceLock<Vec<Regex>> = OnceLock::new();
11 EXPRESSIONS.get_or_init(|| {
12 vec![
13 Regex::new(
15 r##"gl(pat|oas|dt|rt|rtr|cbt|ptt|ft|imt|agent|wt|soat|ffct)-[0-9a-zA-Z\-_]*"##,
16 )
17 .unwrap(),
18 Regex::new(r##"g(hp|ithub_pat|ho|hu|hs|hr)_[0-9a-zA-Z\-_]*"##).unwrap(),
21 ]
22 })
23}
24
25pub fn redact(input: &impl Display) -> String {
28 let mut copy = input.to_string();
29 for expr in get_expressions() {
30 copy = expr.replace_all(©, "LORRY_REDACTED").to_string();
31 }
32 copy
33}
34
35#[cfg(test)]
36mod tests {
37 use super::*;
38
39 struct TestCase<'a> {
40 pub input: &'a str,
41 pub expected: &'a str,
42 }
43
44 impl TestCase<'_> {
45 pub fn check(&self) {
46 let result = redact(&self.input.to_string());
47 assert!(result == self.expected);
48 }
49 }
50
51 #[test]
52 fn test_redact() {
53 TestCase {
54 input: "glpat-Y38-kU_3pviux6H1D9ec",
55 expected: "LORRY_REDACTED",
56 }
57 .check();
58 TestCase {
59 input: "glpat-AdmAbJdT-FMYPa_2bQyy",
60 expected: "LORRY_REDACTED",
61 }
62 .check();
63 TestCase {
64 input: "glpat-MjW2FqijnvVBs7yPEoDG",
65 expected: "LORRY_REDACTED",
66 }
67 .check();
68 TestCase {
69 input: "Using git binary to push remote: http://oauth2:glpat-25HjHgcpRJsY_JQweJYa@localhost:9999/lorry-mirrors/test/lorry.git",
70 expected: "Using git binary to push remote: http://oauth2:LORRY_REDACTED@localhost:9999/lorry-mirrors/test/lorry.git",
71 }.check();
72 }
73}