zxcvbn-ruby 0.0.3 → 0.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.
- data/README.md +17 -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 +2 -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/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 +5 -3
- data/lib/zxcvbn/scorer.rb +11 -0
- data/lib/zxcvbn/tester.rb +35 -0
- data/lib/zxcvbn/version.rb +1 -1
- data/spec/matchers/dictionary_spec.rb +1 -1
- data/spec/matchers/l33t_spec.rb +1 -1
- data/spec/matchers/spatial_spec.rb +1 -1
- data/spec/omnimatch_spec.rb +1 -1
- data/spec/scoring/entropy_spec.rb +4 -1
- data/spec/scoring/math_spec.rb +4 -0
- data/spec/tester_spec.rb +51 -0
- data/spec/zxcvbn_spec.rb +14 -39
- metadata +8 -4
data/README.md
CHANGED
@@ -7,7 +7,7 @@ Ruby port of [zxcvbn.js](https://github.com/dropbox/zxcvbn)
|
|
7
7
|
Gemfile:
|
8
8
|
|
9
9
|
```ruby
|
10
|
-
gem
|
10
|
+
gem 'zxcvbn-ruby', require: 'zxcvbn'
|
11
11
|
```
|
12
12
|
|
13
13
|
Example usage:
|
@@ -20,4 +20,20 @@ $ irb
|
|
20
20
|
=> #<Zxcvbn::Score:0x007fd467803098 @entropy=7.895, @crack_time=0.012, @crack_time_display="instant", @score=0, @match_sequence=[#<Zxcvbn::Match matched_word="alfred", token="@lfred", i=0, j=5, rank=1, pattern="dictionary", dictionary_name="user_inputs", l33t=true, sub={"@"=>"a"}, sub_display"@ -> a", base_entropy0.0, uppercase_entropy0.0, l33t_entropy1, entropy1.0, #<Zxcvbn::Match i=6, j=9, token="2004", pattern="year", entropy=6.894817763307944], @password="@lfred2004", @calc_time=0.003436>
|
21
21
|
>> Zxcvbn.test('asdfghju7654rewq', ['alfred'])
|
22
22
|
=> #<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
|
23
|
+
```
|
24
|
+
|
25
|
+
## Testing Multiple Passwords
|
26
|
+
|
27
|
+
The dictionaries used for password strength testing are loaded each request to `Zxcvbn.test`. If you you'd prefer to persist the dictionaries in memory (approx 20MB RSS) to perform lots of password tests in succession then you can use the `Zxcvbn::Tester` API:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
$ irb
|
31
|
+
>> require 'zxcvbn'
|
32
|
+
=> true
|
33
|
+
>> tester = Zxcvbn::Tester.new
|
34
|
+
=> #<Zxcvbn::Tester:0x3fe99d869aa4>
|
35
|
+
>> tester.test('@lfred2004', ['alfred'])
|
36
|
+
=> #<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
|
+
>> tester.test('@lfred2004', ['alfred'])
|
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
|
23
39
|
```
|
data/lib/zxcvbn.rb
CHANGED
@@ -1,46 +1,20 @@
|
|
1
|
-
require 'json'
|
2
1
|
require 'pathname'
|
3
|
-
|
4
2
|
require 'zxcvbn/version'
|
5
|
-
require 'zxcvbn/
|
6
|
-
require 'zxcvbn/matchers/regex_helpers'
|
7
|
-
require 'zxcvbn/matchers/dictionary'
|
8
|
-
require 'zxcvbn/matchers/l33t'
|
9
|
-
require 'zxcvbn/matchers/spatial'
|
10
|
-
require 'zxcvbn/matchers/sequences'
|
11
|
-
require 'zxcvbn/matchers/repeat'
|
12
|
-
require 'zxcvbn/matchers/digits'
|
13
|
-
require 'zxcvbn/matchers/year'
|
14
|
-
require 'zxcvbn/matchers/date'
|
15
|
-
require 'zxcvbn/dictionary_ranker'
|
16
|
-
require 'zxcvbn/omnimatch'
|
17
|
-
require 'zxcvbn/math'
|
18
|
-
require 'zxcvbn/entropy'
|
19
|
-
require 'zxcvbn/crack_time'
|
20
|
-
require 'zxcvbn/score'
|
21
|
-
require 'zxcvbn/scorer'
|
22
|
-
require 'zxcvbn/password_strength'
|
3
|
+
require 'zxcvbn/tester'
|
23
4
|
|
24
5
|
module Zxcvbn
|
25
6
|
extend self
|
26
7
|
|
27
8
|
DATA_PATH = Pathname(File.expand_path('../../data', __FILE__))
|
28
|
-
ADJACENCY_GRAPHS = JSON.load(DATA_PATH.join('adjacency_graphs.json').read)
|
29
|
-
FREQUENCY_LISTS_PATH = DATA_PATH.join("frequency_lists")
|
30
|
-
RANKED_DICTIONARIES = DictionaryRanker.rank_dictionaries(
|
31
|
-
"english" => FREQUENCY_LISTS_PATH.join("english.txt").read.split,
|
32
|
-
"female_names" => FREQUENCY_LISTS_PATH.join("female_names.txt").read.split,
|
33
|
-
"male_names" => FREQUENCY_LISTS_PATH.join("male_names.txt").read.split,
|
34
|
-
"passwords" => FREQUENCY_LISTS_PATH.join("passwords.txt").read.split,
|
35
|
-
"surnames" => FREQUENCY_LISTS_PATH.join("surnames.txt").read.split
|
36
|
-
)
|
37
|
-
|
38
|
-
def test(password, user_inputs = [])
|
39
|
-
zxcvbn = PasswordStrength.new
|
40
|
-
zxcvbn.test(password, user_inputs)
|
41
|
-
end
|
42
9
|
|
43
|
-
|
44
|
-
|
10
|
+
# Returns a Zxcvbn::Score for the given password
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
#
|
14
|
+
# Zxcvbn.test("password").score #=> 0
|
15
|
+
def test(password, user_inputs = [], word_lists = {})
|
16
|
+
tester = Tester.new
|
17
|
+
tester.add_word_lists(word_lists)
|
18
|
+
tester.test(password, user_inputs)
|
45
19
|
end
|
46
20
|
end
|
data/lib/zxcvbn/crack_time.rb
CHANGED
@@ -1,51 +1,53 @@
|
|
1
|
-
module Zxcvbn
|
2
|
-
|
3
|
-
|
1
|
+
module Zxcvbn
|
2
|
+
module CrackTime
|
3
|
+
SINGLE_GUESS = 0.010
|
4
|
+
NUM_ATTACKERS = 100
|
4
5
|
|
5
|
-
|
6
|
+
SECONDS_PER_GUESS = SINGLE_GUESS / NUM_ATTACKERS
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
def entropy_to_crack_time(entropy)
|
9
|
+
0.5 * (2 ** entropy) * SECONDS_PER_GUESS
|
10
|
+
end
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
12
|
+
def crack_time_to_score(seconds)
|
13
|
+
case
|
14
|
+
when seconds < 10**2
|
15
|
+
0
|
16
|
+
when seconds < 10**4
|
17
|
+
1
|
18
|
+
when seconds < 10**6
|
19
|
+
2
|
20
|
+
when seconds < 10**8
|
21
|
+
3
|
22
|
+
else
|
23
|
+
4
|
24
|
+
end
|
23
25
|
end
|
24
|
-
end
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
27
|
+
def display_time(seconds)
|
28
|
+
minute = 60
|
29
|
+
hour = minute * 60
|
30
|
+
day = hour * 24
|
31
|
+
month = day * 31
|
32
|
+
year = month * 12
|
33
|
+
century = year * 100
|
33
34
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
35
|
+
case
|
36
|
+
when seconds < minute
|
37
|
+
'instant'
|
38
|
+
when seconds < hour
|
39
|
+
"#{1 + (seconds / minute).ceil} minutes"
|
40
|
+
when seconds < day
|
41
|
+
"#{1 + (seconds / hour).ceil} hours"
|
42
|
+
when seconds < month
|
43
|
+
"#{1 + (seconds / day).ceil} days"
|
44
|
+
when seconds < year
|
45
|
+
"#{1 + (seconds / month).ceil} months"
|
46
|
+
when seconds < century
|
47
|
+
"#{1 + (seconds / year).ceil} years"
|
48
|
+
else
|
49
|
+
'centuries'
|
50
|
+
end
|
49
51
|
end
|
50
52
|
end
|
51
53
|
end
|
data/lib/zxcvbn/data.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'zxcvbn/dictionary_ranker'
|
3
|
+
|
4
|
+
module Zxcvbn
|
5
|
+
class Data
|
6
|
+
def initialize
|
7
|
+
@ranked_dictionaries = DictionaryRanker.rank_dictionaries(
|
8
|
+
"english" => read_word_list("english.txt"),
|
9
|
+
"female_names" => read_word_list("female_names.txt"),
|
10
|
+
"male_names" => read_word_list("male_names.txt"),
|
11
|
+
"passwords" => read_word_list("passwords.txt"),
|
12
|
+
"surnames" => read_word_list("surnames.txt")
|
13
|
+
)
|
14
|
+
@adjacency_graphs = JSON.load(DATA_PATH.join('adjacency_graphs.json').read)
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :ranked_dictionaries, :adjacency_graphs
|
18
|
+
|
19
|
+
def add_word_list(name, list)
|
20
|
+
@ranked_dictionaries[name] = DictionaryRanker.rank_dictionary(list)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def read_word_list(file)
|
26
|
+
DATA_PATH.join("frequency_lists", file).read.split
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/zxcvbn/entropy.rb
CHANGED
data/lib/zxcvbn/matchers/date.rb
CHANGED
data/lib/zxcvbn/matchers/year.rb
CHANGED
data/lib/zxcvbn/math.rb
CHANGED
@@ -41,14 +41,14 @@ module Zxcvbn
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def average_degree_for_graph(graph_name)
|
44
|
-
graph =
|
44
|
+
graph = data.adjacency_graphs[graph_name]
|
45
45
|
degrees = graph.map { |_, neighbors| neighbors.compact.size }
|
46
46
|
sum = degrees.inject(0, :+)
|
47
47
|
sum.to_f / graph.size
|
48
48
|
end
|
49
49
|
|
50
50
|
def starting_positions_for_graph(graph_name)
|
51
|
-
|
51
|
+
data.adjacency_graphs[graph_name].length
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
data/lib/zxcvbn/omnimatch.rb
CHANGED
@@ -1,6 +1,17 @@
|
|
1
|
+
require 'zxcvbn/dictionary_ranker'
|
2
|
+
require 'zxcvbn/matchers/dictionary'
|
3
|
+
require 'zxcvbn/matchers/l33t'
|
4
|
+
require 'zxcvbn/matchers/spatial'
|
5
|
+
require 'zxcvbn/matchers/digits'
|
6
|
+
require 'zxcvbn/matchers/repeat'
|
7
|
+
require 'zxcvbn/matchers/sequences'
|
8
|
+
require 'zxcvbn/matchers/year'
|
9
|
+
require 'zxcvbn/matchers/date'
|
10
|
+
|
1
11
|
module Zxcvbn
|
2
12
|
class Omnimatch
|
3
|
-
def initialize
|
13
|
+
def initialize(data)
|
14
|
+
@data = data
|
4
15
|
@matchers = build_matchers
|
5
16
|
end
|
6
17
|
|
@@ -24,14 +35,14 @@ module Zxcvbn
|
|
24
35
|
|
25
36
|
def build_matchers
|
26
37
|
matchers = []
|
27
|
-
dictionary_matchers =
|
38
|
+
dictionary_matchers = @data.ranked_dictionaries.map do |name, dictionary|
|
28
39
|
Matchers::Dictionary.new(name, dictionary)
|
29
40
|
end
|
30
41
|
l33t_matcher = Matchers::L33t.new(dictionary_matchers)
|
31
42
|
matchers += dictionary_matchers
|
32
43
|
matchers += [
|
33
44
|
l33t_matcher,
|
34
|
-
Matchers::Spatial.new(
|
45
|
+
Matchers::Spatial.new(@data.adjacency_graphs),
|
35
46
|
Matchers::Digits.new,
|
36
47
|
Matchers::Repeat.new,
|
37
48
|
Matchers::Sequences.new,
|
@@ -1,10 +1,12 @@
|
|
1
1
|
require 'benchmark'
|
2
|
+
require 'zxcvbn/omnimatch'
|
3
|
+
require 'zxcvbn/scorer'
|
2
4
|
|
3
5
|
module Zxcvbn
|
4
6
|
class PasswordStrength
|
5
|
-
def initialize
|
6
|
-
@omnimatch = Omnimatch.new
|
7
|
-
@scorer = Scorer.new
|
7
|
+
def initialize(data)
|
8
|
+
@omnimatch = Omnimatch.new(data)
|
9
|
+
@scorer = Scorer.new(data)
|
8
10
|
end
|
9
11
|
|
10
12
|
def test(password, user_inputs = [])
|
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,35 @@
|
|
1
|
+
require 'zxcvbn/data'
|
2
|
+
require 'zxcvbn/password_strength'
|
3
|
+
|
4
|
+
module Zxcvbn
|
5
|
+
# Allows you to test the strength of multiple passwords without reading and
|
6
|
+
# parsing the dictionary data from disk each test. Dictionary data is read
|
7
|
+
# once from disk and stored in memory for the life of the Tester object.
|
8
|
+
#
|
9
|
+
# Example:
|
10
|
+
#
|
11
|
+
# tester = Zxcvbn::Tester.new
|
12
|
+
#
|
13
|
+
# tester.add_word_lists("custom" => ["words"])
|
14
|
+
#
|
15
|
+
# tester.test("password 1")
|
16
|
+
# tester.test("password 2")
|
17
|
+
# tester.test("password 3")
|
18
|
+
class Tester
|
19
|
+
def initialize
|
20
|
+
@data = Data.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def test(password, user_inputs = [])
|
24
|
+
PasswordStrength.new(@data).test(password, user_inputs)
|
25
|
+
end
|
26
|
+
|
27
|
+
def add_word_lists(lists)
|
28
|
+
lists.each_pair {|name, words| @data.add_word_list(name, words)}
|
29
|
+
end
|
30
|
+
|
31
|
+
def inspect
|
32
|
+
"#<#{self.class}:0x#{self.__id__.to_s(16)}>"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/zxcvbn/version.rb
CHANGED
@@ -2,7 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Zxcvbn::Matchers::Dictionary do
|
4
4
|
let(:matcher) { described_class.new('english', dictionary) }
|
5
|
-
let(:dictionary) { Zxcvbn::
|
5
|
+
let(:dictionary) { Zxcvbn::Data.new.ranked_dictionaries['english'] }
|
6
6
|
|
7
7
|
it 'finds all the matches' do
|
8
8
|
matches = matcher.matches('whatisinit')
|
data/spec/matchers/l33t_spec.rb
CHANGED
@@ -2,7 +2,7 @@ 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
|
@@ -2,7 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Zxcvbn::Matchers::Spatial do
|
4
4
|
let(:matcher) { Zxcvbn::Matchers::Spatial.new(graphs) }
|
5
|
-
let(:graphs) { Zxcvbn::
|
5
|
+
let(:graphs) { Zxcvbn::Data.new.adjacency_graphs }
|
6
6
|
|
7
7
|
describe '#matches' do
|
8
8
|
let(:matches) { matcher.matches('rtyikm') }
|
data/spec/omnimatch_spec.rb
CHANGED
@@ -6,6 +6,9 @@ describe Zxcvbn::Entropy do
|
|
6
6
|
let(:entropy) {
|
7
7
|
Class.new do
|
8
8
|
include Zxcvbn::Entropy
|
9
|
+
def data
|
10
|
+
Zxcvbn::Data.new
|
11
|
+
end
|
9
12
|
end.new
|
10
13
|
}
|
11
14
|
|
@@ -163,7 +166,7 @@ describe Zxcvbn::Entropy do
|
|
163
166
|
context 'when keyboard is qwerty' do
|
164
167
|
it 'should return the correct entropy' do
|
165
168
|
match.graph = 'qwerty'
|
166
|
-
|
169
|
+
|
167
170
|
entropy.spatial_entropy(match).should eql 11.562242424221074
|
168
171
|
end
|
169
172
|
end
|
data/spec/scoring/math_spec.rb
CHANGED
data/spec/tester_spec.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Zxcvbn::Tester do
|
4
|
+
let(:tester) { Zxcvbn::Tester.new }
|
5
|
+
|
6
|
+
TEST_PASSWORDS.each do |password|
|
7
|
+
it "gives back the same score for #{password}" do
|
8
|
+
ruby_result = tester.test(password)
|
9
|
+
js_result = js_zxcvbn(password)
|
10
|
+
|
11
|
+
ruby_result.calc_time.should_not be_nil
|
12
|
+
ruby_result.password.should eq js_result['password']
|
13
|
+
ruby_result.entropy.should eq js_result['entropy']
|
14
|
+
ruby_result.crack_time.should eq js_result['crack_time']
|
15
|
+
ruby_result.crack_time_display.should eq js_result['crack_time_display']
|
16
|
+
ruby_result.score.should eq js_result['score']
|
17
|
+
ruby_result.pattern.should eq js_result['pattern']
|
18
|
+
ruby_result.match_sequence.count.should eq js_result['match_sequence'].count
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'with a custom user dictionary' do
|
23
|
+
it 'scores them against the user dictionary' do
|
24
|
+
result = tester.test('themeforest', ['themeforest'])
|
25
|
+
result.entropy.should eq 0
|
26
|
+
result.score.should eq 0
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'matches l33t substitutions on this dictionary' do
|
30
|
+
result = tester.test('th3m3for3st', ['themeforest'])
|
31
|
+
result.entropy.should eq 1
|
32
|
+
result.score.should eq 0
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'with a custom global dictionary' do
|
37
|
+
before { tester.add_word_lists('envato' => ['envato']) }
|
38
|
+
|
39
|
+
it 'scores them against the dictionary' do
|
40
|
+
result = tester.test('envato')
|
41
|
+
result.entropy.should eq 0
|
42
|
+
result.score.should eq 0
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'nil password' do
|
47
|
+
specify do
|
48
|
+
expect { tester.test(nil) }.to_not raise_error
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/spec/zxcvbn_spec.rb
CHANGED
@@ -1,49 +1,24 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe '
|
4
|
-
|
5
|
-
it
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
ruby_result.calc_time.should_not be_nil
|
10
|
-
ruby_result.password.should eq js_result['password']
|
11
|
-
ruby_result.entropy.should eq js_result['entropy']
|
12
|
-
ruby_result.crack_time.should eq js_result['crack_time']
|
13
|
-
ruby_result.crack_time_display.should eq js_result['crack_time_display']
|
14
|
-
ruby_result.score.should eq js_result['score']
|
15
|
-
ruby_result.pattern.should eq js_result['pattern']
|
16
|
-
ruby_result.match_sequence.count.should eq js_result['match_sequence'].count
|
3
|
+
describe 'Zxcvbn.test' do
|
4
|
+
context 'with a password' do
|
5
|
+
it 'returns a result' do
|
6
|
+
result = Zxcvbn.test('password')
|
7
|
+
expect(result.entropy).to_not be_nil
|
17
8
|
end
|
18
9
|
end
|
19
10
|
|
20
|
-
context 'with a
|
21
|
-
it '
|
22
|
-
result = Zxcvbn.test('
|
23
|
-
result.entropy.
|
24
|
-
result.score.should eq 0
|
25
|
-
end
|
26
|
-
|
27
|
-
it 'matches l33t substitutions on this dictionary' do
|
28
|
-
result = Zxcvbn.test('th3m3for3st', ['themeforest'])
|
29
|
-
result.entropy.should eq 1
|
30
|
-
result.score.should eq 0
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
context 'with a custom global dictionary' do
|
35
|
-
before { Zxcvbn.add_word_list('envato', ['envato']) }
|
36
|
-
|
37
|
-
it 'scores them against the dictionary' do
|
38
|
-
result = Zxcvbn.test('envato')
|
39
|
-
result.entropy.should eq 0
|
40
|
-
result.score.should eq 0
|
11
|
+
context 'with a password and user input' do
|
12
|
+
it 'returns a result' do
|
13
|
+
result = Zxcvbn.test('password', ['inputs'])
|
14
|
+
expect(result.entropy).to_not be_nil
|
41
15
|
end
|
42
16
|
end
|
43
17
|
|
44
|
-
context '
|
45
|
-
|
46
|
-
|
18
|
+
context 'with a password, user input and custom word lists' do
|
19
|
+
it 'returns a result' do
|
20
|
+
result = Zxcvbn.test('password', ['inputs'], {'list' => ['words']})
|
21
|
+
expect(result.entropy).to_not be_nil
|
47
22
|
end
|
48
23
|
end
|
49
|
-
end
|
24
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zxcvbn-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2015-
|
13
|
+
date: 2015-04-28 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: therubyracer
|
@@ -66,6 +66,7 @@ files:
|
|
66
66
|
- data/frequency_lists/surnames.txt
|
67
67
|
- lib/zxcvbn.rb
|
68
68
|
- lib/zxcvbn/crack_time.rb
|
69
|
+
- lib/zxcvbn/data.rb
|
69
70
|
- lib/zxcvbn/dictionary_ranker.rb
|
70
71
|
- lib/zxcvbn/entropy.rb
|
71
72
|
- lib/zxcvbn/match.rb
|
@@ -84,6 +85,7 @@ files:
|
|
84
85
|
- lib/zxcvbn/password_strength.rb
|
85
86
|
- lib/zxcvbn/score.rb
|
86
87
|
- lib/zxcvbn/scorer.rb
|
88
|
+
- lib/zxcvbn/tester.rb
|
87
89
|
- lib/zxcvbn/version.rb
|
88
90
|
- spec/dictionary_ranker_spec.rb
|
89
91
|
- spec/matchers/date_spec.rb
|
@@ -111,6 +113,7 @@ files:
|
|
111
113
|
- spec/support/js_source/scoring.coffee
|
112
114
|
- spec/support/js_source/scoring.js
|
113
115
|
- spec/support/matcher.rb
|
116
|
+
- spec/tester_spec.rb
|
114
117
|
- spec/zxcvbn_spec.rb
|
115
118
|
- zxcvbn-ruby.gemspec
|
116
119
|
homepage: http://github.com/envato/zxcvbn-ruby
|
@@ -127,7 +130,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
127
130
|
version: '0'
|
128
131
|
segments:
|
129
132
|
- 0
|
130
|
-
hash:
|
133
|
+
hash: 3120558835973257605
|
131
134
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
132
135
|
none: false
|
133
136
|
requirements:
|
@@ -136,7 +139,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
136
139
|
version: '0'
|
137
140
|
segments:
|
138
141
|
- 0
|
139
|
-
hash:
|
142
|
+
hash: 3120558835973257605
|
140
143
|
requirements: []
|
141
144
|
rubyforge_project:
|
142
145
|
rubygems_version: 1.8.23.2
|
@@ -170,4 +173,5 @@ test_files:
|
|
170
173
|
- spec/support/js_source/scoring.coffee
|
171
174
|
- spec/support/js_source/scoring.js
|
172
175
|
- spec/support/matcher.rb
|
176
|
+
- spec/tester_spec.rb
|
173
177
|
- spec/zxcvbn_spec.rb
|