kovra_wrapper/
sanitize.rs1pub const MASK: &[u8] = b"***";
16
17pub fn mask_secrets(data: &[u8], secrets: &[&[u8]]) -> Vec<u8> {
21 let mut out = data.to_vec();
22 for secret in secrets {
23 if secret.is_empty() {
24 continue;
25 }
26 out = replace_bytes(&out, secret, MASK);
27 }
28 out
29}
30
31fn replace_bytes(haystack: &[u8], needle: &[u8], rep: &[u8]) -> Vec<u8> {
33 if needle.is_empty() || needle.len() > haystack.len() {
34 return haystack.to_vec();
35 }
36 let mut out = Vec::with_capacity(haystack.len());
37 let mut i = 0;
38 while i < haystack.len() {
39 if i + needle.len() <= haystack.len() && &haystack[i..i + needle.len()] == needle {
40 out.extend_from_slice(rep);
41 i += needle.len();
42 } else {
43 out.push(haystack[i]);
44 i += 1;
45 }
46 }
47 out
48}
49
50#[cfg(test)]
51mod tests {
52 use super::*;
53
54 #[test]
55 fn masks_naive_occurrences() {
56 let out = mask_secrets(b"connecting with hunter2 now", &[b"hunter2"]);
57 assert_eq!(out, b"connecting with *** now");
58 assert!(!String::from_utf8_lossy(&out).contains("hunter2"));
59 }
60
61 #[test]
62 fn masks_multiple_secrets_and_repeats() {
63 let out = mask_secrets(b"a=AAA b=BBB a=AAA", &[b"AAA", b"BBB"]);
64 assert_eq!(out, b"a=*** b=*** a=***");
65 }
66
67 #[test]
68 fn empty_secret_is_ignored() {
69 let out = mask_secrets(b"untouched", &[b""]);
70 assert_eq!(out, b"untouched");
71 }
72
73 #[test]
74 fn does_not_catch_obfuscated_exfiltration() {
75 let out = mask_secrets(b"aHVudGVyMg==", &[b"hunter2"]);
78 assert_eq!(out, b"aHVudGVyMg==");
79 }
80}