bitwarden_core/auth/password/
strength.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
use zxcvbn::zxcvbn;

const GLOBAL_INPUTS: [&str; 3] = ["bitwarden", "bit", "warden"];

pub(crate) fn password_strength(
    password: String,
    email: String,
    additional_inputs: Vec<String>,
) -> u8 {
    let mut inputs = email_to_user_inputs(&email);
    inputs.extend(additional_inputs);

    let mut arr: Vec<_> = inputs.iter().map(String::as_str).collect();
    arr.extend(GLOBAL_INPUTS);

    zxcvbn(&password, &arr).score().into()
}

fn email_to_user_inputs(email: &str) -> Vec<String> {
    let parts = email.split_once('@');
    match parts {
        Some((prefix, _)) => prefix
            .trim()
            .to_lowercase()
            .split(|c: char| !c.is_alphanumeric())
            .map(str::to_owned)
            .collect(),
        None => vec![],
    }
}

#[cfg(test)]
mod tests {
    use super::{email_to_user_inputs, password_strength};

    #[test]
    fn test_password_strength() {
        let cases = vec![
            ("password", "[email protected]", 0),
            ("password11", "[email protected]", 1),
            ("Weakpass2", "[email protected]", 2),
            ("GoodPass3!", "[email protected]", 3),
            ("VeryStrong123@#", "[email protected]", 4),
        ];

        for (password, email, expected) in cases {
            let result = password_strength(password.to_owned(), email.to_owned(), vec![]);
            assert_eq!(expected, result, "{email}: {password}");
        }
    }

    #[test]
    fn test_penalize_email() {
        let password = "asdfjkhkjwer!";

        let result = password_strength(
            password.to_owned(),
            "[email protected]".to_owned(),
            vec![],
        );
        assert_eq!(result, 4);

        let result = password_strength(
            password.to_owned(),
            "[email protected]".to_owned(),
            vec![],
        );
        assert_eq!(result, 1);
    }

    #[test]
    fn test_email_to_user_inputs() {
        let email = "[email protected]";
        let result = email_to_user_inputs(email);

        assert_eq!(result, vec!["random"]);
    }
}