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 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 "zxcvbn-ruby", :require => 'zxcvbn'
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/match'
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
- def add_word_list(name, list)
44
- RANKED_DICTIONARIES[name] = DictionaryRanker.rank_dictionary(list)
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
@@ -1,51 +1,53 @@
1
- module Zxcvbn::CrackTime
2
- SINGLE_GUESS = 0.010
3
- NUM_ATTACKERS = 100
1
+ module Zxcvbn
2
+ module CrackTime
3
+ SINGLE_GUESS = 0.010
4
+ NUM_ATTACKERS = 100
4
5
 
5
- SECONDS_PER_GUESS = SINGLE_GUESS / NUM_ATTACKERS
6
+ SECONDS_PER_GUESS = SINGLE_GUESS / NUM_ATTACKERS
6
7
 
7
- def entropy_to_crack_time(entropy)
8
- 0.5 * (2 ** entropy) * SECONDS_PER_GUESS
9
- end
8
+ def entropy_to_crack_time(entropy)
9
+ 0.5 * (2 ** entropy) * SECONDS_PER_GUESS
10
+ end
10
11
 
11
- def crack_time_to_score(seconds)
12
- case
13
- when seconds < 10**2
14
- 0
15
- when seconds < 10**4
16
- 1
17
- when seconds < 10**6
18
- 2
19
- when seconds < 10**8
20
- 3
21
- else
22
- 4
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
- def display_time(seconds)
27
- minute = 60
28
- hour = minute * 60
29
- day = hour * 24
30
- month = day * 31
31
- year = month * 12
32
- century = year * 100
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
- case
35
- when seconds < minute
36
- 'instant'
37
- when seconds < hour
38
- "#{1 + (seconds / minute).ceil} minutes"
39
- when seconds < day
40
- "#{1 + (seconds / hour).ceil} hours"
41
- when seconds < month
42
- "#{1 + (seconds / day).ceil} days"
43
- when seconds < year
44
- "#{1 + (seconds / month).ceil} months"
45
- when seconds < century
46
- "#{1 + (seconds / year).ceil} years"
47
- else
48
- 'centuries'
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
@@ -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
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module Zxcvbn
4
2
  class DictionaryRanker
5
3
  def self.rank_dictionaries(lists)
@@ -1,3 +1,5 @@
1
+ require 'zxcvbn/math'
2
+
1
3
  module Zxcvbn::Entropy
2
4
  include Zxcvbn::Math
3
5
 
@@ -1,3 +1,5 @@
1
+ require 'zxcvbn/matchers/regex_helpers'
2
+
1
3
  module Zxcvbn
2
4
  module Matchers
3
5
  class Date
@@ -1,3 +1,5 @@
1
+ require 'zxcvbn/match'
2
+
1
3
  module Zxcvbn
2
4
  module Matchers
3
5
  # Given a password and a dictionary, match on any sequential segment of
@@ -1,3 +1,5 @@
1
+ require 'zxcvbn/matchers/regex_helpers'
2
+
1
3
  module Zxcvbn
2
4
  module Matchers
3
5
  class Digits
@@ -1,3 +1,5 @@
1
+ require 'zxcvbn/match'
2
+
1
3
  module Zxcvbn
2
4
  module Matchers
3
5
  module RegexHelpers
@@ -1,3 +1,5 @@
1
+ require 'zxcvbn/match'
2
+
1
3
  module Zxcvbn
2
4
  module Matchers
3
5
  class Repeat
@@ -1,3 +1,5 @@
1
+ require 'zxcvbn/match'
2
+
1
3
  module Zxcvbn
2
4
  module Matchers
3
5
  class Sequences
@@ -1,3 +1,5 @@
1
+ require 'zxcvbn/match'
2
+
1
3
  module Zxcvbn
2
4
  module Matchers
3
5
  class Spatial
@@ -1,3 +1,5 @@
1
+ require 'zxcvbn/matchers/regex_helpers'
2
+
1
3
  module Zxcvbn
2
4
  module Matchers
3
5
  class Year
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 = Zxcvbn::ADJACENCY_GRAPHS[graph_name]
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
- Zxcvbn::ADJACENCY_GRAPHS[graph_name].length
51
+ data.adjacency_graphs[graph_name].length
52
52
  end
53
53
  end
54
54
  end
@@ -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 = RANKED_DICTIONARIES.map do |name, dictionary|
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(ADJACENCY_GRAPHS),
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
@@ -1,3 +1,3 @@
1
1
  module Zxcvbn
2
- VERSION = "0.0.3"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -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::RANKED_DICTIONARIES['english'] }
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')
@@ -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::RANKED_DICTIONARIES['english'] }
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::ADJACENCY_GRAPHS }
5
+ let(:graphs) { Zxcvbn::Data.new.adjacency_graphs }
6
6
 
7
7
  describe '#matches' do
8
8
  let(:matches) { matcher.matches('rtyikm') }
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe Zxcvbn::Omnimatch do
4
4
  before(:all) do
5
- @omnimatch = described_class.new
5
+ @omnimatch = described_class.new(Zxcvbn::Data.new)
6
6
  end
7
7
 
8
8
  def omnimatch(password)
@@ -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
@@ -3,6 +3,10 @@ require 'spec_helper'
3
3
  describe Zxcvbn::Math do
4
4
  include Zxcvbn::Math
5
5
 
6
+ def data
7
+ Zxcvbn::Data.new
8
+ end
9
+
6
10
  describe '#bruteforce_cardinality' do
7
11
  context 'when empty password' do
8
12
  it 'should return 0 if empty password' do
@@ -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 'zxcvbn over a set of example passwords' do
4
- TEST_PASSWORDS.each do |password|
5
- it "gives back the same score for #{password}" do
6
- ruby_result = Zxcvbn.test(password)
7
- js_result = js_zxcvbn(password)
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 custom user dictionary' do
21
- it 'scores them against the user dictionary' do
22
- result = Zxcvbn.test('themeforest', ['themeforest'])
23
- result.entropy.should eq 0
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 'nil password' do
45
- specify do
46
- expect { Zxcvbn.test(nil) }.to_not raise_error
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.3
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-02-19 00:00:00.000000000 Z
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: -1798543645320205772
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: -1798543645320205772
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