zxcvbn 0.1.2 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +34 -2
- data/.travis.yml +4 -1
- data/CHANGELOG.md +15 -1
- data/Gemfile +1 -1
- data/Gemfile.lock +1 -1
- data/README.md +45 -37
- data/bin/console +1 -1
- data/lib/zxcvbn.rb +14 -6
- data/lib/zxcvbn/adjacency_graphs.rb +46 -42
- data/lib/zxcvbn/feedback.rb +51 -53
- data/lib/zxcvbn/frequency_lists.rb +9 -7
- data/lib/zxcvbn/matching.rb +168 -191
- data/lib/zxcvbn/scoring.rb +91 -106
- data/lib/zxcvbn/time_estimates.rb +12 -14
- data/lib/zxcvbn/version.rb +1 -1
- data/zxcvbn.gemspec +2 -2
- metadata +9 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 497a8237437937a17ce899c92181dcc11f32e164ca5515ba2739ac02e59f79b2
|
4
|
+
data.tar.gz: 0e0ee12a70d1814db3178c9d925a43b22036c3c5c1444dd2ed38873c5c5686a7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ab0bf4b0709602f08bdb20caae26f124487c3dac8d1f35ef04fb728a0bda822e68ad35db065d28be15745a7c61705ecb402cc7fb463209d8bbc24ca3d8edf379
|
7
|
+
data.tar.gz: e03692c3985a020195e1304451dcd3c91b847d9ef2c391ba62ce61b33f08251a73f195877618a649bd847dd4736769de0b31779a5a61e6b7ab2b23ca946cda81
|
data/.rubocop.yml
CHANGED
@@ -1,5 +1,31 @@
|
|
1
1
|
AllCops:
|
2
2
|
TargetRubyVersion: 2.5
|
3
|
+
NewCops: enable
|
4
|
+
SuggestExtensions: false
|
5
|
+
|
6
|
+
Layout/EndAlignment:
|
7
|
+
EnforcedStyleAlignWith: start_of_line
|
8
|
+
|
9
|
+
Layout/LineLength:
|
10
|
+
Max: 120
|
11
|
+
|
12
|
+
Lint/UnderscorePrefixedVariableName:
|
13
|
+
Enabled: false
|
14
|
+
|
15
|
+
Metrics:
|
16
|
+
Enabled: false
|
17
|
+
|
18
|
+
Naming/VariableNumber:
|
19
|
+
Enabled: false
|
20
|
+
|
21
|
+
Style/Documentation:
|
22
|
+
Enabled: false
|
23
|
+
|
24
|
+
Style/Next:
|
25
|
+
Enabled: false
|
26
|
+
|
27
|
+
Style/NumericPredicate:
|
28
|
+
Enabled: false
|
3
29
|
|
4
30
|
Style/StringLiterals:
|
5
31
|
Enabled: true
|
@@ -9,5 +35,11 @@ Style/StringLiteralsInInterpolation:
|
|
9
35
|
Enabled: true
|
10
36
|
EnforcedStyle: double_quotes
|
11
37
|
|
12
|
-
|
13
|
-
|
38
|
+
Style/NegatedIf:
|
39
|
+
Enabled: false
|
40
|
+
|
41
|
+
Style/SymbolArray:
|
42
|
+
Enabled: false
|
43
|
+
|
44
|
+
Style/WordArray:
|
45
|
+
Enabled: false
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,18 @@
|
|
1
|
-
## [
|
1
|
+
## [0.1.7] - 2021-06-12
|
2
|
+
- Ported original specs
|
3
|
+
- Fix difference found on enumerate_l33t_subs
|
4
|
+
- Setup to also test against current versions of ruby
|
5
|
+
|
6
|
+
## [0.1.6] - 2021-05-28
|
7
|
+
- Added test methods for compatibility with zxcvbn-js and zxcvbn-ruby.
|
8
|
+
|
9
|
+
## [0.1.5] - 2021-05-27
|
10
|
+
- Fix classification of scoring causing differences between js and ruby.
|
11
|
+
|
12
|
+
## [0.1.4] - 2021-05-16
|
13
|
+
|
14
|
+
- Bunch of fixes, all example passwords included have same result as js version.
|
15
|
+
- consistent code style applied.
|
2
16
|
|
3
17
|
## [0.1.0] - 2021-05-16
|
4
18
|
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
# Zxcvbn
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
[](https://badge.fury.io/rb/zxcvbn)
|
4
|
+
[](https://travis-ci.com/formigarafa/zxcvbn-rb)
|
5
|
+
|
6
|
+
Ruby port of Dropbox's [zxcvbn.js](https://github.com/dropbox/zxcvbn) JavaScript library running completely in Ruby (no need to load execjs or libv8).
|
7
|
+
|
8
|
+
The intention is to provide an option 100% Ruby solution with all the same features and same results (or as close to the original JS function as possible).
|
5
9
|
|
6
10
|
## Installation
|
7
11
|
|
@@ -24,49 +28,53 @@ Or install it yourself as:
|
|
24
28
|
```
|
25
29
|
Zxcvbn.zxcvbn("password")
|
26
30
|
=> {
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
+
"password" => "password",
|
32
|
+
"guesses" => 3,
|
33
|
+
"guesses_log10" => 0.47712125471966244,
|
34
|
+
"sequence" => [
|
31
35
|
{
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
36
|
+
"pattern" => "dictionary",
|
37
|
+
"i" => 0,
|
38
|
+
"j" => 7,
|
39
|
+
"token" => "password",
|
40
|
+
"matched_word" => "password",
|
41
|
+
"rank" => 2,
|
42
|
+
"dictionary_name" => "passwords",
|
43
|
+
"reversed" => false,
|
44
|
+
"l33t" => false,
|
45
|
+
"base_guesses" => 2,
|
46
|
+
"uppercase_variations" => 1,
|
47
|
+
"l33t_variations" => 1,
|
48
|
+
"guesses" => 2,
|
49
|
+
"guesses_log10" => 0.3010299956639812
|
46
50
|
}
|
47
51
|
],
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
:offline_fast_hashing_1e10_per_second => "less than a second"
|
52
|
+
"calc_time" => 1,
|
53
|
+
"crack_times_seconds" => {
|
54
|
+
"online_throttling_100_per_hour" => 108.0,
|
55
|
+
"online_no_throttling_10_per_second" => 0.3,
|
56
|
+
"offline_slow_hashing_1e4_per_second" => 0.0003,
|
57
|
+
"offline_fast_hashing_1e10_per_second" => 3.0e-10},
|
58
|
+
"crack_times_display" => {
|
59
|
+
"online_throttling_100_per_hour" => "2 minutes",
|
60
|
+
"online_no_throttling_10_per_second" => "less than a second",
|
61
|
+
"offline_slow_hashing_1e4_per_second" => "less than a second",
|
62
|
+
"offline_fast_hashing_1e10_per_second" => "less than a second"
|
60
63
|
},
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
64
|
+
"score" => 0,
|
65
|
+
"feedback" => {
|
66
|
+
"warning" => "This is a top-10 common password",
|
67
|
+
"suggestions" => [
|
68
|
+
"Add another word or two. Uncommon words are better."
|
69
|
+
]
|
66
70
|
}
|
67
71
|
}
|
68
72
|
```
|
69
73
|
|
74
|
+
### Compatible with `zxcvbn-js` and `zxcvbn-ruby`
|
75
|
+
|
76
|
+
This gem include a compatible interface so it can be used as a drop-in substitution for `zxcvbn-js` or `zxcvbn-ruby`. You can just call `Zxcvbn.test` or use `Zxcvbn::Tester.new` the same way as you would if you were using `zxcvbn-js` or `zxcvbn-ruby`.
|
77
|
+
|
70
78
|
## Development
|
71
79
|
|
72
80
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/bin/console
CHANGED
data/lib/zxcvbn.rb
CHANGED
@@ -14,13 +14,11 @@ module Zxcvbn
|
|
14
14
|
def self.zxcvbn(password, user_inputs = [])
|
15
15
|
start = (Time.now.to_f * 1000).to_i
|
16
16
|
# reset the user inputs matcher on a per-request basis to keep things stateless
|
17
|
-
sanitized_inputs = []
|
17
|
+
sanitized_inputs = []
|
18
18
|
user_inputs.each do |arg|
|
19
|
-
if arg.is_a?(String) || arg.is_a?(Numeric) || arg == true || arg == false
|
20
|
-
sanitized_inputs << arg.to_s.downcase
|
21
|
-
end
|
19
|
+
sanitized_inputs << arg.to_s.downcase if arg.is_a?(String) || arg.is_a?(Numeric) || arg == true || arg == false
|
22
20
|
end
|
23
|
-
Matching.
|
21
|
+
Matching.user_input_dictionary = sanitized_inputs
|
24
22
|
matches = Matching.omnimatch(password)
|
25
23
|
result = Scoring.most_guessable_match_sequence(password, matches)
|
26
24
|
result["calc_time"] = (Time.now.to_f * 1000).to_i - start
|
@@ -29,6 +27,16 @@ module Zxcvbn
|
|
29
27
|
result[prop] = val
|
30
28
|
end
|
31
29
|
result["feedback"] = Feedback.get_feedback(result["score"], result["sequence"])
|
32
|
-
|
30
|
+
result
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.test(password, user_inputs = [])
|
34
|
+
OpenStruct.new(Zxcvbn.zxcvbn(password, user_inputs))
|
35
|
+
end
|
36
|
+
|
37
|
+
class Tester
|
38
|
+
def test(password, user_inputs = [])
|
39
|
+
Zxcvbn.test(password, user_inputs)
|
40
|
+
end
|
33
41
|
end
|
34
42
|
end
|
@@ -2,10 +2,12 @@
|
|
2
2
|
|
3
3
|
# generated by scripts/build_keyboard_adjacency_graphs.py
|
4
4
|
module Zxcvbn
|
5
|
+
# rubocop:disable Layout/SpaceInsideArrayLiteralBrackets
|
6
|
+
# rubocop:disable Layout/ExtraSpacing
|
5
7
|
ADJACENCY_GRAPHS = {
|
6
8
|
"qwerty" => {
|
7
9
|
"!" => ["`~", nil, nil, "2@", "qQ", nil],
|
8
|
-
"\"" => [";:", "[{", "]}",
|
10
|
+
"\"" => [";:", "[{", "]}", nil, nil, "/?"],
|
9
11
|
"#" => ["2@", nil, nil, "4$", "eE", "wW"],
|
10
12
|
"$" => ["3#", nil, nil, "5%", "rR", "eE"],
|
11
13
|
"%" => ["4$", nil, nil, "6^", "tT", "rR"],
|
@@ -18,7 +20,7 @@ module Zxcvbn
|
|
18
20
|
"," => ["mM", "kK", "lL", ".>", nil, nil],
|
19
21
|
"-" => ["0)", nil, nil, "=+", "[{", "pP"],
|
20
22
|
"." => [",<", "lL", ";:", "/?", nil, nil],
|
21
|
-
"/" => [".>", ";:", "'\"",
|
23
|
+
"/" => [".>", ";:", "'\"", nil, nil, nil],
|
22
24
|
"0" => ["9(", nil, nil, "-_", "pP", "oO"],
|
23
25
|
"1" => ["`~", nil, nil, "2@", "qQ", nil],
|
24
26
|
"2" => ["1!", nil, nil, "3#", "wW", "qQ"],
|
@@ -34,7 +36,7 @@ module Zxcvbn
|
|
34
36
|
"<" => ["mM", "kK", "lL", ".>", nil, nil],
|
35
37
|
"=" => ["-_", nil, nil, nil, "]}", "[{"],
|
36
38
|
">" => [",<", "lL", ";:", "/?", nil, nil],
|
37
|
-
"?" => [".>", ";:", "'\"",
|
39
|
+
"?" => [".>", ";:", "'\"", nil, nil, nil],
|
38
40
|
"@" => ["1!", nil, nil, "3#", "wW", "qQ"],
|
39
41
|
"A" => [ nil, "qQ", "wW", "sS", "zZ", nil],
|
40
42
|
"B" => ["vV", "gG", "hH", "nN", nil, nil],
|
@@ -63,8 +65,8 @@ module Zxcvbn
|
|
63
65
|
"Y" => ["tT", "6^", "7&", "uU", "hH", "gG"],
|
64
66
|
"Z" => [ nil, "aA", "sS", "xX", nil, nil],
|
65
67
|
"[" => ["pP", "-_", "=+", "]}", "'\"", ";:"],
|
66
|
-
"\\" => ["]}",
|
67
|
-
"]" => ["[{", "=+", nil, "\\|",
|
68
|
+
"\\" => ["]}", nil, nil, nil, nil, nil],
|
69
|
+
"]" => ["[{", "=+", nil, "\\|", nil, "'\""],
|
68
70
|
"^" => ["5%", nil, nil, "7&", "yY", "tT"],
|
69
71
|
"_" => ["0)", nil, nil, "=+", "[{", "pP"],
|
70
72
|
"`" => [ nil, nil, nil, "1!", nil, nil],
|
@@ -96,12 +98,12 @@ module Zxcvbn
|
|
96
98
|
"z" => [ nil, "aA", "sS", "xX", nil, nil],
|
97
99
|
"{" => ["pP", "-_", "=+", "]}", "'\"", ";:"],
|
98
100
|
"|" => ["]}", nil, nil, nil, nil, nil],
|
99
|
-
"}" => ["[{", "=+", nil, "\\|",
|
101
|
+
"}" => ["[{", "=+", nil, "\\|", nil, "'\""],
|
100
102
|
"~" => [ nil, nil, nil, "1!", nil, nil]
|
101
103
|
},
|
102
104
|
"dvorak" => {
|
103
|
-
"!" => ["`~", nil, nil, "2@", "'\"",
|
104
|
-
"\"" => [ nil, "1!", "2@", ",<", "aA",
|
105
|
+
"!" => ["`~", nil, nil, "2@", "'\"", nil],
|
106
|
+
"\"" => [ nil, "1!", "2@", ",<", "aA", nil],
|
105
107
|
"#" => ["2@", nil, nil, "4$", ".>", ",<"],
|
106
108
|
"$" => ["3#", nil, nil, "5%", "pP", ".>"],
|
107
109
|
"%" => ["4$", nil, nil, "6^", "yY", "pP"],
|
@@ -110,13 +112,13 @@ module Zxcvbn
|
|
110
112
|
"(" => ["8*", nil, nil, "0)", "rR", "cC"],
|
111
113
|
")" => ["9(", nil, nil, "[{", "lL", "rR"],
|
112
114
|
"*" => ["7&", nil, nil, "9(", "cC", "gG"],
|
113
|
-
"+" => ["/?", "]}", nil, "\\|",
|
115
|
+
"+" => ["/?", "]}", nil, "\\|", nil, "-_"],
|
114
116
|
"," => ["'\"", "2@", "3#", ".>", "oO", "aA"],
|
115
117
|
"-" => ["sS", "/?", "=+", nil, nil, "zZ"],
|
116
118
|
"." => [",<", "3#", "4$", "pP", "eE", "oO"],
|
117
119
|
"/" => ["lL", "[{", "]}", "=+", "-_", "sS"],
|
118
120
|
"0" => ["9(", nil, nil, "[{", "lL", "rR"],
|
119
|
-
"1" => ["`~", nil, nil, "2@", "'\"",
|
121
|
+
"1" => ["`~", nil, nil, "2@", "'\"", nil],
|
120
122
|
"2" => ["1!", nil, nil, "3#", ",<", "'\""],
|
121
123
|
"3" => ["2@", nil, nil, "4$", ".>", ",<"],
|
122
124
|
"4" => ["3#", nil, nil, "5%", "pP", ".>"],
|
@@ -132,7 +134,7 @@ module Zxcvbn
|
|
132
134
|
">" => [",<", "3#", "4$", "pP", "eE", "oO"],
|
133
135
|
"?" => ["lL", "[{", "]}", "=+", "-_", "sS"],
|
134
136
|
"@" => ["1!", nil, nil, "3#", ",<", "'\""],
|
135
|
-
"A" => [ nil, "'\"", ",<", "oO", ";:",
|
137
|
+
"A" => [ nil, "'\"", ",<", "oO", ";:", nil],
|
136
138
|
"B" => ["xX", "dD", "hH", "mM", nil, nil],
|
137
139
|
"C" => ["gG", "8*", "9(", "rR", "tT", "hH"],
|
138
140
|
"D" => ["iI", "fF", "gG", "hH", "bB", "xX"],
|
@@ -159,12 +161,12 @@ module Zxcvbn
|
|
159
161
|
"Y" => ["pP", "5%", "6^", "fF", "iI", "uU"],
|
160
162
|
"Z" => ["vV", "sS", "-_", nil, nil, nil],
|
161
163
|
"[" => ["0)", nil, nil, "]}", "/?", "lL"],
|
162
|
-
"\\" => ["=+",
|
164
|
+
"\\" => ["=+", nil, nil, nil, nil, nil],
|
163
165
|
"]" => ["[{", nil, nil, nil, "=+", "/?"],
|
164
166
|
"^" => ["5%", nil, nil, "7&", "fF", "yY"],
|
165
167
|
"_" => ["sS", "/?", "=+", nil, nil, "zZ"],
|
166
168
|
"`" => [ nil, nil, nil, "1!", nil, nil],
|
167
|
-
"a" => [ nil, "'\"", ",<", "oO", ";:",
|
169
|
+
"a" => [ nil, "'\"", ",<", "oO", ";:", nil],
|
168
170
|
"b" => ["xX", "dD", "hH", "mM", nil, nil],
|
169
171
|
"c" => ["gG", "8*", "9(", "rR", "tT", "hH"],
|
170
172
|
"d" => ["iI", "fF", "gG", "hH", "bB", "xX"],
|
@@ -196,39 +198,41 @@ module Zxcvbn
|
|
196
198
|
"~" => [ nil, nil, nil, "1!", nil, nil]
|
197
199
|
},
|
198
200
|
"keypad" => {
|
199
|
-
"*" => ["/",
|
200
|
-
"+" => ["9", "*", "-",
|
201
|
-
"-" => ["*",
|
202
|
-
"." => ["0", "2", "3",
|
203
|
-
"/" => [
|
204
|
-
"0" => [
|
205
|
-
"1" => [
|
206
|
-
"2" => ["1", "4", "5", "6", "3", ".", "0",
|
207
|
-
"3" => ["2", "5", "6",
|
208
|
-
"4" => [
|
201
|
+
"*" => ["/", nil, nil, nil, "-", "+", "9", "8"],
|
202
|
+
"+" => ["9", "*", "-", nil, nil, nil, nil, "6"],
|
203
|
+
"-" => ["*", nil, nil, nil, nil, nil, "+", "9"],
|
204
|
+
"." => ["0", "2", "3", nil, nil, nil, nil, nil],
|
205
|
+
"/" => [nil, nil, nil, nil, "*", "9", "8", "7"],
|
206
|
+
"0" => [nil, "1", "2", "3", ".", nil, nil, nil],
|
207
|
+
"1" => [nil, nil, "4", "5", "2", "0", nil, nil],
|
208
|
+
"2" => ["1", "4", "5", "6", "3", ".", "0", nil],
|
209
|
+
"3" => ["2", "5", "6", nil, nil, nil, ".", "0"],
|
210
|
+
"4" => [nil, nil, "7", "8", "5", "2", "1", nil],
|
209
211
|
"5" => ["4", "7", "8", "9", "6", "3", "2", "1"],
|
210
|
-
"6" => ["5", "8", "9", "+",
|
211
|
-
"7" => [
|
212
|
-
"8" => ["7",
|
213
|
-
"9" => ["8", "/", "*", "-", "+",
|
212
|
+
"6" => ["5", "8", "9", "+", nil, nil, "3", "2"],
|
213
|
+
"7" => [nil, nil, nil, "/", "8", "5", "4", nil],
|
214
|
+
"8" => ["7", nil, "/", "*", "9", "6", "5", "4"],
|
215
|
+
"9" => ["8", "/", "*", "-", "+", nil, "6", "5"]
|
214
216
|
},
|
215
217
|
"mac_keypad" => {
|
216
|
-
"*" => ["/",
|
217
|
-
"+" => ["6", "9", "-",
|
218
|
-
"-" => ["9", "/", "*",
|
219
|
-
"." => ["0", "2", "3",
|
220
|
-
"/" => ["=",
|
221
|
-
"0" => [
|
222
|
-
"1" => [
|
223
|
-
"2" => ["1", "4", "5", "6", "3", ".", "0",
|
224
|
-
"3" => ["2", "5", "6", "+",
|
225
|
-
"4" => [
|
218
|
+
"*" => ["/", nil, nil, nil, nil, nil, "-", "9"],
|
219
|
+
"+" => ["6", "9", "-", nil, nil, nil, nil, "3"],
|
220
|
+
"-" => ["9", "/", "*", nil, nil, nil, "+", "6"],
|
221
|
+
"." => ["0", "2", "3", nil, nil, nil, nil, nil],
|
222
|
+
"/" => ["=", nil, nil, nil, "*", "-", "9", "8"],
|
223
|
+
"0" => [nil, "1", "2", "3", ".", nil, nil, nil],
|
224
|
+
"1" => [nil, nil, "4", "5", "2", "0", nil, nil],
|
225
|
+
"2" => ["1", "4", "5", "6", "3", ".", "0", nil],
|
226
|
+
"3" => ["2", "5", "6", "+", nil, nil, ".", "0"],
|
227
|
+
"4" => [nil, nil, "7", "8", "5", "2", "1", nil],
|
226
228
|
"5" => ["4", "7", "8", "9", "6", "3", "2", "1"],
|
227
|
-
"6" => ["5", "8", "9", "-", "+",
|
228
|
-
"7" => [
|
229
|
-
"8" => ["7",
|
229
|
+
"6" => ["5", "8", "9", "-", "+", nil, "3", "2"],
|
230
|
+
"7" => [nil, nil, nil, "=", "8", "5", "4", nil],
|
231
|
+
"8" => ["7", nil, "=", "/", "9", "6", "5", "4"],
|
230
232
|
"9" => ["8", "=", "/", "*", "-", "+", "6", "5"],
|
231
|
-
"=" => [
|
233
|
+
"=" => [nil, nil, nil, nil, "/", "9", "8", "7"]
|
232
234
|
}
|
233
|
-
}
|
235
|
+
}.freeze
|
236
|
+
# rubocop:enable Layout/ExtraSpacing
|
237
|
+
# rubocop:enable Layout/SpaceInsideArrayLiteralBrackets
|
234
238
|
end
|
data/lib/zxcvbn/feedback.rb
CHANGED
@@ -3,9 +3,9 @@
|
|
3
3
|
module Zxcvbn
|
4
4
|
module Feedback
|
5
5
|
DEFAULT_FEEDBACK = {
|
6
|
-
"warning" =>
|
6
|
+
"warning" => "",
|
7
7
|
"suggestions" => ["Use a few words, avoid common phrases", "No need for symbols, digits, or uppercase letters"]
|
8
|
-
}
|
8
|
+
}.freeze
|
9
9
|
|
10
10
|
def self.get_feedback(score, sequence)
|
11
11
|
if sequence.empty?
|
@@ -16,91 +16,94 @@ module Zxcvbn
|
|
16
16
|
# no feedback if score is good or great.
|
17
17
|
if score > 2
|
18
18
|
return {
|
19
|
-
"warning" =>
|
19
|
+
"warning" => "",
|
20
20
|
"suggestions" => []
|
21
21
|
}
|
22
22
|
end
|
23
23
|
|
24
|
-
longest_match = sequence.max_by{|match| match["token"].length }
|
24
|
+
longest_match = sequence.max_by { |match| match["token"].length }
|
25
25
|
feedback = get_match_feedback(longest_match, sequence.size == 1)
|
26
|
-
extra_feedback =
|
27
|
-
if
|
26
|
+
extra_feedback = "Add another word or two. Uncommon words are better."
|
27
|
+
if feedback
|
28
28
|
feedback["suggestions"].unshift(extra_feedback)
|
29
|
-
if feedback["warning"].nil?
|
30
|
-
feedback["warning"] = ''
|
31
|
-
end
|
29
|
+
feedback["warning"] = "" if feedback["warning"].nil?
|
32
30
|
else
|
33
31
|
feedback = {
|
34
|
-
"warning" =>
|
32
|
+
"warning" => "",
|
35
33
|
"suggestions" => [extra_feedback]
|
36
34
|
}
|
37
35
|
end
|
38
|
-
|
36
|
+
feedback
|
39
37
|
end
|
40
38
|
|
41
39
|
def self.get_match_feedback(match, is_sole_match)
|
42
40
|
case match["pattern"]
|
43
|
-
when
|
44
|
-
|
45
|
-
when
|
46
|
-
|
47
|
-
|
48
|
-
|
41
|
+
when "dictionary"
|
42
|
+
get_dictionary_match_feedback(match, is_sole_match)
|
43
|
+
when "spatial"
|
44
|
+
warning = if match["turns"] == 1
|
45
|
+
"Straight rows of keys are easy to guess"
|
46
|
+
else
|
47
|
+
"Short keyboard patterns are easy to guess"
|
48
|
+
end
|
49
|
+
{
|
49
50
|
"warning" => warning,
|
50
|
-
"suggestions" => [
|
51
|
+
"suggestions" => ["Use a longer keyboard pattern with more turns"]
|
51
52
|
}
|
52
|
-
when
|
53
|
-
warning = match["base_token"].length == 1
|
54
|
-
|
53
|
+
when "repeat"
|
54
|
+
warning = if match["base_token"].length == 1
|
55
|
+
'Repeats like "aaa" are easy to guess'
|
56
|
+
else
|
57
|
+
'Repeats like "abcabcabc" are only slightly harder to guess than "abc"'
|
58
|
+
end
|
59
|
+
{
|
55
60
|
"warning" => warning,
|
56
|
-
"suggestions" => [
|
61
|
+
"suggestions" => ["Avoid repeated words and characters"]
|
57
62
|
}
|
58
|
-
when
|
59
|
-
|
63
|
+
when "sequence"
|
64
|
+
{
|
60
65
|
"warning" => "Sequences like abc or 6543 are easy to guess",
|
61
|
-
"suggestions" => [
|
66
|
+
"suggestions" => ["Avoid sequences"]
|
62
67
|
}
|
63
|
-
when
|
64
|
-
if match["regex_name"]
|
65
|
-
|
68
|
+
when "regex"
|
69
|
+
if match["regex_name"] == "recent_year"
|
70
|
+
{
|
66
71
|
"warning" => "Recent years are easy to guess",
|
67
|
-
"suggestions" => [
|
72
|
+
"suggestions" => ["Avoid recent years", "Avoid years that are associated with you"]
|
68
73
|
}
|
69
74
|
end
|
70
75
|
# break
|
71
|
-
when
|
72
|
-
|
76
|
+
when "date"
|
77
|
+
{
|
73
78
|
"warning" => "Dates are often easy to guess",
|
74
|
-
"suggestions" => [
|
79
|
+
"suggestions" => ["Avoid dates and years that are associated with you"]
|
75
80
|
}
|
76
81
|
end
|
77
82
|
end
|
78
83
|
|
79
84
|
def self.get_dictionary_match_feedback(match, is_sole_match)
|
80
|
-
warning = if match["dictionary_name"] ==
|
85
|
+
warning = if match["dictionary_name"] == "passwords"
|
81
86
|
if is_sole_match && !match["l33t"] && !match["reversed"]
|
82
87
|
if match["rank"] <= 10
|
83
|
-
|
88
|
+
"This is a top-10 common password"
|
84
89
|
elsif match["rank"] <= 100
|
85
|
-
|
90
|
+
"This is a top-100 common password"
|
86
91
|
else
|
87
|
-
|
92
|
+
"This is a very common password"
|
88
93
|
end
|
89
94
|
elsif match["guesses_log10"] <= 4
|
90
|
-
|
91
|
-
end
|
92
|
-
elsif match["dictionary_name"] == 'english_wikipedia'
|
93
|
-
if is_sole_match
|
94
|
-
'A word by itself is easy to guess'
|
95
|
+
"This is similar to a commonly used password"
|
95
96
|
end
|
96
|
-
elsif
|
97
|
+
elsif match["dictionary_name"] == "english_wikipedia"
|
98
|
+
"A word by itself is easy to guess" if is_sole_match
|
99
|
+
elsif ["surnames", "male_names", "female_names"].include?(match["dictionary_name"])
|
97
100
|
if is_sole_match
|
98
|
-
|
101
|
+
"Names and surnames by themselves are easy to guess"
|
99
102
|
else
|
100
|
-
|
103
|
+
"Common names and surnames are easy to guess"
|
101
104
|
end
|
102
105
|
else
|
103
|
-
|
106
|
+
""
|
104
107
|
end
|
105
108
|
suggestions = []
|
106
109
|
word = match["token"]
|
@@ -109,17 +112,12 @@ module Zxcvbn
|
|
109
112
|
elsif word.match(Scoring::ALL_UPPER) && word.downcase != word
|
110
113
|
suggestions << "All-uppercase is almost as easy to guess as all-lowercase"
|
111
114
|
end
|
112
|
-
if match["reversed"] && match["token"].length >= 4
|
113
|
-
|
114
|
-
|
115
|
-
if match["l33t"]
|
116
|
-
suggestions << "Predictable substitutions like '@' instead of 'a' don't help very much"
|
117
|
-
end
|
118
|
-
result = {
|
115
|
+
suggestions << "Reversed words aren't much harder to guess" if match["reversed"] && match["token"].length >= 4
|
116
|
+
suggestions << "Predictable substitutions like '@' instead of 'a' don't help very much" if match["l33t"]
|
117
|
+
{
|
119
118
|
"warning" => warning,
|
120
119
|
"suggestions" => suggestions
|
121
120
|
}
|
122
|
-
return result
|
123
121
|
end
|
124
122
|
end
|
125
123
|
end
|