zxcvbn-ruby 0.0.3 → 1.1.0
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 +7 -0
- data/.travis.yml +12 -0
- data/CHANGELOG.md +42 -0
- data/CODE_OF_CONDUCT.md +130 -0
- data/Gemfile +8 -1
- data/Guardfile +26 -0
- data/{LICENSE → LICENSE.txt} +0 -0
- data/README.md +165 -9
- data/Rakefile +5 -1
- data/lib/zxcvbn.rb +10 -36
- data/lib/zxcvbn/crack_time.rb +44 -42
- data/lib/zxcvbn/data.rb +29 -0
- data/lib/zxcvbn/dictionary_ranker.rb +0 -2
- data/lib/zxcvbn/entropy.rb +3 -1
- data/lib/zxcvbn/feedback.rb +10 -0
- data/lib/zxcvbn/feedback_giver.rb +133 -0
- data/lib/zxcvbn/matchers/date.rb +2 -0
- data/lib/zxcvbn/matchers/dictionary.rb +2 -0
- data/lib/zxcvbn/matchers/digits.rb +2 -0
- data/lib/zxcvbn/matchers/l33t.rb +2 -2
- data/lib/zxcvbn/matchers/regex_helpers.rb +2 -0
- data/lib/zxcvbn/matchers/repeat.rb +2 -0
- data/lib/zxcvbn/matchers/sequences.rb +2 -0
- data/lib/zxcvbn/matchers/spatial.rb +2 -0
- data/lib/zxcvbn/matchers/year.rb +2 -0
- data/lib/zxcvbn/math.rb +2 -2
- data/lib/zxcvbn/omnimatch.rb +14 -3
- data/lib/zxcvbn/password_strength.rb +7 -3
- data/lib/zxcvbn/score.rb +1 -1
- data/lib/zxcvbn/scorer.rb +11 -0
- data/lib/zxcvbn/tester.rb +43 -0
- data/lib/zxcvbn/version.rb +1 -1
- data/spec/dictionary_ranker_spec.rb +2 -2
- data/spec/feedback_giver_spec.rb +212 -0
- data/spec/matchers/date_spec.rb +8 -8
- data/spec/matchers/dictionary_spec.rb +25 -14
- data/spec/matchers/digits_spec.rb +3 -3
- data/spec/matchers/l33t_spec.rb +15 -13
- data/spec/matchers/repeat_spec.rb +6 -6
- data/spec/matchers/sequences_spec.rb +5 -5
- data/spec/matchers/spatial_spec.rb +8 -8
- data/spec/matchers/year_spec.rb +3 -3
- data/spec/omnimatch_spec.rb +2 -2
- data/spec/scoring/crack_time_spec.rb +13 -13
- data/spec/scoring/entropy_spec.rb +28 -25
- data/spec/scoring/math_spec.rb +22 -18
- data/spec/support/matcher.rb +1 -1
- data/spec/tester_spec.rb +99 -0
- data/spec/zxcvbn_spec.rb +14 -39
- data/zxcvbn-ruby.gemspec +11 -0
- metadata +34 -29
data/lib/zxcvbn/scorer.rb
CHANGED
@@ -1,5 +1,16 @@
|
|
1
|
+
require 'zxcvbn/entropy'
|
2
|
+
require 'zxcvbn/crack_time'
|
3
|
+
require 'zxcvbn/score'
|
4
|
+
require 'zxcvbn/match'
|
5
|
+
|
1
6
|
module Zxcvbn
|
2
7
|
class Scorer
|
8
|
+
def initialize(data)
|
9
|
+
@data = data
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :data
|
13
|
+
|
3
14
|
include Entropy
|
4
15
|
include CrackTime
|
5
16
|
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "zxcvbn/data"
|
4
|
+
require "zxcvbn/password_strength"
|
5
|
+
|
6
|
+
module Zxcvbn
|
7
|
+
# Allows you to test the strength of multiple passwords without reading and
|
8
|
+
# parsing the dictionary data from disk each test. Dictionary data is read
|
9
|
+
# once from disk and stored in memory for the life of the Tester object.
|
10
|
+
#
|
11
|
+
# Example:
|
12
|
+
#
|
13
|
+
# tester = Zxcvbn::Tester.new
|
14
|
+
#
|
15
|
+
# tester.add_word_lists("custom" => ["words"])
|
16
|
+
#
|
17
|
+
# tester.test("password 1")
|
18
|
+
# tester.test("password 2")
|
19
|
+
# tester.test("password 3")
|
20
|
+
class Tester
|
21
|
+
def initialize
|
22
|
+
@data = Data.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def test(password, user_inputs = [])
|
26
|
+
PasswordStrength.new(@data).test(password, sanitize(user_inputs))
|
27
|
+
end
|
28
|
+
|
29
|
+
def add_word_lists(lists)
|
30
|
+
lists.each_pair { |name, words| @data.add_word_list(name, sanitize(words)) }
|
31
|
+
end
|
32
|
+
|
33
|
+
def inspect
|
34
|
+
"#<#{self.class}:0x#{__id__.to_s(16)}>"
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def sanitize(user_inputs)
|
40
|
+
user_inputs.select { |i| i.respond_to?(:downcase) }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/zxcvbn/version.rb
CHANGED
@@ -5,8 +5,8 @@ describe Zxcvbn::DictionaryRanker do
|
|
5
5
|
it 'ranks word lists' do
|
6
6
|
result = Zxcvbn::DictionaryRanker.rank_dictionaries({:test => ['ABC', 'def'],
|
7
7
|
:test2 => ['def', 'ABC']})
|
8
|
-
result[:test].
|
9
|
-
result[:test2].
|
8
|
+
expect(result[:test]).to eq({'abc' => 1, 'def' => 2})
|
9
|
+
expect(result[:test2]).to eq({'def' => 1, 'abc' => 2})
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
@@ -0,0 +1,212 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Zxcvbn::FeedbackGiver do
|
4
|
+
# NOTE: We go in via the tester because the `FeedbackGiver` relies on both
|
5
|
+
# Omnimatch and the Scorer, which are troublesome to wire up for tests
|
6
|
+
let(:tester) { Zxcvbn::Tester.new }
|
7
|
+
|
8
|
+
describe '.get_feedback' do
|
9
|
+
it "gives empty feedback when a password's score is good" do
|
10
|
+
feedback = tester.test('5815A30BE798').feedback
|
11
|
+
|
12
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
13
|
+
expect(feedback.warning).to be_nil
|
14
|
+
expect(feedback.suggestions).to be_empty
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'gives general feedback when a password is empty' do
|
18
|
+
feedback = tester.test('').feedback
|
19
|
+
|
20
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
21
|
+
expect(feedback.warning).to be_nil
|
22
|
+
expect(feedback.suggestions).to contain_exactly(
|
23
|
+
'Use a few words, avoid common phrases',
|
24
|
+
'No need for symbols, digits, or uppercase letters'
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "gives general feedback when a password is poor but doesn't match any heuristics" do
|
29
|
+
feedback = tester.test(':005:0').feedback
|
30
|
+
|
31
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
32
|
+
expect(feedback.warning).to be_nil
|
33
|
+
expect(feedback.suggestions).to contain_exactly(
|
34
|
+
'Add another word or two. Uncommon words are better.'
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'gives specific feedback' do
|
39
|
+
describe 'for dictionary passwords' do
|
40
|
+
it 'that are extremely common' do
|
41
|
+
feedback = tester.test('password').feedback
|
42
|
+
|
43
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
44
|
+
expect(feedback.warning).to eql('This is a top-10 common password')
|
45
|
+
expect(feedback.suggestions).to contain_exactly(
|
46
|
+
'Add another word or two. Uncommon words are better.'
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'that are very, very common' do
|
51
|
+
feedback = tester.test('letmein').feedback
|
52
|
+
|
53
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
54
|
+
expect(feedback.warning).to eql('This is a top-100 common password')
|
55
|
+
expect(feedback.suggestions).to contain_exactly(
|
56
|
+
'Add another word or two. Uncommon words are better.'
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'that are very common' do
|
61
|
+
feedback = tester.test('playstation').feedback
|
62
|
+
|
63
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
64
|
+
expect(feedback.warning).to eql('This is a very common password')
|
65
|
+
expect(feedback.suggestions).to contain_exactly(
|
66
|
+
'Add another word or two. Uncommon words are better.'
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'that are common and you tried to use l33tsp33k' do
|
71
|
+
feedback = tester.test('pl4yst4ti0n').feedback
|
72
|
+
|
73
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
74
|
+
expect(feedback.warning).to eql(
|
75
|
+
'This is similar to a commonly used password'
|
76
|
+
)
|
77
|
+
expect(feedback.suggestions).to contain_exactly(
|
78
|
+
'Add another word or two. Uncommon words are better.',
|
79
|
+
"Predictable substitutions like '@' instead of 'a' don't help very much"
|
80
|
+
)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'that are common and you capitalised the start' do
|
84
|
+
feedback = tester.test('Password').feedback
|
85
|
+
|
86
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
87
|
+
expect(feedback.warning).to eql(
|
88
|
+
'This is a top-10 common password'
|
89
|
+
)
|
90
|
+
expect(feedback.suggestions).to contain_exactly(
|
91
|
+
'Add another word or two. Uncommon words are better.',
|
92
|
+
"Capitalization doesn't help very much"
|
93
|
+
)
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'that are common and you capitalised the whole thing' do
|
97
|
+
feedback = tester.test('PASSWORD').feedback
|
98
|
+
|
99
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
100
|
+
expect(feedback.warning).to eql(
|
101
|
+
'This is a top-10 common password'
|
102
|
+
)
|
103
|
+
expect(feedback.suggestions).to contain_exactly(
|
104
|
+
'Add another word or two. Uncommon words are better.',
|
105
|
+
'All-uppercase is almost as easy to guess as all-lowercase'
|
106
|
+
)
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'that contain a common first name or last name' do
|
110
|
+
feedback = tester.test('jessica').feedback
|
111
|
+
|
112
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
113
|
+
expect(feedback.warning).to eql(
|
114
|
+
'Names and surnames by themselves are easy to guess'
|
115
|
+
)
|
116
|
+
expect(feedback.suggestions).to contain_exactly(
|
117
|
+
'Add another word or two. Uncommon words are better.'
|
118
|
+
)
|
119
|
+
|
120
|
+
feedback = tester.test('smith').feedback
|
121
|
+
|
122
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
123
|
+
expect(feedback.warning).to eql(
|
124
|
+
'Names and surnames by themselves are easy to guess'
|
125
|
+
)
|
126
|
+
expect(feedback.suggestions).to contain_exactly(
|
127
|
+
'Add another word or two. Uncommon words are better.'
|
128
|
+
)
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'that contain a common name and surname' do
|
132
|
+
feedback = tester.test('jessica smith').feedback
|
133
|
+
|
134
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
135
|
+
expect(feedback.warning).to eql(
|
136
|
+
'Common names and surnames are easy to guess'
|
137
|
+
)
|
138
|
+
expect(feedback.suggestions).to contain_exactly(
|
139
|
+
'Add another word or two. Uncommon words are better.'
|
140
|
+
)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe 'for spatial passwords' do
|
145
|
+
it 'that contain a straight keyboard row' do
|
146
|
+
feedback = tester.test('1qaz').feedback
|
147
|
+
|
148
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
149
|
+
expect(feedback.warning).to eql(
|
150
|
+
'Straight rows of keys are easy to guess'
|
151
|
+
)
|
152
|
+
expect(feedback.suggestions).to contain_exactly(
|
153
|
+
'Add another word or two. Uncommon words are better.',
|
154
|
+
'Use a longer keyboard pattern with more turns'
|
155
|
+
)
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'that contain a keyboard pattern with one turn' do
|
159
|
+
feedback = tester.test('zaqwer').feedback
|
160
|
+
|
161
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
162
|
+
expect(feedback.warning).to eql(
|
163
|
+
'Short keyboard patterns are easy to guess'
|
164
|
+
)
|
165
|
+
expect(feedback.suggestions).to contain_exactly(
|
166
|
+
'Add another word or two. Uncommon words are better.',
|
167
|
+
'Use a longer keyboard pattern with more turns'
|
168
|
+
)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'for passwords with repeated characters' do
|
173
|
+
feedback = tester.test('zzz').feedback
|
174
|
+
|
175
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
176
|
+
expect(feedback.warning).to eql(
|
177
|
+
'Repeats like "aaa" are easy to guess'
|
178
|
+
)
|
179
|
+
expect(feedback.suggestions).to contain_exactly(
|
180
|
+
'Add another word or two. Uncommon words are better.',
|
181
|
+
'Avoid repeated words and characters'
|
182
|
+
)
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'for passwords with sequential characters' do
|
186
|
+
feedback = tester.test('pqrpqrpqr').feedback
|
187
|
+
|
188
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
189
|
+
expect(feedback.warning).to eql(
|
190
|
+
'Sequences like abc or 6543 are easy to guess'
|
191
|
+
)
|
192
|
+
expect(feedback.suggestions).to contain_exactly(
|
193
|
+
'Add another word or two. Uncommon words are better.',
|
194
|
+
'Avoid sequences'
|
195
|
+
)
|
196
|
+
end
|
197
|
+
|
198
|
+
it 'for passwords containing dates' do
|
199
|
+
feedback = tester.test('testing02\12\1997').feedback
|
200
|
+
|
201
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
202
|
+
expect(feedback.warning).to eql(
|
203
|
+
'Dates are often easy to guess'
|
204
|
+
)
|
205
|
+
expect(feedback.suggestions).to contain_exactly(
|
206
|
+
'Add another word or two. Uncommon words are better.',
|
207
|
+
'Avoid dates and years that are associated with you'
|
208
|
+
)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
data/spec/matchers/date_spec.rb
CHANGED
@@ -15,16 +15,16 @@ describe Zxcvbn::Matchers::Date do
|
|
15
15
|
let(:matches) { matcher.matches(password) }
|
16
16
|
|
17
17
|
it 'finds matches' do
|
18
|
-
matches.
|
18
|
+
expect(matches).not_to be_empty
|
19
19
|
end
|
20
20
|
|
21
21
|
it 'finds the correct matches' do
|
22
|
-
matches.count.
|
23
|
-
matches[0].token.
|
24
|
-
matches[0].separator.
|
25
|
-
matches[0].day.
|
26
|
-
matches[0].month.
|
27
|
-
matches[0].year.
|
22
|
+
expect(matches.count).to eq 1
|
23
|
+
expect(matches[0].token).to eq %w[ 02 12 1997 ].join(separator)
|
24
|
+
expect(matches[0].separator).to eq separator
|
25
|
+
expect(matches[0].day).to eq 2
|
26
|
+
expect(matches[0].month).to eq 12
|
27
|
+
expect(matches[0].year).to eq 1997
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
@@ -103,7 +103,7 @@ describe Zxcvbn::Matchers::Date do
|
|
103
103
|
let(:matches) { matcher.matches('testing0.x.1997') }
|
104
104
|
|
105
105
|
it 'doesnt match' do
|
106
|
-
matches.
|
106
|
+
expect(matches).to be_empty
|
107
107
|
end
|
108
108
|
end
|
109
109
|
end
|
@@ -1,19 +1,30 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
2
4
|
|
3
5
|
describe Zxcvbn::Matchers::Dictionary do
|
4
|
-
|
5
|
-
let(:dictionary) { Zxcvbn::RANKED_DICTIONARIES['english'] }
|
6
|
+
subject(:matcher) { described_class.new("Test dictionary", dictionary) }
|
6
7
|
|
7
|
-
|
8
|
-
matches
|
9
|
-
matches.
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
8
|
+
describe "#matches" do
|
9
|
+
let(:matches) { matcher.matches(password) }
|
10
|
+
let(:matched_words) { matches.map(&:matched_word) }
|
11
|
+
|
12
|
+
context "Given a dictionary of English words" do
|
13
|
+
let(:dictionary) { Zxcvbn::Data.new.ranked_dictionaries["english"] }
|
14
|
+
let(:password) { "whatisinit" }
|
15
|
+
|
16
|
+
it "finds all the matches" do
|
17
|
+
expect(matched_words).to match_array %w[wha what ha hat a at tis i is sin i in i it]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "Given a custom dictionary" do
|
22
|
+
let(:dictionary) { Zxcvbn::DictionaryRanker.rank_dictionary(%w[test AB10CD]) }
|
23
|
+
let(:password) { "AB10CD" }
|
14
24
|
|
15
|
-
|
16
|
-
|
17
|
-
|
25
|
+
it "matches uppercase passwords with normalised dictionary entries" do
|
26
|
+
expect(matched_words).to match_array(%w[ab10cd])
|
27
|
+
end
|
28
|
+
end
|
18
29
|
end
|
19
|
-
end
|
30
|
+
end
|
@@ -5,11 +5,11 @@ describe Zxcvbn::Matchers::Digits do
|
|
5
5
|
let(:matches) { matcher.matches('testing1239xx9712') }
|
6
6
|
|
7
7
|
it 'sets the pattern name' do
|
8
|
-
matches.all? { |m| m.pattern == 'digits' }.
|
8
|
+
expect(matches.all? { |m| m.pattern == 'digits' }).to eql(true)
|
9
9
|
end
|
10
10
|
|
11
11
|
it 'finds the correct matches' do
|
12
|
-
matches.count.
|
13
|
-
matches[0].token.
|
12
|
+
expect(matches.count).to eq(2)
|
13
|
+
expect(matches[0].token).to eq '1239'
|
14
14
|
end
|
15
15
|
end
|
data/spec/matchers/l33t_spec.rb
CHANGED
@@ -2,12 +2,12 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Zxcvbn::Matchers::L33t do
|
4
4
|
let(:matcher) { described_class.new([dictionary_matcher]) }
|
5
|
-
let(:dictionary) { Zxcvbn::
|
5
|
+
let(:dictionary) { Zxcvbn::Data.new.ranked_dictionaries['english'] }
|
6
6
|
let(:dictionary_matcher) { Zxcvbn::Matchers::Dictionary.new('english', dictionary) }
|
7
7
|
|
8
8
|
describe '#relevant_l33t_substitutions' do
|
9
9
|
it 'returns relevant l33t substitutions' do
|
10
|
-
matcher.relevent_l33t_subtable('p@ssw1rd24').
|
10
|
+
expect(matcher.relevent_l33t_subtable('p@ssw1rd24')).to eq(
|
11
11
|
{'a' => ['4', '@'], 'i' => ['1'], 'l' => ['1'], 'z' => ['2']}
|
12
12
|
)
|
13
13
|
end
|
@@ -17,14 +17,14 @@ describe Zxcvbn::Matchers::L33t do
|
|
17
17
|
context 'with 2 possible substitutions' do
|
18
18
|
it 'returns the correct possible substitutions' do
|
19
19
|
substitutions = {'a' => ['@'], 'i' => ['1']}
|
20
|
-
matcher.l33t_subs(substitutions).
|
20
|
+
expect(matcher.l33t_subs(substitutions)).to match_array([
|
21
21
|
{'@' => 'a', '1' => 'i'}
|
22
22
|
])
|
23
23
|
end
|
24
24
|
|
25
25
|
it 'returns the correct possible substitutions with multiple options' do
|
26
26
|
substitutions = {'a' => ['@', '4'], 'i' => ['1']}
|
27
|
-
matcher.l33t_subs(substitutions).
|
27
|
+
expect(matcher.l33t_subs(substitutions)).to match_array([
|
28
28
|
{'@' => 'a', '1' => 'i'},
|
29
29
|
{'4' => 'a', '1' => 'i'}
|
30
30
|
])
|
@@ -34,7 +34,7 @@ describe Zxcvbn::Matchers::L33t do
|
|
34
34
|
context 'with 3 possible substitutions' do
|
35
35
|
it 'returns the correct possible substitutions' do
|
36
36
|
substitutions = {'a' => ['@'], 'i' => ['1'], 'z' => ['3']}
|
37
|
-
matcher.l33t_subs(substitutions).
|
37
|
+
expect(matcher.l33t_subs(substitutions)).to match_array([
|
38
38
|
{'@' => 'a', '1' => 'i', '3' => 'z'}
|
39
39
|
])
|
40
40
|
end
|
@@ -43,7 +43,7 @@ describe Zxcvbn::Matchers::L33t do
|
|
43
43
|
context 'with 4 possible substitutions' do
|
44
44
|
it 'returns the correct possible substitutions' do
|
45
45
|
substitutions = {'a' => ['@'], 'i' => ['1'], 'z' => ['3'], 'b' => ['8']}
|
46
|
-
matcher.l33t_subs(substitutions).
|
46
|
+
expect(matcher.l33t_subs(substitutions)).to match_array([
|
47
47
|
{'@' => 'a', '1' => 'i', '3' => 'z', '8' => 'b'}
|
48
48
|
])
|
49
49
|
end
|
@@ -51,12 +51,14 @@ describe Zxcvbn::Matchers::L33t do
|
|
51
51
|
end
|
52
52
|
|
53
53
|
describe '#matches' do
|
54
|
-
|
55
|
-
|
56
|
-
|
54
|
+
subject(:matches) { matcher.matches('p@ssword') }
|
55
|
+
|
56
|
+
it "doesn't find 'password' because it's not in english.txt" do
|
57
|
+
expect(matches.map(&:matched_word)).not_to include "password"
|
58
|
+
end
|
57
59
|
|
58
60
|
it 'finds the correct matches' do
|
59
|
-
matches.map(&:matched_word).
|
61
|
+
expect(matches.map(&:matched_word)).to match_array([
|
60
62
|
'pas',
|
61
63
|
'a',
|
62
64
|
'as',
|
@@ -65,7 +67,7 @@ describe Zxcvbn::Matchers::L33t do
|
|
65
67
|
end
|
66
68
|
|
67
69
|
it 'sets the token correctly on those matches' do
|
68
|
-
matches.map(&:token).
|
70
|
+
expect(matches.map(&:token)).to match_array([
|
69
71
|
'p@s',
|
70
72
|
'@',
|
71
73
|
'@s',
|
@@ -74,7 +76,7 @@ describe Zxcvbn::Matchers::L33t do
|
|
74
76
|
end
|
75
77
|
|
76
78
|
it 'sets the substituions used' do
|
77
|
-
matches.map(&:sub).
|
79
|
+
expect(matches.map(&:sub)).to match_array([
|
78
80
|
{'@' => 'a'},
|
79
81
|
{'@' => 'a'},
|
80
82
|
{'@' => 'a'},
|
@@ -82,4 +84,4 @@ describe Zxcvbn::Matchers::L33t do
|
|
82
84
|
])
|
83
85
|
end
|
84
86
|
end
|
85
|
-
end
|
87
|
+
end
|