1use regex::Regex;
9use std::sync::LazyLock;
10
11static URL_WITH_CREDENTIALS_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
13 Regex::new(r"[a-z][a-z0-9+.-]*://[^:@\s]+:[^@\s]+@[^\s]+")
16 .expect("Failed to create url regex pattern")
17});
18
19static HEX_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
20 Regex::new(r"(?:0x)?[0-9a-fA-F]{32,}").expect("Failed to create hex regex pattern")
22});
23
24pub fn sanitize_message(message: &str) -> String {
26 let mut result = message.to_string();
27
28 result = URL_WITH_CREDENTIALS_PATTERN.replace_all(&result, "[REDACTED_URL]").to_string();
29
30 result = HEX_PATTERN.replace_all(&result, "[REDACTED_HEX]").to_string();
31
32 result
33}
34
35#[macro_export]
40macro_rules! sanitize_error {
41 ($error:expr) => {{
42 #[cfg(feature = "unsafe-debug")]
43 {
44 format!("{}", $error)
45 }
46 #[cfg(not(feature = "unsafe-debug"))]
47 {
48 $crate::sanitize::sanitize_message(&format!("{}", $error))
49 }
50 }};
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 #[test]
58 fn test_sanitize_url_with_credentials_redis() {
59 let msg = "Failed to connect to redis://user:password@localhost:6379";
60 let sanitized = sanitize_message(msg);
61 assert!(sanitized.contains("[REDACTED_URL]"));
62 assert!(!sanitized.contains("password"));
63 assert!(!sanitized.contains("redis://user:"));
64 assert!(sanitized.contains("Failed to connect to"));
66 }
67
68 #[test]
69 fn test_sanitize_url_with_credentials_http() {
70 let msg = "Request failed: https://user:token@api.example.com/endpoint";
71 let sanitized = sanitize_message(msg);
72 assert!(sanitized.contains("[REDACTED_URL]"));
73 assert!(!sanitized.contains("token"));
74 assert!(!sanitized.contains("https://user:"));
75 }
76
77 #[test]
78 fn test_sanitize_url_with_credentials_postgres() {
79 let msg = "DB error: postgres://admin:secret123@db.internal:5432/mydb";
80 let sanitized = sanitize_message(msg);
81 assert!(sanitized.contains("[REDACTED_URL]"));
82 assert!(!sanitized.contains("admin"));
83 assert!(!sanitized.contains("secret123"));
84 }
85
86 #[test]
87 fn test_sanitize_hex_string() {
88 let msg = "Key: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
89 let sanitized = sanitize_message(msg);
90 assert!(sanitized.contains("[REDACTED_HEX]"));
91 }
92}