Skip to content

Commit 283aa21

Browse files
feat: mask PII data if env is set (#1265)
New environment variable `P_MASK_PII` is added if set to true, server masks the PII data as per below - 1. emails are masked like `[email protected]` -> `TXXX@XXX` 2. urls are sent as None 3. strings are masked as all `X`
1 parent bd3bc8b commit 283aa21

File tree

2 files changed

+103
-2
lines changed

2 files changed

+103
-2
lines changed

src/cli.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,14 @@ pub struct Options {
174174
)]
175175
pub send_analytics: bool,
176176

177+
#[arg(
178+
long,
179+
env = "P_MASK_PII",
180+
default_value = "false",
181+
help = "mask PII data when sending to Prism"
182+
)]
183+
pub mask_pii: bool,
184+
177185
// TLS/Security
178186
#[arg(
179187
long,

src/rbac/utils.rs

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
*/
1818
use std::collections::HashMap;
1919

20+
use url::Url;
21+
22+
use crate::parseable::PARSEABLE;
23+
2024
use super::{
2125
map::roles,
2226
role::model::DefaultPrivilege,
@@ -47,8 +51,97 @@ pub fn to_prism_user(user: &User) -> UsersPrism {
4751
UsersPrism {
4852
id: id.into(),
4953
method: method.into(),
50-
email,
51-
picture,
54+
email: mask_pii_string(email),
55+
picture: mask_pii_url(picture),
5256
roles,
5357
}
5458
}
59+
60+
//mask PII string if the P_MASK_PII is set
61+
fn mask_pii_string(input: Option<String>) -> Option<String> {
62+
if !PARSEABLE.options.mask_pii {
63+
return input;
64+
}
65+
mask_string(input)
66+
}
67+
68+
//mask PII url if the P_MASK_PII is set
69+
fn mask_pii_url(input: Option<Url>) -> Option<Url> {
70+
if !PARSEABLE.options.mask_pii {
71+
return input;
72+
}
73+
None
74+
}
75+
76+
fn mask_string(input: Option<String>) -> Option<String> {
77+
let input = input.as_ref()?;
78+
if input.contains('@') {
79+
// masking an email
80+
let parts: Vec<&str> = input.split('@').collect();
81+
//mask everything if not a proper email format
82+
if parts.len() != 2 {
83+
return Some("X".repeat(input.len()));
84+
}
85+
86+
let username = parts[0];
87+
88+
// Mask the username - first letter capitalized, rest are X
89+
let masked_username = if !username.is_empty() {
90+
let first = username.chars().next().unwrap().to_uppercase().to_string();
91+
format!("{}{}", first, "X".repeat(username.len() - 1))
92+
} else {
93+
return Some("X".repeat(input.len()));
94+
};
95+
96+
// mask to XXX for everything after the @ symbol
97+
Some(format!("{}@XXX", masked_username))
98+
} else {
99+
// mask all other strings with X
100+
Some("X".repeat(input.len()))
101+
}
102+
}
103+
104+
#[cfg(test)]
105+
mod tests {
106+
use super::*;
107+
108+
#[test]
109+
fn test_mask_string_with_email() {
110+
// Test masking a valid email
111+
let email = Some("[email protected]".to_string());
112+
let masked_email = mask_string(email);
113+
assert_eq!(masked_email, Some("TXXX@XXX".to_string()));
114+
}
115+
116+
#[test]
117+
fn test_mask_string_with_invalid_email() {
118+
// Test masking an invalid email
119+
let invalid_email = Some("invalid-email".to_string());
120+
let masked_email = mask_string(invalid_email);
121+
assert_eq!(masked_email, Some("XXXXXXXXXXXXX".to_string()));
122+
}
123+
124+
#[test]
125+
fn test_mask_string_with_empty_string() {
126+
// Test masking an empty string
127+
let empty_string = Some("".to_string());
128+
let masked_string = mask_string(empty_string);
129+
assert_eq!(masked_string, Some("".to_string()));
130+
}
131+
132+
#[test]
133+
fn test_mask_string_with_generic_string() {
134+
// Test masking a generic string
135+
let generic_string = Some("sensitive_data".to_string());
136+
let masked_string = mask_string(generic_string);
137+
assert_eq!(masked_string, Some("XXXXXXXXXXXXXX".to_string()));
138+
}
139+
140+
#[test]
141+
fn test_mask_string_with_none() {
142+
// Test masking a None value
143+
let none_value: Option<String> = None;
144+
let masked_value = mask_string(none_value);
145+
assert_eq!(masked_value, None);
146+
}
147+
}

0 commit comments

Comments
 (0)