zxcvbn-ruby 0.0.3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|