zxcvbn-ruby 1.1.0 → 1.2.1
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/CHANGELOG.md +29 -3
- data/README.md +5 -5
- data/lib/zxcvbn/clock.rb +10 -0
- data/lib/zxcvbn/password_strength.rb +3 -3
- data/lib/zxcvbn/version.rb +3 -1
- metadata +9 -77
- data/.gitignore +0 -18
- data/.rspec +0 -1
- data/.travis.yml +0 -12
- data/CODE_OF_CONDUCT.md +0 -130
- data/Gemfile +0 -10
- data/Guardfile +0 -26
- data/Rakefile +0 -22
- data/spec/dictionary_ranker_spec.rb +0 -12
- data/spec/feedback_giver_spec.rb +0 -212
- data/spec/matchers/date_spec.rb +0 -109
- data/spec/matchers/dictionary_spec.rb +0 -30
- data/spec/matchers/digits_spec.rb +0 -15
- data/spec/matchers/l33t_spec.rb +0 -87
- data/spec/matchers/repeat_spec.rb +0 -18
- data/spec/matchers/sequences_spec.rb +0 -21
- data/spec/matchers/spatial_spec.rb +0 -20
- data/spec/matchers/year_spec.rb +0 -15
- data/spec/omnimatch_spec.rb +0 -24
- data/spec/scorer_spec.rb +0 -5
- data/spec/scoring/crack_time_spec.rb +0 -106
- data/spec/scoring/entropy_spec.rb +0 -216
- data/spec/scoring/math_spec.rb +0 -135
- data/spec/spec_helper.rb +0 -54
- data/spec/support/js_helpers.rb +0 -35
- data/spec/support/js_source/adjacency_graphs.js +0 -8
- data/spec/support/js_source/compiled.js +0 -1188
- data/spec/support/js_source/frequency_lists.js +0 -10
- data/spec/support/js_source/init.coffee +0 -63
- data/spec/support/js_source/init.js +0 -95
- data/spec/support/js_source/matching.coffee +0 -444
- data/spec/support/js_source/matching.js +0 -685
- data/spec/support/js_source/scoring.coffee +0 -270
- data/spec/support/js_source/scoring.js +0 -390
- data/spec/support/matcher.rb +0 -35
- data/spec/tester_spec.rb +0 -99
- data/spec/zxcvbn_spec.rb +0 -24
- data/zxcvbn-ruby.gemspec +0 -31
data/spec/feedback_giver_spec.rb
DELETED
|
@@ -1,212 +0,0 @@
|
|
|
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
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
describe Zxcvbn::Matchers::Date do
|
|
4
|
-
let(:matcher) { subject }
|
|
5
|
-
|
|
6
|
-
{
|
|
7
|
-
' ' => 'testing02 12 1997',
|
|
8
|
-
'-' => 'testing02-12-1997',
|
|
9
|
-
'/' => 'testing02/12/1997',
|
|
10
|
-
'\\' => 'testing02\12\1997',
|
|
11
|
-
'_' => 'testing02_12_1997',
|
|
12
|
-
'.' => 'testing02.12.1997'
|
|
13
|
-
}.each do |separator, password|
|
|
14
|
-
context "with #{separator} seperator" do
|
|
15
|
-
let(:matches) { matcher.matches(password) }
|
|
16
|
-
|
|
17
|
-
it 'finds matches' do
|
|
18
|
-
expect(matches).not_to be_empty
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
it 'finds the correct matches' do
|
|
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
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# context 'without separator' do
|
|
33
|
-
# context '5 digit date' do
|
|
34
|
-
# let(:matches) { matcher.matches('13192boo') }
|
|
35
|
-
|
|
36
|
-
# it 'finds matches' do
|
|
37
|
-
# matches.should_not be_empty
|
|
38
|
-
# end
|
|
39
|
-
|
|
40
|
-
# it 'finds the correct matches' do
|
|
41
|
-
# matches.count.should eq 2
|
|
42
|
-
# matches[0].token.should eq '13192'
|
|
43
|
-
# matches[0].separator.should eq ''
|
|
44
|
-
# matches[0].day.should eq 13
|
|
45
|
-
# matches[0].month.should eq 1
|
|
46
|
-
# matches[0].year.should eq 1992
|
|
47
|
-
|
|
48
|
-
# matches[1].token.should eq '13192'
|
|
49
|
-
# matches[1].separator.should eq ''
|
|
50
|
-
# matches[1].day.should eq 31
|
|
51
|
-
# matches[1].month.should eq 1
|
|
52
|
-
# matches[1].year.should eq 1992
|
|
53
|
-
# end
|
|
54
|
-
# end
|
|
55
|
-
# end
|
|
56
|
-
|
|
57
|
-
# describe '#extract_dates' do
|
|
58
|
-
# {
|
|
59
|
-
# '1234' => [
|
|
60
|
-
# {:year => 2012, :month => 3, :day => 4},
|
|
61
|
-
# {:year => 1934, :month => 2, :day => 1},
|
|
62
|
-
# {:year => 1934, :month => 1, :day => 2}
|
|
63
|
-
# ],
|
|
64
|
-
# '12345' => [
|
|
65
|
-
# {:year => 1945, :month => 3, :day => 12},
|
|
66
|
-
# {:year => 1945, :month => 12, :day => 3},
|
|
67
|
-
# {:year => 1945, :month => 1, :day => 23}
|
|
68
|
-
# ],
|
|
69
|
-
# '54321' => [
|
|
70
|
-
# {:year => 1954, :month => 3, :day => 21}
|
|
71
|
-
# ],
|
|
72
|
-
# '151290' => [
|
|
73
|
-
# {:year => 1990, :month => 12, :day => 15}
|
|
74
|
-
# ],
|
|
75
|
-
# '901215' => [
|
|
76
|
-
# {:year => 1990, :month => 12, :day => 15}
|
|
77
|
-
# ],
|
|
78
|
-
# '1511990' => [
|
|
79
|
-
# {:year => 1990, :month => 1, :day => 15}
|
|
80
|
-
# ]
|
|
81
|
-
# }.each do |token, expected_candidates|
|
|
82
|
-
# it "finds the correct candidates for #{token}" do
|
|
83
|
-
# matcher.extract_dates(token).should match_array expected_candidates
|
|
84
|
-
# end
|
|
85
|
-
# end
|
|
86
|
-
# end
|
|
87
|
-
|
|
88
|
-
# describe '#expand_year' do
|
|
89
|
-
# {
|
|
90
|
-
# 12 => 2012,
|
|
91
|
-
# 01 => 2001,
|
|
92
|
-
# 15 => 2015,
|
|
93
|
-
# 19 => 2019,
|
|
94
|
-
# 20 => 1920
|
|
95
|
-
# }.each do |small, expanded|
|
|
96
|
-
# it "expands #{small} to #{expanded}" do
|
|
97
|
-
# matcher.expand_year(small).should eq expanded
|
|
98
|
-
# end
|
|
99
|
-
# end
|
|
100
|
-
# end
|
|
101
|
-
|
|
102
|
-
context 'invalid date' do
|
|
103
|
-
let(:matches) { matcher.matches('testing0.x.1997') }
|
|
104
|
-
|
|
105
|
-
it 'doesnt match' do
|
|
106
|
-
expect(matches).to be_empty
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
end
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "spec_helper"
|
|
4
|
-
|
|
5
|
-
describe Zxcvbn::Matchers::Dictionary do
|
|
6
|
-
subject(:matcher) { described_class.new("Test dictionary", dictionary) }
|
|
7
|
-
|
|
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" }
|
|
24
|
-
|
|
25
|
-
it "matches uppercase passwords with normalised dictionary entries" do
|
|
26
|
-
expect(matched_words).to match_array(%w[ab10cd])
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
describe Zxcvbn::Matchers::Digits do
|
|
4
|
-
let(:matcher) { subject }
|
|
5
|
-
let(:matches) { matcher.matches('testing1239xx9712') }
|
|
6
|
-
|
|
7
|
-
it 'sets the pattern name' do
|
|
8
|
-
expect(matches.all? { |m| m.pattern == 'digits' }).to eql(true)
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
it 'finds the correct matches' do
|
|
12
|
-
expect(matches.count).to eq(2)
|
|
13
|
-
expect(matches[0].token).to eq '1239'
|
|
14
|
-
end
|
|
15
|
-
end
|
data/spec/matchers/l33t_spec.rb
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
describe Zxcvbn::Matchers::L33t do
|
|
4
|
-
let(:matcher) { described_class.new([dictionary_matcher]) }
|
|
5
|
-
let(:dictionary) { Zxcvbn::Data.new.ranked_dictionaries['english'] }
|
|
6
|
-
let(:dictionary_matcher) { Zxcvbn::Matchers::Dictionary.new('english', dictionary) }
|
|
7
|
-
|
|
8
|
-
describe '#relevant_l33t_substitutions' do
|
|
9
|
-
it 'returns relevant l33t substitutions' do
|
|
10
|
-
expect(matcher.relevent_l33t_subtable('p@ssw1rd24')).to eq(
|
|
11
|
-
{'a' => ['4', '@'], 'i' => ['1'], 'l' => ['1'], 'z' => ['2']}
|
|
12
|
-
)
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
describe 'possible l33t substitutions' do
|
|
17
|
-
context 'with 2 possible substitutions' do
|
|
18
|
-
it 'returns the correct possible substitutions' do
|
|
19
|
-
substitutions = {'a' => ['@'], 'i' => ['1']}
|
|
20
|
-
expect(matcher.l33t_subs(substitutions)).to match_array([
|
|
21
|
-
{'@' => 'a', '1' => 'i'}
|
|
22
|
-
])
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
it 'returns the correct possible substitutions with multiple options' do
|
|
26
|
-
substitutions = {'a' => ['@', '4'], 'i' => ['1']}
|
|
27
|
-
expect(matcher.l33t_subs(substitutions)).to match_array([
|
|
28
|
-
{'@' => 'a', '1' => 'i'},
|
|
29
|
-
{'4' => 'a', '1' => 'i'}
|
|
30
|
-
])
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
context 'with 3 possible substitutions' do
|
|
35
|
-
it 'returns the correct possible substitutions' do
|
|
36
|
-
substitutions = {'a' => ['@'], 'i' => ['1'], 'z' => ['3']}
|
|
37
|
-
expect(matcher.l33t_subs(substitutions)).to match_array([
|
|
38
|
-
{'@' => 'a', '1' => 'i', '3' => 'z'}
|
|
39
|
-
])
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
context 'with 4 possible substitutions' do
|
|
44
|
-
it 'returns the correct possible substitutions' do
|
|
45
|
-
substitutions = {'a' => ['@'], 'i' => ['1'], 'z' => ['3'], 'b' => ['8']}
|
|
46
|
-
expect(matcher.l33t_subs(substitutions)).to match_array([
|
|
47
|
-
{'@' => 'a', '1' => 'i', '3' => 'z', '8' => 'b'}
|
|
48
|
-
])
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
describe '#matches' do
|
|
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
|
|
59
|
-
|
|
60
|
-
it 'finds the correct matches' do
|
|
61
|
-
expect(matches.map(&:matched_word)).to match_array([
|
|
62
|
-
'pas',
|
|
63
|
-
'a',
|
|
64
|
-
'as',
|
|
65
|
-
'ass'
|
|
66
|
-
])
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
it 'sets the token correctly on those matches' do
|
|
70
|
-
expect(matches.map(&:token)).to match_array([
|
|
71
|
-
'p@s',
|
|
72
|
-
'@',
|
|
73
|
-
'@s',
|
|
74
|
-
'@ss'
|
|
75
|
-
])
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
it 'sets the substituions used' do
|
|
79
|
-
expect(matches.map(&:sub)).to match_array([
|
|
80
|
-
{'@' => 'a'},
|
|
81
|
-
{'@' => 'a'},
|
|
82
|
-
{'@' => 'a'},
|
|
83
|
-
{'@' => 'a'}
|
|
84
|
-
])
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
end
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
describe Zxcvbn::Matchers::Repeat do
|
|
4
|
-
let(:matcher) { subject }
|
|
5
|
-
let(:matches) { matcher.matches('bbbbbtestingaaa') }
|
|
6
|
-
|
|
7
|
-
it 'sets the pattern name' do
|
|
8
|
-
expect(matches.all? { |m| m.pattern == 'repeat' }).to eql(true)
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
it 'finds the repeated patterns' do
|
|
12
|
-
expect(matches.count).to eq 2
|
|
13
|
-
expect(matches[0].token).to eq 'bbbbb'
|
|
14
|
-
expect(matches[0].repeated_char).to eq 'b'
|
|
15
|
-
expect(matches[1].token).to eq 'aaa'
|
|
16
|
-
expect(matches[1].repeated_char).to eq 'a'
|
|
17
|
-
end
|
|
18
|
-
end
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
describe Zxcvbn::Matchers::Sequences do
|
|
4
|
-
let(:matcher) { subject }
|
|
5
|
-
let(:matches) { matcher.matches('abcde87654') }
|
|
6
|
-
|
|
7
|
-
it 'sets the pattern name' do
|
|
8
|
-
expect(matches.all? { |m| m.pattern == 'sequence' }).to eql(true)
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
it 'finds the correct matches' do
|
|
12
|
-
expect(matches.count).to eq(2)
|
|
13
|
-
expect(matches[0].token).to eq 'abcde'
|
|
14
|
-
expect(matches[1].token).to eq '87654'
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
it 'finds overlapping matches' do
|
|
18
|
-
matches = matcher.matches('abcba')
|
|
19
|
-
expect(matches.map(&:token)).to eq ['abc', 'cba']
|
|
20
|
-
end
|
|
21
|
-
end
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
describe Zxcvbn::Matchers::Spatial do
|
|
4
|
-
let(:matcher) { Zxcvbn::Matchers::Spatial.new(graphs) }
|
|
5
|
-
let(:graphs) { Zxcvbn::Data.new.adjacency_graphs }
|
|
6
|
-
|
|
7
|
-
describe '#matches' do
|
|
8
|
-
let(:matches) { matcher.matches('rtyikm') }
|
|
9
|
-
|
|
10
|
-
it 'finds the correct of matches' do
|
|
11
|
-
expect(matches.count).to eq 3
|
|
12
|
-
expect(matches[0].token).to eq 'rty'
|
|
13
|
-
expect(matches[0].graph).to eq 'qwerty'
|
|
14
|
-
expect(matches[1].token).to eq 'ikm'
|
|
15
|
-
expect(matches[1].graph).to eq 'qwerty'
|
|
16
|
-
expect(matches[2].token).to eq 'yik'
|
|
17
|
-
expect(matches[2].graph).to eq 'dvorak'
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
end
|
data/spec/matchers/year_spec.rb
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
describe Zxcvbn::Matchers::Year do
|
|
4
|
-
let(:matcher) { subject }
|
|
5
|
-
let(:matches) { matcher.matches('testing1998') }
|
|
6
|
-
|
|
7
|
-
it 'sets the pattern name' do
|
|
8
|
-
expect(matches.all? { |m| m.pattern == 'year' }).to eql(true)
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
it 'finds the correct matches' do
|
|
12
|
-
expect(matches.count).to eq(1)
|
|
13
|
-
expect(matches[0].token).to eq '1998'
|
|
14
|
-
end
|
|
15
|
-
end
|
data/spec/omnimatch_spec.rb
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
describe Zxcvbn::Omnimatch do
|
|
4
|
-
before(:all) do
|
|
5
|
-
@omnimatch = described_class.new(Zxcvbn::Data.new)
|
|
6
|
-
end
|
|
7
|
-
|
|
8
|
-
def omnimatch(password)
|
|
9
|
-
@omnimatch.matches(password)
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def js_omnimatch(password)
|
|
13
|
-
run_js(%'omnimatch("#{password}")')
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
TEST_PASSWORDS.each do |password|
|
|
17
|
-
it "gives back the same results for #{password}" do
|
|
18
|
-
js_results = js_omnimatch(password)
|
|
19
|
-
ruby_results = omnimatch(password)
|
|
20
|
-
|
|
21
|
-
expect(ruby_results).to match_js_results js_results
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
data/spec/scorer_spec.rb
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
describe Zxcvbn::CrackTime do
|
|
4
|
-
include Zxcvbn::CrackTime
|
|
5
|
-
|
|
6
|
-
describe '#entropy_to_crack_time' do
|
|
7
|
-
specify do
|
|
8
|
-
expect(entropy_to_crack_time(15.433976574415976)).to eq 2.2134000000000014
|
|
9
|
-
end
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
describe '#crack_time_to_score' do
|
|
13
|
-
context 'crack time less than 10 to the power 2' do
|
|
14
|
-
it 'returns 0' do
|
|
15
|
-
expect(crack_time_to_score(90)).to eq 0
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
context 'crack time in between 10**2 and 10**4' do
|
|
20
|
-
it 'returns 1' do
|
|
21
|
-
expect(crack_time_to_score(5000)).to eq 1
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
context 'crack time in between 10**4 and 10**6' do
|
|
26
|
-
it 'returns 2' do
|
|
27
|
-
expect(crack_time_to_score(500_000)).to eq 2
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
context 'crack time in between 10**6 and 10**8' do
|
|
32
|
-
it 'returns 3' do
|
|
33
|
-
expect(crack_time_to_score(50_000_000)).to eq 3
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
context 'crack time above 10**8' do
|
|
38
|
-
it 'returns 4' do
|
|
39
|
-
expect(crack_time_to_score(110_000_000)).to eq 4
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
describe '#display_time' do
|
|
45
|
-
let(:minute_to_seconds) { 60 }
|
|
46
|
-
let(:hour_to_seconds) { minute_to_seconds * 60 }
|
|
47
|
-
let(:day_to_seconds) { hour_to_seconds * 24 }
|
|
48
|
-
let(:month_to_seconds) { day_to_seconds * 31 }
|
|
49
|
-
let(:year_to_seconds) { month_to_seconds * 12 }
|
|
50
|
-
let(:century_to_seconds) { year_to_seconds * 100 }
|
|
51
|
-
|
|
52
|
-
context 'when less than a minute' do
|
|
53
|
-
it 'should return instant' do
|
|
54
|
-
[0, minute_to_seconds - 1].each do |seconds|
|
|
55
|
-
expect(display_time(seconds)).to eql 'instant'
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
context 'when less than an hour' do
|
|
61
|
-
it 'should return a readable time in minutes' do
|
|
62
|
-
[60, (hour_to_seconds - 1)].each do |seconds|
|
|
63
|
-
expect(display_time(seconds)).to match(/[0-9]+ minutes$/)
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
context 'when less than a day' do
|
|
69
|
-
it 'should return a readable time in hours' do
|
|
70
|
-
[hour_to_seconds, (day_to_seconds - 1)].each do |seconds|
|
|
71
|
-
expect(display_time(seconds)).to match(/[0-9]+ hours$/)
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
context 'when less than 31 days' do
|
|
77
|
-
it 'should return a readable time in days' do
|
|
78
|
-
[day_to_seconds, month_to_seconds - 1].each do |seconds|
|
|
79
|
-
expect(display_time(seconds)).to match(/[0-9]+ days$/)
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
context 'when less than 1 year' do
|
|
85
|
-
it 'should return a readable time in days' do
|
|
86
|
-
[month_to_seconds, (year_to_seconds - 1)].each do |seconds|
|
|
87
|
-
expect(display_time(seconds)).to match(/[0-9]+ months$/)
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
context 'when less than a century' do
|
|
93
|
-
it 'should return a readable time in days' do
|
|
94
|
-
[year_to_seconds, (century_to_seconds - 1)].each do |seconds|
|
|
95
|
-
expect(display_time(seconds)).to match(/[0-9]+ years$/)
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
context 'when a century or more' do
|
|
101
|
-
it 'should return centuries' do
|
|
102
|
-
expect(display_time(century_to_seconds)).to eql 'centuries'
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
end
|