zxcvbn 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +33 -2
- data/.travis.yml +1 -1
- data/Gemfile +1 -1
- data/Gemfile.lock +1 -1
- data/bin/console +1 -1
- data/lib/zxcvbn.rb +4 -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 +167 -187
- data/lib/zxcvbn/scoring.rb +87 -105
- data/lib/zxcvbn/time_estimates.rb +12 -14
- data/lib/zxcvbn/version.rb +1 -1
- data/zxcvbn.gemspec +1 -1
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 109b1843277c9742ff72f5a6fabda24301fcc6b37d2cdd2075cea0f7e2c91926
|
4
|
+
data.tar.gz: d306b716657970d9f14525e5a4eaa0e8e19c1a21183fb09ff0f7ebb488f81337
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6bd52c6440b10692b22a3c1b10e13fd9c84305ca66c455f90361fb5d20962926676b19402c968dc4d1a17be45d95c6da05828fc2f9e7244b0e9555cf9984203d
|
7
|
+
data.tar.gz: 30189e55b622422c4222034eaf5212f9ff2f7d22a9dd5da35bc724372681b43d1e542b91805310943e8a2be37481fd8447c99b9d94924a3cd11d40b9c0107873
|
data/.rubocop.yml
CHANGED
@@ -1,5 +1,30 @@
|
|
1
1
|
AllCops:
|
2
2
|
TargetRubyVersion: 2.5
|
3
|
+
NewCops: enable
|
4
|
+
|
5
|
+
Layout/EndAlignment:
|
6
|
+
EnforcedStyleAlignWith: start_of_line
|
7
|
+
|
8
|
+
Layout/LineLength:
|
9
|
+
Max: 120
|
10
|
+
|
11
|
+
Lint/UnderscorePrefixedVariableName:
|
12
|
+
Enabled: false
|
13
|
+
|
14
|
+
Metrics:
|
15
|
+
Enabled: false
|
16
|
+
|
17
|
+
Naming/VariableNumber:
|
18
|
+
Enabled: false
|
19
|
+
|
20
|
+
Style/Documentation:
|
21
|
+
Enabled: false
|
22
|
+
|
23
|
+
Style/Next:
|
24
|
+
Enabled: false
|
25
|
+
|
26
|
+
Style/NumericPredicate:
|
27
|
+
Enabled: false
|
3
28
|
|
4
29
|
Style/StringLiterals:
|
5
30
|
Enabled: true
|
@@ -9,5 +34,11 @@ Style/StringLiteralsInInterpolation:
|
|
9
34
|
Enabled: true
|
10
35
|
EnforcedStyle: double_quotes
|
11
36
|
|
12
|
-
|
13
|
-
|
37
|
+
Style/NegatedIf:
|
38
|
+
Enabled: false
|
39
|
+
|
40
|
+
Style/SymbolArray:
|
41
|
+
Enabled: false
|
42
|
+
|
43
|
+
Style/WordArray:
|
44
|
+
Enabled: false
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
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,6 @@ module Zxcvbn
|
|
29
27
|
result[prop] = val
|
30
28
|
end
|
31
29
|
result["feedback"] = Feedback.get_feedback(result["score"], result["sequence"])
|
32
|
-
|
30
|
+
result
|
33
31
|
end
|
34
32
|
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
|