zxcvbn 0.1.3 → 0.1.4
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 +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
|