use regex::Regex;
use std::sync::LazyLock;
static URL_WITH_CREDENTIALS_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"[a-z][a-z0-9+.-]*://[^:@\s]+:[^@\s]+@[^\s]+")
.expect("Failed to create url regex pattern")
});
static HEX_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"(?:0x)?[0-9a-fA-F]{32,}").expect("Failed to create hex regex pattern")
});
pub fn sanitize_message(message: &str) -> String {
let mut result = message.to_string();
result = URL_WITH_CREDENTIALS_PATTERN.replace_all(&result, "[REDACTED_URL]").to_string();
result = HEX_PATTERN.replace_all(&result, "[REDACTED_HEX]").to_string();
result
}
#[macro_export]
macro_rules! sanitize_error {
($error:expr) => {{
#[cfg(feature = "unsafe-debug")]
{
format!("{}", $error)
}
#[cfg(not(feature = "unsafe-debug"))]
{
$crate::sanitize::sanitize_message(&format!("{}", $error))
}
}};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sanitize_url_with_credentials_redis() {
let msg = "Failed to connect to redis://user:password@localhost:6379";
let sanitized = sanitize_message(msg);
assert!(sanitized.contains("[REDACTED_URL]"));
assert!(!sanitized.contains("password"));
assert!(!sanitized.contains("redis://user:"));
assert!(sanitized.contains("Failed to connect to"));
}
#[test]
fn test_sanitize_url_with_credentials_http() {
let msg = "Request failed: https://user:token@api.example.com/endpoint";
let sanitized = sanitize_message(msg);
assert!(sanitized.contains("[REDACTED_URL]"));
assert!(!sanitized.contains("token"));
assert!(!sanitized.contains("https://user:"));
}
#[test]
fn test_sanitize_url_with_credentials_postgres() {
let msg = "DB error: postgres://admin:secret123@db.internal:5432/mydb";
let sanitized = sanitize_message(msg);
assert!(sanitized.contains("[REDACTED_URL]"));
assert!(!sanitized.contains("admin"));
assert!(!sanitized.contains("secret123"));
}
#[test]
fn test_sanitize_hex_string() {
let msg = "Key: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
let sanitized = sanitize_message(msg);
assert!(sanitized.contains("[REDACTED_HEX]"));
}
}