use crate::RedactionStrategy;
use std::fmt;
#[derive(Default, Clone)]
pub struct IpRedactor;
impl<T: AsRef<str>> RedactionStrategy<T> for IpRedactor {
fn display(&self, value: &T, f: &mut fmt::Formatter<'_>) -> fmt::Result {
redact_ip(value.as_ref(), f)
}
}
fn redact_ip(ip: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if ip.contains(':') {
let parts: Vec<&str> = ip.split(':').collect();
if parts.len() >= 4 {
write!(
f,
"{}:{}:{}:{}:****:****:****:****",
parts[0], parts[1], parts[2], parts[3]
)
} else {
write!(f, "****:****:****:****:****:****:****:****")
}
} else {
let parts: Vec<&str> = ip.split('.').collect();
if parts.len() == 4 {
write!(f, "{}.{}.*.*", parts[0], parts[1])
} else {
write!(f, "[REDACTED: invalid IP]")
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn display_ipv4() {
let my_secret = "192.168.1.100";
let redacted = crate::Redacted::<_, IpRedactor>::new(my_secret);
assert_eq!(
format!("private ip is {redacted}"),
"private ip is 192.168.*.*",
);
}
#[test]
fn display_ipv6() {
let my_secret = "4fd3:694a:d3f4:c465:9b17:18af:e6ae:a243";
let redacted = crate::Redacted::<_, IpRedactor>::new(my_secret);
assert_eq!(
format!("private ip is {redacted}"),
"private ip is 4fd3:694a:d3f4:c465:****:****:****:****",
);
}
#[test]
fn display_invalid() {
let my_secret = "this is not an ip address";
let redacted = crate::Redacted::<_, IpRedactor>::new(my_secret);
assert_eq!(
format!("private ip is {redacted}"),
"private ip is [REDACTED: invalid IP]",
"must not leak secret if it's malformed"
);
}
}