Skip to main content

oxigdal_security/anonymization/
masking.rs

1//! Data masking strategies.
2
3/// Masking strategy.
4pub enum MaskingStrategy {
5    /// Full masking.
6    Full(char),
7    /// Partial masking (keep first/last N chars).
8    Partial {
9        /// Number of characters to keep at the beginning.
10        keep_first: usize,
11        /// Number of characters to keep at the end.
12        keep_last: usize,
13        /// Character to use for masking.
14        mask_char: char,
15    },
16    /// Email masking.
17    Email,
18    /// Credit card masking.
19    CreditCard,
20}
21
22impl MaskingStrategy {
23    /// Apply masking to a string.
24    pub fn apply(&self, input: &str) -> String {
25        match self {
26            MaskingStrategy::Full(mask_char) => mask_char.to_string().repeat(input.len()),
27            MaskingStrategy::Partial {
28                keep_first,
29                keep_last,
30                mask_char,
31            } => {
32                if input.len() <= keep_first + keep_last {
33                    return input.to_string();
34                }
35                let first = &input[..*keep_first];
36                let last = &input[input.len() - keep_last..];
37                let mask_len = input.len() - keep_first - keep_last;
38                format!(
39                    "{}{}{}",
40                    first,
41                    mask_char.to_string().repeat(mask_len),
42                    last
43                )
44            }
45            MaskingStrategy::Email => {
46                if let Some(at_pos) = input.find('@') {
47                    let username = &input[..at_pos];
48                    let domain = &input[at_pos..];
49                    if username.len() <= 2 {
50                        format!("**{}", domain)
51                    } else {
52                        format!("{}***{}", &username[..1], domain)
53                    }
54                } else {
55                    MaskingStrategy::Full('*').apply(input)
56                }
57            }
58            MaskingStrategy::CreditCard => {
59                if input.len() >= 4 {
60                    format!("****-****-****-{}", &input[input.len() - 4..])
61                } else {
62                    MaskingStrategy::Full('*').apply(input)
63                }
64            }
65        }
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72
73    #[test]
74    fn test_full_masking() {
75        let strategy = MaskingStrategy::Full('*');
76        assert_eq!(strategy.apply("secret"), "******");
77    }
78
79    #[test]
80    fn test_partial_masking() {
81        let strategy = MaskingStrategy::Partial {
82            keep_first: 2,
83            keep_last: 2,
84            mask_char: '*',
85        };
86        assert_eq!(strategy.apply("1234567890"), "12******90");
87    }
88
89    #[test]
90    fn test_email_masking() {
91        let strategy = MaskingStrategy::Email;
92        assert_eq!(strategy.apply("user@example.com"), "u***@example.com");
93    }
94
95    #[test]
96    fn test_credit_card_masking() {
97        let strategy = MaskingStrategy::CreditCard;
98        assert_eq!(strategy.apply("1234567812345678"), "****-****-****-5678");
99    }
100}