zxcvbn 0.1.2 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/zxcvbn.svg)](https://badge.fury.io/rb/zxcvbn)
|
4
|
+
[![Build Status](https://travis-ci.com/formigarafa/zxcvbn-rb.svg?branch=master)](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
|