zxcvbn-ruby 0.1.0 → 0.1.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 +7 -0
- data/.travis.yml +15 -0
- data/Gemfile +5 -1
- data/README.md +9 -2
- data/Rakefile +5 -1
- data/lib/zxcvbn/version.rb +1 -1
- data/spec/dictionary_ranker_spec.rb +2 -2
- data/spec/matchers/date_spec.rb +8 -8
- data/spec/matchers/dictionary_spec.rb +3 -3
- data/spec/matchers/digits_spec.rb +3 -3
- data/spec/matchers/l33t_spec.rb +8 -8
- data/spec/matchers/repeat_spec.rb +6 -6
- data/spec/matchers/sequences_spec.rb +5 -5
- data/spec/matchers/spatial_spec.rb +7 -7
- data/spec/matchers/year_spec.rb +3 -3
- data/spec/omnimatch_spec.rb +1 -1
- data/spec/scoring/crack_time_spec.rb +13 -13
- data/spec/scoring/entropy_spec.rb +24 -24
- data/spec/scoring/math_spec.rb +18 -18
- data/spec/support/matcher.rb +1 -1
- data/spec/tester_spec.rb +14 -14
- data/zxcvbn-ruby.gemspec +3 -3
- metadata +14 -25
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 69db86071c8f195cde23fbde7f8bf03cf67fcf0a
|
4
|
+
data.tar.gz: 96b11de7f15abb43d75e6675cf5881ec736417fb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c0573f620c740b0f0154c6ce39c9e0c2f6acf164584f3af8612f3eed690aaa9b1de98e032bfc1b21127175cdb36e4f48b94420214927373cecc735758797b082
|
7
|
+
data.tar.gz: a5442632ad5ac404bda9e798d1825c87cf20afdfd0be93bd07243afde1444cd5f5bdae402ab138e74548c2e51385a2180ca0846686923eac9e5f0e4e245ac7a2
|
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# zxcvbn-ruby
|
1
|
+
# zxcvbn-ruby [](https://travis-ci.org/envato/zxcvbn-ruby)
|
2
2
|
|
3
3
|
Ruby port of [zxcvbn.js](https://github.com/dropbox/zxcvbn)
|
4
4
|
|
@@ -10,6 +10,8 @@ Gemfile:
|
|
10
10
|
gem 'zxcvbn-ruby', require: 'zxcvbn'
|
11
11
|
```
|
12
12
|
|
13
|
+
[RubyGems.org gem replacement vulnerability and mitigation](http://blog.rubygems.org/2016/04/06/gem-replacement-vulnerability-and-mitigation.html): not affected.
|
14
|
+
|
13
15
|
Example usage:
|
14
16
|
|
15
17
|
```ruby
|
@@ -36,4 +38,9 @@ $ irb
|
|
36
38
|
=> #<Zxcvbn::Score:0x007fd4689c1168 @entropy=29.782, @crack_time=46159.451, @crack_time_display="14 hours", @score=2, @match_sequence=[#<Zxcvbn::Match pattern="spatial", i=0, j=15, token="asdfghju7654rewq", graph="qwerty", turns=5, shifted_count=0, entropy=29.7820508329166>], password"asdfghju7654rewq", calc_time0.00526
|
37
39
|
>> tester.test('@lfred2004', ['alfred'])
|
38
40
|
=> #<Zxcvbn::Score:0x007fd4689c1168 @entropy=29.782, @crack_time=46159.451, @crack_time_display="14 hours", @score=2, @match_sequence=[#<Zxcvbn::Match pattern="spatial", i=0, j=15, token="asdfghju7654rewq", graph="qwerty", turns=5, shifted_count=0, entropy=29.7820508329166>], password"asdfghju7654rewq", calc_time0.00526
|
39
|
-
```
|
41
|
+
```
|
42
|
+
|
43
|
+
**Note**: Storing the entropy of an encrypted or hashed value provides
|
44
|
+
information that can make cracking the value orders of magnitude easier for an
|
45
|
+
attacker. For this reason we advise you not to store the results of
|
46
|
+
`Zxcvbn::Tester#test`. Further reading: [A Tale of Security Gone Wrong](http://gavinmiller.io/2016/a-tale-of-security-gone-wrong/).
|
data/Rakefile
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
#!/usr/bin/env rake
|
2
2
|
require "bundler/gem_tasks"
|
3
3
|
require "bundler/setup"
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
|
6
|
+
RSpec::Core::RakeTask.new('spec')
|
7
|
+
task default: [:spec]
|
4
8
|
|
5
9
|
task :console do
|
6
10
|
require 'zxcvbn'
|
@@ -15,4 +19,4 @@ end
|
|
15
19
|
task :compile_coffeescript do
|
16
20
|
`coffee --compile --bare spec/support/js_source/{matching,scoring,init}.coffee`
|
17
21
|
`cat spec/support/js_source/{matching,scoring,adjacency_graphs,frequency_lists,init}.js > spec/support/js_source/compiled.js`
|
18
|
-
end
|
22
|
+
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
|
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
|
@@ -6,14 +6,14 @@ describe Zxcvbn::Matchers::Dictionary do
|
|
6
6
|
|
7
7
|
it 'finds all the matches' do
|
8
8
|
matches = matcher.matches('whatisinit')
|
9
|
-
matches.count.
|
9
|
+
expect(matches.count).to eq(14)
|
10
10
|
expected_matches = ['wha', 'what', 'ha', 'hat', 'a', 'at', 'tis', 'i', 'is',
|
11
11
|
'sin', 'i', 'in', 'i', 'it']
|
12
|
-
matches.map(&:matched_word).
|
12
|
+
expect(matches.map(&:matched_word)).to eq(expected_matches)
|
13
13
|
end
|
14
14
|
|
15
15
|
it 'matches uppercase' do
|
16
16
|
matcher = described_class.new('user_inputs', Zxcvbn::DictionaryRanker.rank_dictionary(['test','AB10CD']))
|
17
|
-
matcher.matches('AB10CD').
|
17
|
+
expect(matcher.matches('AB10CD')).not_to be_empty
|
18
18
|
end
|
19
19
|
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
@@ -7,7 +7,7 @@ describe Zxcvbn::Matchers::L33t do
|
|
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
|
@@ -56,7 +56,7 @@ describe Zxcvbn::Matchers::L33t do
|
|
56
56
|
# dictionary/frequency list
|
57
57
|
|
58
58
|
it 'finds the correct matches' do
|
59
|
-
matches.map(&:matched_word).
|
59
|
+
expect(matches.map(&:matched_word)).to eq([
|
60
60
|
'pas',
|
61
61
|
'a',
|
62
62
|
'as',
|
@@ -65,7 +65,7 @@ describe Zxcvbn::Matchers::L33t do
|
|
65
65
|
end
|
66
66
|
|
67
67
|
it 'sets the token correctly on those matches' do
|
68
|
-
matches.map(&:token).
|
68
|
+
expect(matches.map(&:token)).to eq([
|
69
69
|
'p@s',
|
70
70
|
'@',
|
71
71
|
'@s',
|
@@ -74,7 +74,7 @@ describe Zxcvbn::Matchers::L33t do
|
|
74
74
|
end
|
75
75
|
|
76
76
|
it 'sets the substituions used' do
|
77
|
-
matches.map(&:sub).
|
77
|
+
expect(matches.map(&:sub)).to eq([
|
78
78
|
{'@' => 'a'},
|
79
79
|
{'@' => 'a'},
|
80
80
|
{'@' => 'a'},
|
@@ -5,14 +5,14 @@ describe Zxcvbn::Matchers::Repeat do
|
|
5
5
|
let(:matches) { matcher.matches('bbbbbtestingaaa') }
|
6
6
|
|
7
7
|
it 'sets the pattern name' do
|
8
|
-
matches.all? { |m| m.pattern == 'repeat' }.
|
8
|
+
expect(matches.all? { |m| m.pattern == 'repeat' }).to eql(true)
|
9
9
|
end
|
10
10
|
|
11
11
|
it 'finds the repeated patterns' do
|
12
|
-
matches.count.
|
13
|
-
matches[0].token.
|
14
|
-
matches[0].repeated_char.
|
15
|
-
matches[1].token.
|
16
|
-
matches[1].repeated_char.
|
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
17
|
end
|
18
18
|
end
|
@@ -5,17 +5,17 @@ describe Zxcvbn::Matchers::Sequences do
|
|
5
5
|
let(:matches) { matcher.matches('abcde87654') }
|
6
6
|
|
7
7
|
it 'sets the pattern name' do
|
8
|
-
matches.all? { |m| m.pattern == 'sequence' }.
|
8
|
+
expect(matches.all? { |m| m.pattern == 'sequence' }).to eql(true)
|
9
9
|
end
|
10
10
|
|
11
11
|
it 'finds the correct matches' do
|
12
|
-
matches.count.
|
13
|
-
matches[0].token.
|
14
|
-
matches[1].token.
|
12
|
+
expect(matches.count).to eq(2)
|
13
|
+
expect(matches[0].token).to eq 'abcde'
|
14
|
+
expect(matches[1].token).to eq '87654'
|
15
15
|
end
|
16
16
|
|
17
17
|
it 'finds overlapping matches' do
|
18
18
|
matches = matcher.matches('abcba')
|
19
|
-
matches.map(&:token).
|
19
|
+
expect(matches.map(&:token)).to eq ['abc', 'cba']
|
20
20
|
end
|
21
21
|
end
|
@@ -8,13 +8,13 @@ describe Zxcvbn::Matchers::Spatial do
|
|
8
8
|
let(:matches) { matcher.matches('rtyikm') }
|
9
9
|
|
10
10
|
it 'finds the correct of matches' do
|
11
|
-
matches.count.
|
12
|
-
matches[0].token.
|
13
|
-
matches[0].graph.
|
14
|
-
matches[1].token.
|
15
|
-
matches[1].graph.
|
16
|
-
matches[2].token.
|
17
|
-
matches[2].graph.
|
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
18
|
end
|
19
19
|
end
|
20
20
|
end
|
data/spec/matchers/year_spec.rb
CHANGED
@@ -5,11 +5,11 @@ describe Zxcvbn::Matchers::Year do
|
|
5
5
|
let(:matches) { matcher.matches('testing1998') }
|
6
6
|
|
7
7
|
it 'sets the pattern name' do
|
8
|
-
matches.all? { |m| m.pattern == 'year' }.
|
8
|
+
expect(matches.all? { |m| m.pattern == 'year' }).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(1)
|
13
|
+
expect(matches[0].token).to eq '1998'
|
14
14
|
end
|
15
15
|
end
|
data/spec/omnimatch_spec.rb
CHANGED
@@ -5,38 +5,38 @@ describe Zxcvbn::CrackTime do
|
|
5
5
|
|
6
6
|
describe '#entropy_to_crack_time' do
|
7
7
|
specify do
|
8
|
-
entropy_to_crack_time(15.433976574415976).
|
8
|
+
expect(entropy_to_crack_time(15.433976574415976)).to eq 2.2134000000000014
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
12
|
describe '#crack_time_to_score' do
|
13
13
|
context 'crack time less than 10 to the power 2' do
|
14
14
|
it 'returns 0' do
|
15
|
-
crack_time_to_score(90).
|
15
|
+
expect(crack_time_to_score(90)).to eq 0
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
19
|
context 'crack time in between 10**2 and 10**4' do
|
20
20
|
it 'returns 1' do
|
21
|
-
crack_time_to_score(5000).
|
21
|
+
expect(crack_time_to_score(5000)).to eq 1
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
25
|
context 'crack time in between 10**4 and 10**6' do
|
26
26
|
it 'returns 2' do
|
27
|
-
crack_time_to_score(500_000).
|
27
|
+
expect(crack_time_to_score(500_000)).to eq 2
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
31
|
context 'crack time in between 10**6 and 10**8' do
|
32
32
|
it 'returns 3' do
|
33
|
-
crack_time_to_score(50_000_000).
|
33
|
+
expect(crack_time_to_score(50_000_000)).to eq 3
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
37
|
context 'crack time above 10**8' do
|
38
38
|
it 'returns 4' do
|
39
|
-
crack_time_to_score(110_000_000).
|
39
|
+
expect(crack_time_to_score(110_000_000)).to eq 4
|
40
40
|
end
|
41
41
|
end
|
42
42
|
end
|
@@ -52,7 +52,7 @@ describe Zxcvbn::CrackTime do
|
|
52
52
|
context 'when less than a minute' do
|
53
53
|
it 'should return instant' do
|
54
54
|
[0, minute_to_seconds - 1].each do |seconds|
|
55
|
-
display_time(seconds).
|
55
|
+
expect(display_time(seconds)).to eql 'instant'
|
56
56
|
end
|
57
57
|
end
|
58
58
|
end
|
@@ -60,7 +60,7 @@ describe Zxcvbn::CrackTime do
|
|
60
60
|
context 'when less than an hour' do
|
61
61
|
it 'should return a readable time in minutes' do
|
62
62
|
[60, (hour_to_seconds - 1)].each do |seconds|
|
63
|
-
display_time(seconds).
|
63
|
+
expect(display_time(seconds)).to match(/[0-9]+ minutes$/)
|
64
64
|
end
|
65
65
|
end
|
66
66
|
end
|
@@ -68,7 +68,7 @@ describe Zxcvbn::CrackTime do
|
|
68
68
|
context 'when less than a day' do
|
69
69
|
it 'should return a readable time in hours' do
|
70
70
|
[hour_to_seconds, (day_to_seconds - 1)].each do |seconds|
|
71
|
-
display_time(seconds).
|
71
|
+
expect(display_time(seconds)).to match(/[0-9]+ hours$/)
|
72
72
|
end
|
73
73
|
end
|
74
74
|
end
|
@@ -76,7 +76,7 @@ describe Zxcvbn::CrackTime do
|
|
76
76
|
context 'when less than 31 days' do
|
77
77
|
it 'should return a readable time in days' do
|
78
78
|
[day_to_seconds, month_to_seconds - 1].each do |seconds|
|
79
|
-
display_time(seconds).
|
79
|
+
expect(display_time(seconds)).to match(/[0-9]+ days$/)
|
80
80
|
end
|
81
81
|
end
|
82
82
|
end
|
@@ -84,7 +84,7 @@ describe Zxcvbn::CrackTime do
|
|
84
84
|
context 'when less than 1 year' do
|
85
85
|
it 'should return a readable time in days' do
|
86
86
|
[month_to_seconds, (year_to_seconds - 1)].each do |seconds|
|
87
|
-
display_time(seconds).
|
87
|
+
expect(display_time(seconds)).to match(/[0-9]+ months$/)
|
88
88
|
end
|
89
89
|
end
|
90
90
|
end
|
@@ -92,14 +92,14 @@ describe Zxcvbn::CrackTime do
|
|
92
92
|
context 'when less than a century' do
|
93
93
|
it 'should return a readable time in days' do
|
94
94
|
[year_to_seconds, (century_to_seconds - 1)].each do |seconds|
|
95
|
-
display_time(seconds).
|
95
|
+
expect(display_time(seconds)).to match(/[0-9]+ years$/)
|
96
96
|
end
|
97
97
|
end
|
98
98
|
end
|
99
99
|
|
100
100
|
context 'when a century or more' do
|
101
101
|
it 'should return centuries' do
|
102
|
-
display_time(century_to_seconds).
|
102
|
+
expect(display_time(century_to_seconds)).to eql 'centuries'
|
103
103
|
end
|
104
104
|
end
|
105
105
|
end
|
@@ -15,7 +15,7 @@ describe Zxcvbn::Entropy do
|
|
15
15
|
describe '#repeat_entropy' do
|
16
16
|
it 'returns the correct value' do
|
17
17
|
match = Zxcvbn::Match.new(:token => '2222')
|
18
|
-
entropy.repeat_entropy(match).
|
18
|
+
expect(entropy.repeat_entropy(match)).to eq 5.321928094887363
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
@@ -27,7 +27,7 @@ describe Zxcvbn::Entropy do
|
|
27
27
|
let(:token) { token }
|
28
28
|
|
29
29
|
it 'returns the correct value' do
|
30
|
-
entropy.sequence_entropy(match).
|
30
|
+
expect(entropy.sequence_entropy(match)).to eq 3.807354922057604
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
@@ -36,7 +36,7 @@ describe Zxcvbn::Entropy do
|
|
36
36
|
let(:token) { '23456' }
|
37
37
|
|
38
38
|
it 'returns the correct value' do
|
39
|
-
entropy.sequence_entropy(match).
|
39
|
+
expect(entropy.sequence_entropy(match)).to eq 5.643856189774725
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
@@ -44,7 +44,7 @@ describe Zxcvbn::Entropy do
|
|
44
44
|
let(:token) { 'bcdef' }
|
45
45
|
|
46
46
|
it 'returns the correct value' do
|
47
|
-
entropy.sequence_entropy(match).
|
47
|
+
expect(entropy.sequence_entropy(match)).to eq 7.022367813028454
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
@@ -52,7 +52,7 @@ describe Zxcvbn::Entropy do
|
|
52
52
|
let(:token) { 'BCDEF' }
|
53
53
|
|
54
54
|
it 'returns the correct value' do
|
55
|
-
entropy.sequence_entropy(match).
|
55
|
+
expect(entropy.sequence_entropy(match)).to eq 8.022367813028454
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
@@ -61,7 +61,7 @@ describe Zxcvbn::Entropy do
|
|
61
61
|
let(:token) { 'bcdef' }
|
62
62
|
|
63
63
|
it 'returns the correct value' do
|
64
|
-
entropy.sequence_entropy(match).
|
64
|
+
expect(entropy.sequence_entropy(match)).to eq 8.022367813028454
|
65
65
|
end
|
66
66
|
end
|
67
67
|
end
|
@@ -69,13 +69,13 @@ describe Zxcvbn::Entropy do
|
|
69
69
|
describe '#digits_entropy' do
|
70
70
|
it 'returns the correct value' do
|
71
71
|
match = Zxcvbn::Match.new(:token => '12345678')
|
72
|
-
entropy.digits_entropy(match).
|
72
|
+
expect(entropy.digits_entropy(match)).to eq 26.5754247590989
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
76
|
describe '#year_entropy' do
|
77
77
|
it 'returns the correct value' do
|
78
|
-
entropy.year_entropy(nil).
|
78
|
+
expect(entropy.year_entropy(nil)).to eq 6.894817763307944
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
@@ -83,21 +83,21 @@ describe Zxcvbn::Entropy do
|
|
83
83
|
context 'with a two digit year' do
|
84
84
|
it 'returns the correct value' do
|
85
85
|
match = Zxcvbn::Match.new(:year => 98)
|
86
|
-
entropy.date_entropy(match).
|
86
|
+
expect(entropy.date_entropy(match)).to eq 15.183015000882756
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
90
90
|
context 'with a four digit year' do
|
91
91
|
it 'returns the correct value' do
|
92
92
|
match = Zxcvbn::Match.new(:year => 2012)
|
93
|
-
entropy.date_entropy(match).
|
93
|
+
expect(entropy.date_entropy(match)).to eq 15.433976574415976
|
94
94
|
end
|
95
95
|
end
|
96
96
|
|
97
97
|
context 'with a separator' do
|
98
98
|
it 'returns the correct value' do
|
99
99
|
match = Zxcvbn::Match.new(:year => 2012, :separator => '/')
|
100
|
-
entropy.date_entropy(match).
|
100
|
+
expect(entropy.date_entropy(match)).to eq 17.433976574415976
|
101
101
|
end
|
102
102
|
end
|
103
103
|
end
|
@@ -112,42 +112,42 @@ describe Zxcvbn::Entropy do
|
|
112
112
|
let(:token) { 'you' }
|
113
113
|
let(:rank) { 1 }
|
114
114
|
|
115
|
-
specify { calculated_entropy.
|
115
|
+
specify { expect(calculated_entropy).to eq 0 }
|
116
116
|
end
|
117
117
|
|
118
118
|
context 'with all upper case characters' do
|
119
119
|
let(:token) { 'YOU' }
|
120
120
|
let(:rank) { 1 }
|
121
121
|
|
122
|
-
specify { calculated_entropy.
|
122
|
+
specify { expect(calculated_entropy).to eq 1 }
|
123
123
|
end
|
124
124
|
|
125
125
|
context 'starting with uppercase' do
|
126
126
|
let(:token) { 'You' }
|
127
127
|
let(:rank) { 1 }
|
128
128
|
|
129
|
-
specify { calculated_entropy.
|
129
|
+
specify { expect(calculated_entropy).to eq 1 }
|
130
130
|
end
|
131
131
|
|
132
132
|
context 'starting with uppercase' do
|
133
133
|
let(:token) { 'yoU' }
|
134
134
|
let(:rank) { 1 }
|
135
135
|
|
136
|
-
specify { calculated_entropy.
|
136
|
+
specify { expect(calculated_entropy).to eq 1 }
|
137
137
|
end
|
138
138
|
|
139
139
|
context 'mixed upper and lower' do
|
140
140
|
let(:token) { 'tEsTiNg' }
|
141
141
|
let(:rank) { 1 }
|
142
142
|
|
143
|
-
specify { calculated_entropy.
|
143
|
+
specify { expect(calculated_entropy).to eq 6 }
|
144
144
|
end
|
145
145
|
|
146
146
|
context 'starting with digits' do
|
147
147
|
let(:token) { '12345' }
|
148
148
|
let(:rank) { 1 }
|
149
149
|
|
150
|
-
specify { calculated_entropy.
|
150
|
+
specify { expect(calculated_entropy).to eq 0 }
|
151
151
|
end
|
152
152
|
|
153
153
|
context 'extra l33t entropy' do
|
@@ -156,7 +156,7 @@ describe Zxcvbn::Entropy do
|
|
156
156
|
let(:l33t) { true }
|
157
157
|
let(:sub) { {'3' => 'e', '0' => 'o'} }
|
158
158
|
|
159
|
-
specify { calculated_entropy.
|
159
|
+
specify { expect(calculated_entropy).to eq 1 }
|
160
160
|
end
|
161
161
|
end
|
162
162
|
|
@@ -167,7 +167,7 @@ describe Zxcvbn::Entropy do
|
|
167
167
|
it 'should return the correct entropy' do
|
168
168
|
match.graph = 'qwerty'
|
169
169
|
|
170
|
-
entropy.spatial_entropy(match).
|
170
|
+
expect(entropy.spatial_entropy(match)).to eql 11.562242424221074
|
171
171
|
end
|
172
172
|
end
|
173
173
|
|
@@ -175,7 +175,7 @@ describe Zxcvbn::Entropy do
|
|
175
175
|
it 'should return the correct entropy' do
|
176
176
|
match.graph = 'dvorak'
|
177
177
|
|
178
|
-
entropy.spatial_entropy(match).
|
178
|
+
expect(entropy.spatial_entropy(match)).to eql 11.562242424221074
|
179
179
|
end
|
180
180
|
end
|
181
181
|
|
@@ -183,7 +183,7 @@ describe Zxcvbn::Entropy do
|
|
183
183
|
it 'should return the correct entropy' do
|
184
184
|
match.graph = 'keypad'
|
185
185
|
|
186
|
-
entropy.spatial_entropy(match).
|
186
|
+
expect(entropy.spatial_entropy(match)).to eql 9.05528243550119
|
187
187
|
end
|
188
188
|
end
|
189
189
|
|
@@ -191,7 +191,7 @@ describe Zxcvbn::Entropy do
|
|
191
191
|
it 'should return the correct entropy' do
|
192
192
|
match.turns = 5
|
193
193
|
|
194
|
-
entropy.spatial_entropy(match).
|
194
|
+
expect(entropy.spatial_entropy(match)).to eql 21.761397858718993
|
195
195
|
end
|
196
196
|
end
|
197
197
|
|
@@ -199,7 +199,7 @@ describe Zxcvbn::Entropy do
|
|
199
199
|
it 'should return the correct entropy' do
|
200
200
|
match.shiffted_count = 5
|
201
201
|
|
202
|
-
entropy.spatial_entropy(match).
|
202
|
+
expect(entropy.spatial_entropy(match)).to eql 9.05528243550119
|
203
203
|
end
|
204
204
|
end
|
205
205
|
|
@@ -208,7 +208,7 @@ describe Zxcvbn::Entropy do
|
|
208
208
|
match.shiffted_count = 5
|
209
209
|
match.turns = 5
|
210
210
|
|
211
|
-
entropy.spatial_entropy(match).
|
211
|
+
expect(entropy.spatial_entropy(match)).to eql 21.761397858718993
|
212
212
|
end
|
213
213
|
end
|
214
214
|
end
|
data/spec/scoring/math_spec.rb
CHANGED
@@ -10,7 +10,7 @@ describe Zxcvbn::Math do
|
|
10
10
|
describe '#bruteforce_cardinality' do
|
11
11
|
context 'when empty password' do
|
12
12
|
it 'should return 0 if empty password' do
|
13
|
-
bruteforce_cardinality('').
|
13
|
+
expect(bruteforce_cardinality('')).to eql 0
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
@@ -18,7 +18,7 @@ describe Zxcvbn::Math do
|
|
18
18
|
context 'and a digit' do
|
19
19
|
it 'should return 10' do
|
20
20
|
(0..9).each do |digit|
|
21
|
-
bruteforce_cardinality(digit.to_s).
|
21
|
+
expect(bruteforce_cardinality(digit.to_s)).to eql 10
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
@@ -26,7 +26,7 @@ describe Zxcvbn::Math do
|
|
26
26
|
context 'and an upper case character' do
|
27
27
|
it 'should return 26' do
|
28
28
|
('A'..'Z').each do |character|
|
29
|
-
bruteforce_cardinality(character).
|
29
|
+
expect(bruteforce_cardinality(character)).to eql 26
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
@@ -34,7 +34,7 @@ describe Zxcvbn::Math do
|
|
34
34
|
context 'and a lower case character' do
|
35
35
|
it 'should return 26' do
|
36
36
|
('a'..'z').each do |character|
|
37
|
-
bruteforce_cardinality(character).
|
37
|
+
expect(bruteforce_cardinality(character)).to eql 26
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
@@ -42,7 +42,7 @@ describe Zxcvbn::Math do
|
|
42
42
|
context 'and a symbol' do
|
43
43
|
it 'should return 33' do
|
44
44
|
%w|/ [ ` {|.each do |symbol|
|
45
|
-
bruteforce_cardinality(symbol).
|
45
|
+
expect(bruteforce_cardinality(symbol)).to eql 33
|
46
46
|
end
|
47
47
|
end
|
48
48
|
end
|
@@ -51,31 +51,31 @@ describe Zxcvbn::Math do
|
|
51
51
|
context 'when password is more than one character long' do
|
52
52
|
context 'and only digits' do
|
53
53
|
it 'should return 10' do
|
54
|
-
bruteforce_cardinality('123456789').
|
54
|
+
expect(bruteforce_cardinality('123456789')).to eql 10
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
58
|
context 'and only lowercase characters' do
|
59
59
|
it 'should return 26' do
|
60
|
-
bruteforce_cardinality('password').
|
60
|
+
expect(bruteforce_cardinality('password')).to eql 26
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
64
64
|
context 'and only uppercase characters' do
|
65
65
|
it 'should return 26' do
|
66
|
-
bruteforce_cardinality('PASSWORD').
|
66
|
+
expect(bruteforce_cardinality('PASSWORD')).to eql 26
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
70
|
context 'and only symbols' do
|
71
71
|
it 'should return 33' do
|
72
|
-
bruteforce_cardinality('/ [ ` {').
|
72
|
+
expect(bruteforce_cardinality('/ [ ` {')).to eql 33
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
76
|
context 'and a mixed of character types' do
|
77
77
|
it 'should add up every character type cardinality' do
|
78
|
-
bruteforce_cardinality('p1SsWorD!').
|
78
|
+
expect(bruteforce_cardinality('p1SsWorD!')).to eql 95
|
79
79
|
end
|
80
80
|
end
|
81
81
|
end
|
@@ -84,25 +84,25 @@ describe Zxcvbn::Math do
|
|
84
84
|
describe '#average_degree_for_graph' do
|
85
85
|
context 'when keyboard is qwerty' do
|
86
86
|
it 'returns the correct average degree over all keys' do
|
87
|
-
average_degree_for_graph('qwerty').
|
87
|
+
expect(average_degree_for_graph('qwerty')).to eql 4.595744680851064
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
91
91
|
context 'when keyboard is dvorak' do
|
92
92
|
it 'returns the correct average degree over all keys' do
|
93
|
-
average_degree_for_graph('dvorak').
|
93
|
+
expect(average_degree_for_graph('dvorak')).to eql 4.595744680851064
|
94
94
|
end
|
95
95
|
end
|
96
96
|
|
97
97
|
context 'when keyboard is keypad' do
|
98
98
|
it 'returns the correct average degree over all keys' do
|
99
|
-
average_degree_for_graph('keypad').
|
99
|
+
expect(average_degree_for_graph('keypad')).to eql 5.066666666666666
|
100
100
|
end
|
101
101
|
end
|
102
102
|
|
103
103
|
context 'when keyboard is mac keypad' do
|
104
104
|
it 'returns the correct average degree over all keys' do
|
105
|
-
average_degree_for_graph('mac_keypad').
|
105
|
+
expect(average_degree_for_graph('mac_keypad')).to eql 5.25
|
106
106
|
end
|
107
107
|
end
|
108
108
|
end
|
@@ -110,25 +110,25 @@ describe Zxcvbn::Math do
|
|
110
110
|
describe '#starting_positions_for_graph' do
|
111
111
|
context 'when keyboard is qwerty' do
|
112
112
|
it 'returns the correct average degree over all keys' do
|
113
|
-
starting_positions_for_graph('qwerty').
|
113
|
+
expect(starting_positions_for_graph('qwerty')).to eql 94
|
114
114
|
end
|
115
115
|
end
|
116
116
|
|
117
117
|
context 'when keyboard is dvorak' do
|
118
118
|
it 'returns the correct average degree over all keys' do
|
119
|
-
starting_positions_for_graph('dvorak').
|
119
|
+
expect(starting_positions_for_graph('dvorak')).to eql 94
|
120
120
|
end
|
121
121
|
end
|
122
122
|
|
123
123
|
context 'when keyboard is keypad' do
|
124
124
|
it 'returns the correct average degree over all keys' do
|
125
|
-
starting_positions_for_graph('keypad').
|
125
|
+
expect(starting_positions_for_graph('keypad')).to eql 15
|
126
126
|
end
|
127
127
|
end
|
128
128
|
|
129
129
|
context 'when keyboard is mac keypad' do
|
130
130
|
it 'returns the correct average degree over all keys' do
|
131
|
-
starting_positions_for_graph('mac_keypad').
|
131
|
+
expect(starting_positions_for_graph('mac_keypad')).to eql 16
|
132
132
|
end
|
133
133
|
end
|
134
134
|
end
|
data/spec/support/matcher.rb
CHANGED
@@ -17,7 +17,7 @@ RSpec::Matchers.define :match_js_results do |expected_js_results|
|
|
17
17
|
@missing.empty? && @extra.empty?
|
18
18
|
end
|
19
19
|
|
20
|
-
|
20
|
+
failure_message do |actual|
|
21
21
|
"Matches missing from ruby results:\n#{@missing.inspect}\nMatches unique to ruby results:\n#{@extra.inspect}"
|
22
22
|
end
|
23
23
|
|
data/spec/tester_spec.rb
CHANGED
@@ -8,28 +8,28 @@ describe Zxcvbn::Tester do
|
|
8
8
|
ruby_result = tester.test(password)
|
9
9
|
js_result = js_zxcvbn(password)
|
10
10
|
|
11
|
-
ruby_result.calc_time.
|
12
|
-
ruby_result.password.
|
13
|
-
ruby_result.entropy.
|
14
|
-
ruby_result.crack_time.
|
15
|
-
ruby_result.crack_time_display.
|
16
|
-
ruby_result.score.
|
17
|
-
ruby_result.pattern.
|
18
|
-
ruby_result.match_sequence.count.
|
11
|
+
expect(ruby_result.calc_time).not_to be_nil
|
12
|
+
expect(ruby_result.password).to eq js_result['password']
|
13
|
+
expect(ruby_result.entropy).to eq js_result['entropy']
|
14
|
+
expect(ruby_result.crack_time).to eq js_result['crack_time']
|
15
|
+
expect(ruby_result.crack_time_display).to eq js_result['crack_time_display']
|
16
|
+
expect(ruby_result.score).to eq js_result['score']
|
17
|
+
expect(ruby_result.pattern).to eq js_result['pattern']
|
18
|
+
expect(ruby_result.match_sequence.count).to eq js_result['match_sequence'].count
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
22
|
context 'with a custom user dictionary' do
|
23
23
|
it 'scores them against the user dictionary' do
|
24
24
|
result = tester.test('themeforest', ['themeforest'])
|
25
|
-
result.entropy.
|
26
|
-
result.score.
|
25
|
+
expect(result.entropy).to eq 0
|
26
|
+
expect(result.score).to eq 0
|
27
27
|
end
|
28
28
|
|
29
29
|
it 'matches l33t substitutions on this dictionary' do
|
30
30
|
result = tester.test('th3m3for3st', ['themeforest'])
|
31
|
-
result.entropy.
|
32
|
-
result.score.
|
31
|
+
expect(result.entropy).to eq 1
|
32
|
+
expect(result.score).to eq 0
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
@@ -38,8 +38,8 @@ describe Zxcvbn::Tester do
|
|
38
38
|
|
39
39
|
it 'scores them against the dictionary' do
|
40
40
|
result = tester.test('envato')
|
41
|
-
result.entropy.
|
42
|
-
result.score.
|
41
|
+
expect(result.entropy).to eq 0
|
42
|
+
expect(result.score).to eq 0
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
data/zxcvbn-ruby.gemspec
CHANGED
@@ -11,10 +11,10 @@ Gem::Specification.new do |gem|
|
|
11
11
|
gem.files = `git ls-files`.split($\)
|
12
12
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
13
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.add_development_dependency 'therubyracer'
|
15
|
+
gem.add_development_dependency 'rspec'
|
14
16
|
gem.name = "zxcvbn-ruby"
|
15
17
|
gem.require_paths = ["lib"]
|
16
18
|
gem.version = Zxcvbn::VERSION
|
17
|
-
|
18
|
-
gem.add_development_dependency 'therubyracer'
|
19
|
-
gem.add_development_dependency 'rspec'
|
20
19
|
end
|
20
|
+
|
metadata
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zxcvbn-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
5
|
-
prerelease:
|
4
|
+
version: 0.1.1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Steve Hodgkiss
|
@@ -10,38 +9,34 @@ authors:
|
|
10
9
|
autorequire:
|
11
10
|
bindir: bin
|
12
11
|
cert_chain: []
|
13
|
-
date:
|
12
|
+
date: 2016-04-11 00:00:00.000000000 Z
|
14
13
|
dependencies:
|
15
14
|
- !ruby/object:Gem::Dependency
|
16
15
|
name: therubyracer
|
17
16
|
requirement: !ruby/object:Gem::Requirement
|
18
|
-
none: false
|
19
17
|
requirements:
|
20
|
-
- -
|
18
|
+
- - ">="
|
21
19
|
- !ruby/object:Gem::Version
|
22
20
|
version: '0'
|
23
21
|
type: :development
|
24
22
|
prerelease: false
|
25
23
|
version_requirements: !ruby/object:Gem::Requirement
|
26
|
-
none: false
|
27
24
|
requirements:
|
28
|
-
- -
|
25
|
+
- - ">="
|
29
26
|
- !ruby/object:Gem::Version
|
30
27
|
version: '0'
|
31
28
|
- !ruby/object:Gem::Dependency
|
32
29
|
name: rspec
|
33
30
|
requirement: !ruby/object:Gem::Requirement
|
34
|
-
none: false
|
35
31
|
requirements:
|
36
|
-
- -
|
32
|
+
- - ">="
|
37
33
|
- !ruby/object:Gem::Version
|
38
34
|
version: '0'
|
39
35
|
type: :development
|
40
36
|
prerelease: false
|
41
37
|
version_requirements: !ruby/object:Gem::Requirement
|
42
|
-
none: false
|
43
38
|
requirements:
|
44
|
-
- -
|
39
|
+
- - ">="
|
45
40
|
- !ruby/object:Gem::Version
|
46
41
|
version: '0'
|
47
42
|
description: Ruby port of Dropboxs zxcvbn.js
|
@@ -52,8 +47,9 @@ executables: []
|
|
52
47
|
extensions: []
|
53
48
|
extra_rdoc_files: []
|
54
49
|
files:
|
55
|
-
- .gitignore
|
56
|
-
- .rspec
|
50
|
+
- ".gitignore"
|
51
|
+
- ".rspec"
|
52
|
+
- ".travis.yml"
|
57
53
|
- Gemfile
|
58
54
|
- LICENSE
|
59
55
|
- README.md
|
@@ -118,33 +114,26 @@ files:
|
|
118
114
|
- zxcvbn-ruby.gemspec
|
119
115
|
homepage: http://github.com/envato/zxcvbn-ruby
|
120
116
|
licenses: []
|
117
|
+
metadata: {}
|
121
118
|
post_install_message:
|
122
119
|
rdoc_options: []
|
123
120
|
require_paths:
|
124
121
|
- lib
|
125
122
|
required_ruby_version: !ruby/object:Gem::Requirement
|
126
|
-
none: false
|
127
123
|
requirements:
|
128
|
-
- -
|
124
|
+
- - ">="
|
129
125
|
- !ruby/object:Gem::Version
|
130
126
|
version: '0'
|
131
|
-
segments:
|
132
|
-
- 0
|
133
|
-
hash: 3120558835973257605
|
134
127
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
|
-
none: false
|
136
128
|
requirements:
|
137
|
-
- -
|
129
|
+
- - ">="
|
138
130
|
- !ruby/object:Gem::Version
|
139
131
|
version: '0'
|
140
|
-
segments:
|
141
|
-
- 0
|
142
|
-
hash: 3120558835973257605
|
143
132
|
requirements: []
|
144
133
|
rubyforge_project:
|
145
|
-
rubygems_version:
|
134
|
+
rubygems_version: 2.6.3
|
146
135
|
signing_key:
|
147
|
-
specification_version:
|
136
|
+
specification_version: 4
|
148
137
|
summary: ''
|
149
138
|
test_files:
|
150
139
|
- spec/dictionary_ranker_spec.rb
|