Skip to main content

str0m_proto/
pii.rs

1//! PII-safe wrapper for sensitive values.
2//!
3//! The `Pii<T>` type is a generic wrapper for any value that may contain
4//! personally identifiable information (PII) or other sensitive data.
5//! When the `pii` feature is enabled, any value wrapped in `Pii` will be
6//! redacted (displayed as `REDACTED`) when formatted with `Display`.
7//! Otherwise, the inner value is shown as normal. This helps prevent
8//! accidental leakage of sensitive information in logs or user-facing
9//! output.
10//!
11//! Logging or displaying sensitive data such as IP addresses, user IDs, or
12//! authentication tokens can lead to privacy violations or security
13//! incidents.
14//!
15//! This wrapper should be used for debug, info, warn, and error logs.
16//! It is not intended for trace-level logs, as trace logs are typically
17//! disabled in production environments.
18
19use core::fmt;
20use core::ops::Deref;
21
22#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
23pub struct Pii<T>(pub T);
24
25impl<T: fmt::Display> fmt::Display for Pii<T> {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        #[cfg(feature = "pii")]
28        {
29            write!(f, "{{REDACTED}}")
30        }
31        #[cfg(not(feature = "pii"))]
32        {
33            write!(f, "{}", self.0)
34        }
35    }
36}
37
38impl<T: fmt::Debug> fmt::Debug for Pii<T> {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        #[cfg(feature = "pii")]
41        {
42            write!(f, "{{REDACTED}}")
43        }
44        #[cfg(not(feature = "pii"))]
45        {
46            write!(f, "{:?}", self.0)
47        }
48    }
49}
50
51impl<T> Deref for Pii<T> {
52    type Target = T;
53
54    fn deref(&self) -> &Self::Target {
55        &self.0
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62    use std::net::{IpAddr, Ipv4Addr, SocketAddr};
63
64    #[test]
65    fn socket_addr_display() {
66        let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
67        let pii_addr = Pii(addr);
68
69        #[cfg(feature = "pii")]
70        assert_eq!(pii_addr.to_string(), "{REDACTED}");
71
72        #[cfg(not(feature = "pii"))]
73        assert_eq!(pii_addr.to_string(), "127.0.0.1:8080");
74    }
75
76    #[test]
77    fn string_display() {
78        let sensitive = String::from("sensitive data");
79        let pii_string = Pii(sensitive);
80
81        #[cfg(feature = "pii")]
82        assert_eq!(pii_string.to_string(), "{REDACTED}");
83
84        #[cfg(not(feature = "pii"))]
85        assert_eq!(pii_string.to_string(), "sensitive data");
86    }
87}