bitwarden_vault/cipher_risk/
password_strength.rs1pub(super) fn calculate_password_strength(password: &str, username: Option<&str>) -> u8 {
11 let mut user_inputs = Vec::new();
12
13 if let Some(username) = username {
15 user_inputs.extend(extract_user_inputs(username));
16 }
17
18 let inputs_refs: Vec<&str> = user_inputs.iter().map(|s| s.as_str()).collect();
20 zxcvbn::zxcvbn(password, &inputs_refs).score().into()
21}
22
23fn extract_user_inputs(username: &str) -> Vec<String> {
30 username
31 .split_once('@')
33 .map_or(username, |(local_part, _domain)| local_part)
35 .trim()
36 .to_lowercase()
37 .split(|c: char| !c.is_alphanumeric())
38 .filter(|s| !s.is_empty())
39 .map(str::to_owned)
40 .collect()
41}
42
43#[cfg(test)]
44mod tests {
45 use super::*;
46
47 #[test]
48 fn test_extract_user_inputs_from_email() {
49 let inputs = extract_user_inputs("[email protected]");
50 assert_eq!(inputs, vec!["john", "doe"]);
51 }
52
53 #[test]
54 fn test_extract_user_inputs_from_username() {
55 let inputs = extract_user_inputs("john_doe123");
56 assert_eq!(inputs, vec!["john", "doe123"]);
57 }
58
59 #[test]
60 fn test_extract_user_inputs_lowercase() {
61 let inputs = extract_user_inputs("[email protected]");
62 assert_eq!(inputs, vec!["johndoe"]);
63 }
64
65 #[test]
66 fn test_extract_user_inputs_empty() {
67 let inputs = extract_user_inputs("");
68 assert!(inputs.is_empty());
69 }
70
71 #[test]
72 fn test_calculate_password_strength_penalizes_username() {
73 let strength_with_username = calculate_password_strength("johndoe123!", Some("johndoe"));
75 let strength_without_username = calculate_password_strength("johndoe123!", None);
76
77 assert!(
78 strength_with_username <= strength_without_username,
79 "Password should be weaker when it contains username"
80 );
81 }
82}