zxcvbn-ruby 1.2.0 → 1.2.2
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 +19 -1
- data/README.md +1 -1
- data/lib/zxcvbn/clock.rb +11 -0
- data/lib/zxcvbn/crack_time.rb +4 -2
- data/lib/zxcvbn/data.rb +6 -4
- data/lib/zxcvbn/dictionary_ranker.rb +2 -0
- data/lib/zxcvbn/entropy.rb +38 -33
- data/lib/zxcvbn/feedback.rb +2 -0
- data/lib/zxcvbn/feedback_giver.rb +2 -0
- data/lib/zxcvbn/match.rb +2 -0
- data/lib/zxcvbn/matchers/date.rb +11 -8
- data/lib/zxcvbn/matchers/dictionary.rb +3 -1
- data/lib/zxcvbn/matchers/digits.rb +2 -0
- data/lib/zxcvbn/matchers/l33t.rb +6 -2
- data/lib/zxcvbn/matchers/new_l33t.rb +7 -4
- data/lib/zxcvbn/matchers/regex_helpers.rb +2 -0
- data/lib/zxcvbn/matchers/repeat.rb +3 -1
- data/lib/zxcvbn/matchers/sequences.rb +4 -2
- data/lib/zxcvbn/matchers/spatial.rb +5 -3
- data/lib/zxcvbn/matchers/year.rb +3 -1
- data/lib/zxcvbn/math.rb +3 -0
- data/lib/zxcvbn/omnimatch.rb +3 -1
- data/lib/zxcvbn/password_strength.rb +5 -3
- data/lib/zxcvbn/score.rb +3 -1
- data/lib/zxcvbn/scorer.rb +11 -7
- data/lib/zxcvbn/version.rb +1 -1
- data/lib/zxcvbn.rb +2 -0
- metadata +6 -102
- data/.github/workflows/ci.yml +0 -23
- data/.gitignore +0 -18
- data/.rspec +0 -1
- 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 -34
- 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 -33
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
|
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
describe Zxcvbn::Entropy do
|
|
4
|
-
include Zxcvbn::Math
|
|
5
|
-
|
|
6
|
-
let(:entropy) {
|
|
7
|
-
Class.new do
|
|
8
|
-
include Zxcvbn::Entropy
|
|
9
|
-
def data
|
|
10
|
-
Zxcvbn::Data.new
|
|
11
|
-
end
|
|
12
|
-
end.new
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
describe '#repeat_entropy' do
|
|
16
|
-
it 'returns the correct value' do
|
|
17
|
-
match = Zxcvbn::Match.new(:token => '2222')
|
|
18
|
-
expect(entropy.repeat_entropy(match)).to eq 5.321928094887363
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
describe '#sequence_entropy' do
|
|
23
|
-
let(:match) { Zxcvbn::Match.new(:token => token, :ascending => true) }
|
|
24
|
-
|
|
25
|
-
{'a' => 'abcdefg', '1' => '1234567'}.each do |first_char, token|
|
|
26
|
-
context "when the first char is #{first_char}" do
|
|
27
|
-
let(:token) { token }
|
|
28
|
-
|
|
29
|
-
it 'returns the correct value' do
|
|
30
|
-
expect(entropy.sequence_entropy(match)).to eq 3.807354922057604
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
context 'when the first character is a digit' do
|
|
36
|
-
let(:token) { '23456' }
|
|
37
|
-
|
|
38
|
-
it 'returns the correct value' do
|
|
39
|
-
expect(entropy.sequence_entropy(match)).to eq 5.643856189774725
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
context 'when the first character is a lowercase letter' do
|
|
44
|
-
let(:token) { 'bcdef' }
|
|
45
|
-
|
|
46
|
-
it 'returns the correct value' do
|
|
47
|
-
expect(entropy.sequence_entropy(match)).to eq 7.022367813028454
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
context 'when the first character is an uppercase letter' do
|
|
52
|
-
let(:token) { 'BCDEF' }
|
|
53
|
-
|
|
54
|
-
it 'returns the correct value' do
|
|
55
|
-
expect(entropy.sequence_entropy(match)).to eq 8.022367813028454
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
context 'when the match is ascending' do
|
|
60
|
-
before { match.ascending = false }
|
|
61
|
-
let(:token) { 'bcdef' }
|
|
62
|
-
|
|
63
|
-
it 'returns the correct value' do
|
|
64
|
-
expect(entropy.sequence_entropy(match)).to eq 8.022367813028454
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
describe '#digits_entropy' do
|
|
70
|
-
it 'returns the correct value' do
|
|
71
|
-
match = Zxcvbn::Match.new(:token => '12345678')
|
|
72
|
-
expect(entropy.digits_entropy(match)).to eq 26.5754247590989
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
describe '#year_entropy' do
|
|
77
|
-
it 'returns the correct value' do
|
|
78
|
-
expect(entropy.year_entropy(nil)).to eq 6.894817763307944
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
describe '#date_entropy' do
|
|
83
|
-
context 'with a two digit year' do
|
|
84
|
-
it 'returns the correct value' do
|
|
85
|
-
match = Zxcvbn::Match.new(:year => 98)
|
|
86
|
-
expect(entropy.date_entropy(match)).to eq 15.183015000882756
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
context 'with a four digit year' do
|
|
91
|
-
it 'returns the correct value' do
|
|
92
|
-
match = Zxcvbn::Match.new(:year => 2012)
|
|
93
|
-
expect(entropy.date_entropy(match)).to eq 15.433976574415976
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
context 'with a separator' do
|
|
98
|
-
it 'returns the correct value' do
|
|
99
|
-
match = Zxcvbn::Match.new(:year => 2012, :separator => '/')
|
|
100
|
-
expect(entropy.date_entropy(match)).to eq 17.433976574415976
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
describe '#dictionary_entropy' do
|
|
106
|
-
let(:match) { Zxcvbn::Match.new(:token => token, :rank => rank, :l33t => l33t, :sub => sub) }
|
|
107
|
-
let(:l33t) { false }
|
|
108
|
-
let(:sub) { {} }
|
|
109
|
-
let(:calculated_entropy) { entropy.dictionary_entropy(match) }
|
|
110
|
-
|
|
111
|
-
context 'a simple dictionary word, all lower case and no l33t subs' do
|
|
112
|
-
let(:token) { 'you' }
|
|
113
|
-
let(:rank) { 1 }
|
|
114
|
-
|
|
115
|
-
specify { expect(calculated_entropy).to eq 0 }
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
context 'with all upper case characters' do
|
|
119
|
-
let(:token) { 'YOU' }
|
|
120
|
-
let(:rank) { 1 }
|
|
121
|
-
|
|
122
|
-
specify { expect(calculated_entropy).to eq 1 }
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
context 'starting with uppercase' do
|
|
126
|
-
let(:token) { 'You' }
|
|
127
|
-
let(:rank) { 1 }
|
|
128
|
-
|
|
129
|
-
specify { expect(calculated_entropy).to eq 1 }
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
context 'starting with uppercase' do
|
|
133
|
-
let(:token) { 'yoU' }
|
|
134
|
-
let(:rank) { 1 }
|
|
135
|
-
|
|
136
|
-
specify { expect(calculated_entropy).to eq 1 }
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
context 'mixed upper and lower' do
|
|
140
|
-
let(:token) { 'tEsTiNg' }
|
|
141
|
-
let(:rank) { 1 }
|
|
142
|
-
|
|
143
|
-
specify { expect(calculated_entropy).to eq 6 }
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
context 'starting with digits' do
|
|
147
|
-
let(:token) { '12345' }
|
|
148
|
-
let(:rank) { 1 }
|
|
149
|
-
|
|
150
|
-
specify { expect(calculated_entropy).to eq 0 }
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
context 'extra l33t entropy' do
|
|
154
|
-
let(:token) { 'p3rs0n' }
|
|
155
|
-
let(:rank) { 1 }
|
|
156
|
-
let(:l33t) { true }
|
|
157
|
-
let(:sub) { {'3' => 'e', '0' => 'o'} }
|
|
158
|
-
|
|
159
|
-
specify { expect(calculated_entropy).to eq 1 }
|
|
160
|
-
end
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
describe '#spatial_entropy' do
|
|
164
|
-
let(:match) { Zxcvbn::Match.new(:token => '123wsclf', :turns => 1) }
|
|
165
|
-
|
|
166
|
-
context 'when keyboard is qwerty' do
|
|
167
|
-
it 'should return the correct entropy' do
|
|
168
|
-
match.graph = 'qwerty'
|
|
169
|
-
|
|
170
|
-
expect(entropy.spatial_entropy(match)).to eql 11.562242424221074
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
context 'when keyboard is dvorak' do
|
|
175
|
-
it 'should return the correct entropy' do
|
|
176
|
-
match.graph = 'dvorak'
|
|
177
|
-
|
|
178
|
-
expect(entropy.spatial_entropy(match)).to eql 11.562242424221074
|
|
179
|
-
end
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
context 'when keyboard is not qwerty or dvorak' do
|
|
183
|
-
it 'should return the correct entropy' do
|
|
184
|
-
match.graph = 'keypad'
|
|
185
|
-
|
|
186
|
-
expect(entropy.spatial_entropy(match)).to eql 9.05528243550119
|
|
187
|
-
end
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
context 'when match includes several turns' do
|
|
191
|
-
it 'should return the correct entropy' do
|
|
192
|
-
match.turns = 5
|
|
193
|
-
|
|
194
|
-
expect(entropy.spatial_entropy(match)).to eql 21.761397858718993
|
|
195
|
-
end
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
context 'when match includes shifted count' do
|
|
199
|
-
it 'should return the correct entropy' do
|
|
200
|
-
match.shiffted_count = 5
|
|
201
|
-
|
|
202
|
-
expect(entropy.spatial_entropy(match)).to eql 9.05528243550119
|
|
203
|
-
end
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
context 'when match includes shifted count and several turns' do
|
|
207
|
-
it 'should return the correct entropy' do
|
|
208
|
-
match.shiffted_count = 5
|
|
209
|
-
match.turns = 5
|
|
210
|
-
|
|
211
|
-
expect(entropy.spatial_entropy(match)).to eql 21.761397858718993
|
|
212
|
-
end
|
|
213
|
-
end
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
end
|