zxcvbn-ruby 1.2.1 → 1.2.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e00f4d1e74aab0e662091f1adbcd1e86abeb11bfe3ac0440f0d5c83988b16649
4
- data.tar.gz: 30164328d27edf8e12a8fcb0d79a3eea47c910f0f2cd6cf1d56b9aa1da515b9e
3
+ metadata.gz: 62e582cfff03a7e2075efea27771a6e8a542e799546b67ab7e342df77fc503e5
4
+ data.tar.gz: 9ac56545038db446e0dac7421f8d5ebc22bdb62abc808ba9801f18fbaf474257
5
5
  SHA512:
6
- metadata.gz: e50ab32776c2ab1aed4bb5adccd6067efad60bf203f9d3f7e700e03ba5ecd60e8fb06725fa95ca44683045f2f5471f00a0af586cd8ded69bd985e4f14a09e9e5
7
- data.tar.gz: 107abf8ef445ad4462c62801d53b14c6d6f695707ecb40819973b2e949eaf385dee15bf3e8326138febc088df2c069f959a937e852c0dce56acda6b3bbad051d
6
+ metadata.gz: 15a3f510c69b660169c08e7552f04ba5072db4865977aa436e3b673cd2053dadd0af5a5d143ea936a86993c1cbe48653957384bf97aac7056d80d31a99c986a9
7
+ data.tar.gz: 6c69b9142475710c4ce02ccbef7da7faaa4c54426118d29383f55d20c1b9c1d62459bd8de842f37830b22568ba41cb4a5717dc6531d9c4edb7625691cf3d1229
data/CHANGELOG.md CHANGED
@@ -6,7 +6,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
- [Unreleased]: https://github.com/envato/zxcvbn-ruby/compare/v1.2.1...HEAD
9
+ [Unreleased]: https://github.com/envato/zxcvbn-ruby/compare/v1.2.3...HEAD
10
+
11
+ ## [1.2.3] - 2025-12-07
12
+
13
+ ### Changed
14
+ - Address linting issues ([#52])
15
+ - Address style issues ([#53], [#54], [#55])
16
+
17
+ [1.2.3]: https://github.com/envato/zxcvbn-ruby/compare/v1.2.2...v1.2.3
18
+ [#52]: https://github.com/envato/zxcvbn-ruby/pull/52
19
+ [#53]: https://github.com/envato/zxcvbn-ruby/pull/53
20
+ [#54]: https://github.com/envato/zxcvbn-ruby/pull/54
21
+ [#55]: https://github.com/envato/zxcvbn-ruby/pull/55
22
+
23
+ ## [1.2.2] - 2025-12-06
24
+
25
+ ### Changed
26
+ - Address layout and frozen string literal issues ([#49])
27
+
28
+ [1.2.2]: https://github.com/envato/zxcvbn-ruby/compare/v1.2.1...v1.2.2
29
+ [#49]: https://github.com/envato/zxcvbn-ruby/pull/49
10
30
 
11
31
  ## [1.2.1] - 2025-12-05
12
32
 
@@ -14,7 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
14
34
  - Removed the dependency on the Ruby `benchmark` module ([#44]).
15
35
  - Tests are no longer included in the gem package ([#45]).
16
36
 
17
- [1.2.1]: https://github.com/envato/zxcvbn-ruby/compare/v1.2.0...v.1.2.1
37
+ [1.2.1]: https://github.com/envato/zxcvbn-ruby/compare/v1.2.0...v1.2.1
18
38
  [#44]: https://github.com/envato/zxcvbn-ruby/pull/44
19
39
  [#45]: https://github.com/envato/zxcvbn-ruby/pull/45
20
40
 
@@ -27,7 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
27
47
  - Use [mini\_racer] for running JavaScript specs (thanks [@RSO] ([#33]))
28
48
  - Moved CI to GitHub Actions ([#34])
29
49
 
30
- [1.2.0]: https://github.com/envato/zxcvbn-ruby/compare/v1.1.0...v.1.2.0
50
+ [1.2.0]: https://github.com/envato/zxcvbn-ruby/compare/v1.1.0...v1.2.0
31
51
  [@rso]: https://github.com/RSO
32
52
  [mini\_racer]: https://rubygems.org/gems/mini_racer/
33
53
  [#32]: https://github.com/envato/zxcvbn-ruby/pull/32
data/README.md CHANGED
@@ -126,11 +126,6 @@ attacker. For this reason we advise you not to store the results of
126
126
  - [GitHub project](https://github.com/envato/zxcvbn-ruby)
127
127
  - Bug reports and feature requests are welcome via [GitHub Issues](https://github.com/envato/zxcvbn-ruby/issues)
128
128
 
129
- ## Maintainers
130
-
131
- - [Pete Johns](https://github.com/johnsyweb)
132
- - [Steve Hodgkiss](https://github.com/stevehodgkiss)
133
-
134
129
  ## Authors
135
130
 
136
131
  - [Steve Hodgkiss](https://github.com/stevehodgkiss)
@@ -162,18 +157,14 @@ For larger new features: Do everything as above, but first also make contact wit
162
157
 
163
158
  ## About [![code with heart by Envato](https://img.shields.io/badge/%3C%2F%3E%20with%20%E2%99%A5%20by-Envato-ff69b4.svg?style=flat-square)](https://github.com/envato/zxcvbn-ruby)
164
159
 
165
- This project is maintained by the [Envato engineering team][webuild] and funded by [Envato][envato].
166
-
167
- [<img src="http://opensource.envato.com/images/envato-oss-readme-logo.png" alt="Envato logo">][envato]
160
+ This project is maintained by the Envato engineering team and funded by [Envato][envato].
168
161
 
169
162
  Encouraging the use and creation of open source software is one of the ways we
170
- serve our community. See [our other projects][oss] or [come work with us][careers]
163
+ serve our community. Perhaps [come work with us][careers]
171
164
  where you'll find an incredibly diverse, intelligent and capable group of people
172
165
  who help make our company succeed and make our workplace fun, friendly and
173
166
  happy.
174
167
 
175
168
  [careers]: https://envato.com/careers/?utm_source=github
176
169
  [envato]: https://envato.com?utm_source=github
177
- [oss]: https://opensource.envato.com/?utm_source=github
178
- [webuild]: https://webuild.envato.com?utm_source=github
179
170
  [zxcvbn.js]: https://github.com/dropbox/zxcvbn
data/lib/zxcvbn/clock.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zxcvbn
2
4
  module Clock
3
5
  def self.realtime
@@ -7,4 +9,3 @@ module Zxcvbn
7
9
  end
8
10
  end
9
11
  end
10
-
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zxcvbn
2
4
  module CrackTime
3
5
  SINGLE_GUESS = 0.010
@@ -6,18 +8,17 @@ module Zxcvbn
6
8
  SECONDS_PER_GUESS = SINGLE_GUESS / NUM_ATTACKERS
7
9
 
8
10
  def entropy_to_crack_time(entropy)
9
- 0.5 * (2 ** entropy) * SECONDS_PER_GUESS
11
+ 0.5 * (2**entropy) * SECONDS_PER_GUESS
10
12
  end
11
13
 
12
14
  def crack_time_to_score(seconds)
13
- case
14
- when seconds < 10**2
15
+ if seconds < 10**2
15
16
  0
16
- when seconds < 10**4
17
+ elsif seconds < 10**4
17
18
  1
18
- when seconds < 10**6
19
+ elsif seconds < 10**6
19
20
  2
20
- when seconds < 10**8
21
+ elsif seconds < 10**8
21
22
  3
22
23
  else
23
24
  4
@@ -32,22 +33,21 @@ module Zxcvbn
32
33
  year = month * 12
33
34
  century = year * 100
34
35
 
35
- case
36
- when seconds < minute
36
+ if seconds < minute
37
37
  'instant'
38
- when seconds < hour
38
+ elsif seconds < hour
39
39
  "#{1 + (seconds / minute).ceil} minutes"
40
- when seconds < day
40
+ elsif seconds < day
41
41
  "#{1 + (seconds / hour).ceil} hours"
42
- when seconds < month
42
+ elsif seconds < month
43
43
  "#{1 + (seconds / day).ceil} days"
44
- when seconds < year
44
+ elsif seconds < year
45
45
  "#{1 + (seconds / month).ceil} months"
46
- when seconds < century
46
+ elsif seconds < century
47
47
  "#{1 + (seconds / year).ceil} years"
48
48
  else
49
49
  'centuries'
50
50
  end
51
51
  end
52
52
  end
53
- end
53
+ end
data/lib/zxcvbn/data.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'json'
2
4
  require 'zxcvbn/dictionary_ranker'
3
5
 
@@ -5,11 +7,11 @@ module Zxcvbn
5
7
  class Data
6
8
  def initialize
7
9
  @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")
10
+ 'english' => read_word_list('english.txt'),
11
+ 'female_names' => read_word_list('female_names.txt'),
12
+ 'male_names' => read_word_list('male_names.txt'),
13
+ 'passwords' => read_word_list('passwords.txt'),
14
+ 'surnames' => read_word_list('surnames.txt')
13
15
  )
14
16
  @adjacency_graphs = JSON.load(DATA_PATH.join('adjacency_graphs.json').read)
15
17
  end
@@ -23,7 +25,7 @@ module Zxcvbn
23
25
  private
24
26
 
25
27
  def read_word_list(file)
26
- DATA_PATH.join("frequency_lists", file).read.split
28
+ DATA_PATH.join('frequency_lists', file).read.split
27
29
  end
28
30
  end
29
31
  end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zxcvbn
2
4
  class DictionaryRanker
3
5
  def self.rank_dictionaries(lists)
4
- lists.each_with_object({}) do |(dict_name, words), dictionaries|
5
- dictionaries[dict_name] = rank_dictionary(words)
6
+ lists.transform_values do |words|
7
+ rank_dictionary(words)
6
8
  end
7
9
  end
8
10
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'zxcvbn/math'
2
4
 
3
5
  module Zxcvbn::Entropy
@@ -6,24 +8,25 @@ module Zxcvbn::Entropy
6
8
  def calc_entropy(match)
7
9
  return match.entropy unless match.entropy.nil?
8
10
 
9
- match.entropy = case match.pattern
10
- when 'repeat'
11
- repeat_entropy(match)
12
- when 'sequence'
13
- sequence_entropy(match)
14
- when 'digits'
15
- digits_entropy(match)
16
- when 'year'
17
- year_entropy(match)
18
- when 'date'
19
- date_entropy(match)
20
- when 'spatial'
21
- spatial_entropy(match)
22
- when 'dictionary'
23
- dictionary_entropy(match)
24
- else
25
- 0
26
- end
11
+ match.entropy =
12
+ case match.pattern
13
+ when 'repeat'
14
+ repeat_entropy(match)
15
+ when 'sequence'
16
+ sequence_entropy(match)
17
+ when 'digits'
18
+ digits_entropy(match)
19
+ when 'year'
20
+ year_entropy(match)
21
+ when 'date'
22
+ date_entropy(match)
23
+ when 'spatial'
24
+ spatial_entropy(match)
25
+ when 'dictionary'
26
+ dictionary_entropy(match)
27
+ else
28
+ 0
29
+ end
27
30
  end
28
31
 
29
32
  def repeat_entropy(match)
@@ -33,41 +36,41 @@ module Zxcvbn::Entropy
33
36
 
34
37
  def sequence_entropy(match)
35
38
  first_char = match.token[0]
36
- base_entropy = if ['a', '1'].include?(first_char)
37
- 1
38
- elsif first_char.match(/\d/)
39
- lg(10)
40
- elsif first_char.match(/[a-z]/)
41
- lg(26)
42
- else
43
- lg(26) + 1
44
- end
39
+ base_entropy =
40
+ if ['a', '1'].include?(first_char)
41
+ 1
42
+ elsif first_char.match(/\d/)
43
+ lg(10)
44
+ elsif first_char.match(/[a-z]/)
45
+ lg(26)
46
+ else
47
+ lg(26) + 1
48
+ end
45
49
  base_entropy += 1 unless match.ascending
46
50
  base_entropy + lg(match.token.length)
47
51
  end
48
52
 
49
53
  def digits_entropy(match)
50
- lg(10 ** match.token.length)
54
+ lg(10**match.token.length)
51
55
  end
52
56
 
53
57
  NUM_YEARS = 119 # years match against 1900 - 2019
54
58
  NUM_MONTHS = 12
55
59
  NUM_DAYS = 31
56
60
 
57
- def year_entropy(match)
61
+ def year_entropy(_match)
58
62
  lg(NUM_YEARS)
59
63
  end
60
64
 
61
65
  def date_entropy(match)
62
- if match.year < 100
63
- entropy = lg(NUM_DAYS * NUM_MONTHS * 100)
64
- else
65
- entropy = lg(NUM_DAYS * NUM_MONTHS * NUM_YEARS)
66
- end
66
+ entropy =
67
+ if match.year < 100
68
+ lg(NUM_DAYS * NUM_MONTHS * 100)
69
+ else
70
+ lg(NUM_DAYS * NUM_MONTHS * NUM_YEARS)
71
+ end
67
72
 
68
- if match.separator
69
- entropy += 2
70
- end
73
+ entropy += 2 if match.separator
71
74
 
72
75
  entropy
73
76
  end
@@ -80,18 +83,18 @@ module Zxcvbn::Entropy
80
83
  match.base_entropy + match.uppercase_entropy + match.l33t_entropy
81
84
  end
82
85
 
83
- START_UPPER = /^[A-Z][^A-Z]+$/
84
- END_UPPER = /^[^A-Z]+[A-Z]$/
85
- ALL_UPPER = /^[A-Z]+$/
86
- ALL_LOWER = /^[a-z]+$/
86
+ START_UPPER = /^[A-Z][^A-Z]+$/.freeze
87
+ END_UPPER = /^[^A-Z]+[A-Z]$/.freeze
88
+ ALL_UPPER = /^[A-Z]+$/.freeze
89
+ ALL_LOWER = /^[a-z]+$/.freeze
87
90
 
88
91
  def extra_uppercase_entropy(match)
89
92
  word = match.token
90
93
  [START_UPPER, END_UPPER, ALL_UPPER].each do |regex|
91
94
  return 1 if word.match(regex)
92
95
  end
93
- num_upper = word.chars.count{|c| c.match(/[A-Z]/) }
94
- num_lower = word.chars.count{|c| c.match(/[a-z]/) }
96
+ num_upper = word.chars.count { |c| c.match(/[A-Z]/) }
97
+ num_lower = word.chars.count { |c| c.match(/[a-z]/) }
95
98
  possibilities = 0
96
99
  (0..[num_upper, num_lower].min).each do |i|
97
100
  possibilities += nCk(num_upper + num_lower, i)
@@ -102,20 +105,21 @@ module Zxcvbn::Entropy
102
105
  def extra_l33t_entropy(match)
103
106
  word = match.token
104
107
  return 0 unless match.l33t
108
+
105
109
  possibilities = 0
106
110
  match.sub.each do |subbed, unsubbed|
107
- num_subbed = word.chars.count{|c| c == subbed}
108
- num_unsubbed = word.chars.count{|c| c == unsubbed}
111
+ num_subbed = word.chars.count { |c| c == subbed }
112
+ num_unsubbed = word.chars.count { |c| c == unsubbed }
109
113
  (0..[num_subbed, num_unsubbed].min).each do |i|
110
114
  possibilities += nCk(num_subbed + num_unsubbed, i)
111
115
  end
112
116
  end
113
117
  entropy = lg(possibilities)
114
- entropy == 0 ? 1 : entropy
118
+ entropy.zero? ? 1 : entropy
115
119
  end
116
120
 
117
121
  def spatial_entropy(match)
118
- if %w|qwerty dvorak|.include? match.graph
122
+ if %w[qwerty dvorak].include? match.graph
119
123
  starting_positions = starting_positions_for_graph('qwerty')
120
124
  average_degree = average_degree_for_graph('qwerty')
121
125
  else
@@ -131,7 +135,7 @@ module Zxcvbn::Entropy
131
135
  (2..token_length).each do |i|
132
136
  possible_turns = [turns, i - 1].min
133
137
  (1..possible_turns).each do |j|
134
- possibilities += nCk(i - 1, j - 1) * starting_positions * average_degree ** j
138
+ possibilities += nCk(i - 1, j - 1) * starting_positions * average_degree**j
135
139
  end
136
140
  end
137
141
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zxcvbn
2
4
  class Feedback
3
5
  attr_accessor :warning, :suggestions
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'zxcvbn/entropy'
2
4
  require 'zxcvbn/feedback'
3
5
 
@@ -16,14 +18,14 @@ module Zxcvbn
16
18
 
17
19
  def self.get_feedback(score, sequence)
18
20
  # starting feedback
19
- return DEFAULT_FEEDBACK if sequence.length.zero?
21
+ return DEFAULT_FEEDBACK if sequence.empty?
20
22
 
21
23
  # no feedback if score is good or great.
22
24
  return EMPTY_FEEDBACK if score > 2
23
25
 
24
26
  # tie feedback to the longest match for longer sequences
25
27
  longest_match = sequence[0]
26
- for match in sequence[1..-1]
28
+ sequence[1..-1].each do |match|
27
29
  longest_match = match if match.token.length > longest_match.token.length
28
30
  end
29
31
 
@@ -45,7 +47,6 @@ module Zxcvbn
45
47
  get_dictionary_match_feedback match, is_sole_match
46
48
 
47
49
  when 'spatial'
48
- layout = match.graph.upcase
49
50
  warning = if match.turns == 1
50
51
  'Straight rows of keys are easy to guess'
51
52
  else
data/lib/zxcvbn/match.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'ostruct'
2
4
 
3
5
  module Zxcvbn
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'zxcvbn/matchers/regex_helpers'
2
4
 
3
5
  module Zxcvbn
@@ -5,23 +7,23 @@ module Zxcvbn
5
7
  class Date
6
8
  include RegexHelpers
7
9
 
8
- YEAR_SUFFIX = /
10
+ YEAR_SUFFIX = %r{
9
11
  ( \d{1,2} ) # day or month
10
- ( \s | \- | \/ | \\ | \_ | \. ) # separator
12
+ ( \s | - | / | \\ | _ | \. ) # separator
11
13
  ( \d{1,2} ) # month or day
12
14
  \2 # same separator
13
15
  ( 19\d{2} | 200\d | 201\d | \d{2} ) # year
14
- /x
16
+ }x.freeze
15
17
 
16
- YEAR_PREFIX = /
18
+ YEAR_PREFIX = %r{
17
19
  ( 19\d{2} | 200\d | 201\d | \d{2} ) # year
18
- ( \s | - | \/ | \\ | _ | \. ) # separator
20
+ ( \s | - | / | \\ | _ | \. ) # separator
19
21
  ( \d{1,2} ) # day or month
20
22
  \2 # same separator
21
23
  ( \d{1,2} ) # month or day
22
- /x
24
+ }x.freeze
23
25
 
24
- WITHOUT_SEPARATOR = /\d{4,8}/
26
+ WITHOUT_SEPARATOR = /\d{4,8}/.freeze
25
27
 
26
28
  def matches(password)
27
29
  match_with_separator(password) + match_without_separator(password)
@@ -52,9 +54,11 @@ module Zxcvbn
52
54
 
53
55
  def match_without_separator(password)
54
56
  result = []
55
- re_match_all(WITHOUT_SEPARATOR, password) do |match, re_match|
57
+ re_match_all(WITHOUT_SEPARATOR, password) do |match, _re_match|
56
58
  extract_dates(match.token).each do |candidate|
57
- day, month, year = candidate[:day], candidate[:month], candidate[:year]
59
+ day = candidate[:day]
60
+ month = candidate[:month]
61
+ year = candidate[:year]
58
62
 
59
63
  match.pattern = 'date'
60
64
  match.day = day
@@ -71,11 +75,11 @@ module Zxcvbn
71
75
  dates = []
72
76
  date_patterns_for_length(token.length).map do |pattern|
73
77
  candidate = {
74
- :year => '',
75
- :month => '',
76
- :day => ''
78
+ year: +'',
79
+ month: +'',
80
+ day: +''
77
81
  }
78
- for i in 0...token.length
82
+ (0...token.length).each do |i|
79
83
  candidate[PATTERN_CHAR_TO_SYM[pattern[i]]] << token[i]
80
84
  end
81
85
  candidate.each do |component, value|
@@ -92,18 +96,18 @@ module Zxcvbn
92
96
  end
93
97
 
94
98
  DATE_PATTERN_FOR_LENGTH = {
95
- 8 => %w[ yyyymmdd ddmmyyyy mmddyyyy ],
96
- 7 => %w[ yyyymdd yyyymmd ddmyyyy dmmyyyy ],
97
- 6 => %w[ yymmdd ddmmyy mmddyy ],
98
- 5 => %w[ yymdd yymmd ddmyy dmmyy mmdyy mddyy ],
99
- 4 => %w[ yymd dmyy mdyy ]
100
- }
99
+ 8 => %w[yyyymmdd ddmmyyyy mmddyyyy].freeze,
100
+ 7 => %w[yyyymdd yyyymmd ddmyyyy dmmyyyy].freeze,
101
+ 6 => %w[yymmdd ddmmyy mmddyy].freeze,
102
+ 5 => %w[yymdd yymmd ddmyy dmmyy mmdyy mddyy].freeze,
103
+ 4 => %w[yymd dmyy mdyy].freeze
104
+ }.freeze
101
105
 
102
106
  PATTERN_CHAR_TO_SYM = {
103
107
  'y' => :year,
104
108
  'm' => :month,
105
109
  'd' => :day
106
- }
110
+ }.freeze
107
111
 
108
112
  def date_patterns_for_length(length)
109
113
  DATE_PATTERN_FOR_LENGTH[length] || []
@@ -112,6 +116,7 @@ module Zxcvbn
112
116
  def valid_date?(day, month, year)
113
117
  return false if day > 31 || month > 12
114
118
  return false unless year >= 1900 && year <= 2019
119
+
115
120
  true
116
121
  end
117
122
 
@@ -120,7 +125,7 @@ module Zxcvbn
120
125
  end
121
126
 
122
127
  def expand_year(year)
123
- return year
128
+ year
124
129
  # Block dates with 2 digit years for now to be compatible with the JS version
125
130
  # return year unless year < 100
126
131
  # now = Time.now.year
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'zxcvbn/match'
2
4
 
3
5
  module Zxcvbn
@@ -18,19 +20,21 @@ module Zxcvbn
18
20
  (0..password_length).each do |i|
19
21
  (i...password_length).each do |j|
20
22
  word = lowercased_password[i..j]
21
- if @ranked_dictionary.has_key?(word)
22
- results << Match.new(:matched_word => word,
23
- :token => password[i..j],
24
- :i => i,
25
- :j => j,
26
- :rank => @ranked_dictionary[word],
27
- :pattern => 'dictionary',
28
- :dictionary_name => @name)
29
- end
23
+ next unless @ranked_dictionary.key?(word)
24
+
25
+ results << Match.new(
26
+ matched_word: word,
27
+ token: password[i..j],
28
+ i: i,
29
+ j: j,
30
+ rank: @ranked_dictionary[word],
31
+ pattern: 'dictionary',
32
+ dictionary_name: @name
33
+ )
30
34
  end
31
35
  end
32
36
  results
33
37
  end
34
38
  end
35
39
  end
36
- end
40
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'zxcvbn/matchers/regex_helpers'
2
4
 
3
5
  module Zxcvbn
@@ -5,7 +7,7 @@ module Zxcvbn
5
7
  class Digits
6
8
  include RegexHelpers
7
9
 
8
- DIGITS_REGEX = /\d{3,}/
10
+ DIGITS_REGEX = /\d{3,}/.freeze
9
11
 
10
12
  def matches(password)
11
13
  result = []
@@ -1,20 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zxcvbn
2
4
  module Matchers
3
5
  class L33t
4
6
  L33T_TABLE = {
5
- 'a' => ['4', '@'],
6
- 'b' => ['8'],
7
- 'c' => ['(', '{', '[', '<'],
8
- 'e' => ['3'],
9
- 'g' => ['6', '9'],
10
- 'i' => ['1', '!', '|'],
11
- 'l' => ['1', '|', '7'],
12
- 'o' => ['0'],
13
- 's' => ['$', '5'],
14
- 't' => ['+', '7'],
15
- 'x' => ['%'],
16
- 'z' => ['2']
17
- }
7
+ 'a' => ['4', '@'].freeze,
8
+ 'b' => ['8'].freeze,
9
+ 'c' => ['(', '{', '[', '<'].freeze,
10
+ 'e' => ['3'].freeze,
11
+ 'g' => ['6', '9'].freeze,
12
+ 'i' => ['1', '!', '|'].freeze,
13
+ 'l' => ['1', '|', '7'].freeze,
14
+ 'o' => ['0'].freeze,
15
+ 's' => ['$', '5'].freeze,
16
+ 't' => ['+', '7'].freeze,
17
+ 'x' => ['%'].freeze,
18
+ 'z' => ['2'].freeze
19
+ }.freeze
18
20
 
19
21
  def initialize(dictionary_matchers)
20
22
  @dictionary_matchers = dictionary_matchers
@@ -30,6 +32,7 @@ module Zxcvbn
30
32
  matcher.matches(subbed_password).each do |match|
31
33
  token = password[match.i..match.j]
32
34
  next if token.downcase == match.matched_word.downcase
35
+
33
36
  match_substitutions = {}
34
37
  substitution.each do |s, letter|
35
38
  match_substitutions[s] = letter if token.include?(s)
@@ -79,6 +82,7 @@ module Zxcvbn
79
82
 
80
83
  def find_substitutions(subs, table, keys)
81
84
  return subs if keys.empty?
85
+
82
86
  first_key = keys[0]
83
87
  rest_keys = keys[1..-1]
84
88
  next_subs = []
@@ -113,8 +117,8 @@ module Zxcvbn
113
117
  subs.each do |sub|
114
118
  assoc = sub.dup
115
119
 
116
- assoc.sort! rescue debugger
117
- label = assoc.map{|k, v| "#{k},#{v}"}.join('-')
120
+ assoc.sort!
121
+ label = assoc.map { |k, v| "#{k},#{v}" }.join('-')
118
122
  unless members.include?(label)
119
123
  members << label
120
124
  deduped << sub
@@ -124,4 +128,4 @@ module Zxcvbn
124
128
  end
125
129
  end
126
130
  end
127
- end
131
+ end
@@ -1,20 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zxcvbn
2
4
  module Matchers
3
5
  class L33t
4
6
  L33T_TABLE = {
5
- 'a' => ['4', '@'],
6
- 'b' => ['8'],
7
- 'c' => ['(', '{', '[', '<'],
8
- 'e' => ['3'],
9
- 'g' => ['6', '9'],
10
- 'i' => ['1', '!', '|'],
11
- 'l' => ['1', '|', '7'],
12
- 'o' => ['0'],
13
- 's' => ['$', '5'],
14
- 't' => ['+', '7'],
15
- 'x' => ['%'],
16
- 'z' => ['2']
17
- }
7
+ 'a' => ['4', '@'].freeze,
8
+ 'b' => ['8'].freeze,
9
+ 'c' => ['(', '{', '[', '<'].freeze,
10
+ 'e' => ['3'].freeze,
11
+ 'g' => ['6', '9'].freeze,
12
+ 'i' => ['1', '!', '|'].freeze,
13
+ 'l' => ['1', '|', '7'].freeze,
14
+ 'o' => ['0'].freeze,
15
+ 's' => ['$', '5'].freeze,
16
+ 't' => ['+', '7'].freeze,
17
+ 'x' => ['%'].freeze,
18
+ 'z' => ['2'].freeze
19
+ }.freeze
18
20
 
19
21
  def initialize(dictionary_matchers)
20
22
  @dictionary_matchers = dictionary_matchers
@@ -24,16 +26,15 @@ module Zxcvbn
24
26
  matches = []
25
27
  lowercased_password = password.downcase
26
28
  combinations_to_try = substitution_combinations(relevant_l33t_substitutions(lowercased_password))
27
- # debugger if password == 'abcdefghijk987654321'
28
- combinations_to_try.each do |substitution|
29
+ combinations_to_try.each do |substitutions|
29
30
  @dictionary_matchers.each do |matcher|
30
- subbed_password = substitute(lowercased_password, substitution)
31
+ subbed_password = substitute(lowercased_password, substitutions)
31
32
  matcher.matches(subbed_password).each do |match|
32
33
  token = lowercased_password[match.i..match.j]
33
34
  next if token == match.matched_word.downcase
34
- # debugger if token == '1'
35
+
35
36
  match_substitutions = {}
36
- substitution.each do |letter, substitution|
37
+ substitutions.each do |letter, substitution|
37
38
  match_substitutions[substitution] = letter if token.include?(substitution)
38
39
  end
39
40
  match.l33t = true
@@ -49,9 +50,9 @@ module Zxcvbn
49
50
  matches
50
51
  end
51
52
 
52
- def substitute(password, substitution)
53
+ def substitute(password, substitutions)
53
54
  subbed_password = password.dup
54
- substitution.each do |letter, substitution|
55
+ substitutions.each do |letter, substitution|
55
56
  subbed_password.gsub!(substitution, letter)
56
57
  end
57
58
  subbed_password
@@ -64,9 +65,7 @@ module Zxcvbn
64
65
  end
65
66
  L33T_TABLE.each do |letter, substibutions|
66
67
  password.each_char do |password_char|
67
- if substibutions.include?(password_char)
68
- subs[letter] << password_char
69
- end
68
+ subs[letter] << password_char if substibutions.include?(password_char)
70
69
  end
71
70
  end
72
71
  subs
@@ -82,7 +81,7 @@ module Zxcvbn
82
81
  expanded_substitutions.each do |substitution_hash|
83
82
  # convert a hash to an array of hashes with 1 key each
84
83
  subs_array = substitution_hash.map do |letter, substitutions|
85
- {letter => substitutions}
84
+ { letter => substitutions }
86
85
  end
87
86
  combinations << subs_array
88
87
 
@@ -94,15 +93,13 @@ module Zxcvbn
94
93
  end
95
94
 
96
95
  # convert back to simple hash per substitution combination
97
- combination_hashes = combinations.map do |combination_set|
96
+ combinations.map do |combination_set|
98
97
  hash = {}
99
98
  combination_set.each do |combination_hash|
100
99
  hash.merge!(combination_hash)
101
100
  end
102
101
  hash
103
102
  end
104
-
105
- combination_hashes
106
103
  end
107
104
 
108
105
  # expand possible combinations if multiple characters can be substituted
@@ -110,11 +107,11 @@ module Zxcvbn
110
107
  # [{'a' => '4', 'i' => 1}, {'a' => '@', 'i' => '1'}]
111
108
  def expanded_substitutions(hash)
112
109
  return {} if hash.empty?
110
+
113
111
  values = hash.values
114
112
  product_values = values[0].product(*values[1..-1])
115
- product_values.map{ |p| Hash[hash.keys.zip(p)] }
113
+ product_values.map { |p| Hash[hash.keys.zip(p)] }
116
114
  end
117
-
118
115
  end
119
116
  end
120
- end
117
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'zxcvbn/match'
2
4
 
3
5
  module Zxcvbn
@@ -5,15 +7,15 @@ module Zxcvbn
5
7
  module RegexHelpers
6
8
  def re_match_all(regex, password)
7
9
  pos = 0
8
- while re_match = regex.match(password, pos)
10
+ while (re_match = regex.match(password, pos))
9
11
  i, j = re_match.offset(0)
10
12
  pos = j
11
13
  j -= 1
12
14
 
13
15
  match = Match.new(
14
- :i => i,
15
- :j => j,
16
- :token => password[i..j]
16
+ i: i,
17
+ j: j,
18
+ token: password[i..j]
17
19
  )
18
20
  yield match, re_match
19
21
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'zxcvbn/match'
2
4
 
3
5
  module Zxcvbn
@@ -9,17 +11,15 @@ module Zxcvbn
9
11
  while i < password.length
10
12
  cur_char = password[i]
11
13
  j = i + 1
12
- while cur_char == password[j]
13
- j += 1
14
- end
14
+ j += 1 while cur_char == password[j]
15
15
 
16
16
  if j - i > 2 # don't consider length 1 or 2 chains.
17
17
  result << Match.new(
18
- :pattern => 'repeat',
19
- :i => i,
20
- :j => j-1,
21
- :token => password[i...j],
22
- :repeated_char => cur_char
18
+ pattern: 'repeat',
19
+ i: i,
20
+ j: j - 1,
21
+ token: password[i...j],
22
+ repeated_char: cur_char
23
23
  )
24
24
  end
25
25
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'zxcvbn/match'
2
4
 
3
5
  module Zxcvbn
@@ -7,14 +9,14 @@ module Zxcvbn
7
9
  'lower' => 'abcdefghijklmnopqrstuvwxyz',
8
10
  'upper' => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
9
11
  'digits' => '01234567890'
10
- }
12
+ }.freeze
11
13
 
12
14
  def seq_match_length(password, from, direction, seq)
13
15
  index_from = seq.index(password[from])
14
16
  j = 1
15
17
  while from + j < password.length &&
16
18
  password[from + j] == seq[index_from + direction * j]
17
- j+= 1
19
+ j += 1
18
20
  end
19
21
  j
20
22
  end
@@ -24,15 +26,13 @@ module Zxcvbn
24
26
  def applicable_sequence(password, i)
25
27
  SEQUENCES.each do |name, sequence|
26
28
  index1 = sequence.index(password[i])
27
- index2 = sequence.index(password[i+1])
28
- if index1 and index2
29
- seq_direction = index2 - index1
30
- if [-1, 1].include?(seq_direction)
31
- return [name, sequence, seq_direction]
32
- else
33
- return nil
34
- end
35
- end
29
+ index2 = sequence.index(password[i + 1])
30
+ next unless index1 && index2
31
+
32
+ seq_direction = index2 - index1
33
+ return [name, sequence, seq_direction] if [-1, 1].include?(seq_direction)
34
+
35
+ return nil
36
36
  end
37
37
  end
38
38
 
@@ -46,13 +46,13 @@ module Zxcvbn
46
46
  length = seq_match_length(password, i, seq_direction, seq)
47
47
  if length > 2
48
48
  result << Match.new(
49
- :pattern => 'sequence',
50
- :i => i,
51
- :j => i + length - 1,
52
- :token => password[i, length],
53
- :sequence_name => seq_name,
54
- :sequence_space => seq.length,
55
- :ascending => seq_direction == 1
49
+ pattern: 'sequence',
50
+ i: i,
51
+ j: i + length - 1,
52
+ token: password[i, length],
53
+ sequence_name: seq_name,
54
+ sequence_space: seq.length,
55
+ ascending: seq_direction == 1
56
56
  )
57
57
  end
58
58
  i += length - 1
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'zxcvbn/match'
2
4
 
3
5
  module Zxcvbn
@@ -24,7 +26,7 @@ module Zxcvbn
24
26
  turns = 0
25
27
  shifted_count = 0
26
28
  loop do
27
- prev_char = password[j-1]
29
+ prev_char = password[j - 1]
28
30
  found = false
29
31
  found_direction = -1
30
32
  cur_direction = -1
@@ -34,22 +36,22 @@ module Zxcvbn
34
36
  cur_char = password[j]
35
37
  adjacents.each do |adj|
36
38
  cur_direction += 1
37
- if adj && adj.index(cur_char)
38
- found = true
39
- found_direction = cur_direction
40
- if adj.index(cur_char) == 1
41
- # index 1 in the adjacency means the key is shifted, 0 means unshifted: A vs a, % vs 5, etc.
42
- # for example, 'q' is adjacent to the entry '2@'. @ is shifted w/ index 1, 2 is unshifted.
43
- shifted_count += 1
44
- end
45
- if last_direction != found_direction
46
- # adding a turn is correct even in the initial case when last_direction is null:
47
- # every spatial pattern starts with a turn.
48
- turns += 1
49
- last_direction = found_direction
50
- end
51
- break
39
+ next unless adj&.index(cur_char)
40
+
41
+ found = true
42
+ found_direction = cur_direction
43
+ if adj.index(cur_char) == 1
44
+ # index 1 in the adjacency means the key is shifted, 0 means unshifted: A vs a, % vs 5, etc.
45
+ # for example, 'q' is adjacent to the entry '2@'. @ is shifted w/ index 1, 2 is unshifted.
46
+ shifted_count += 1
47
+ end
48
+ if last_direction != found_direction
49
+ # adding a turn is correct even in the initial case when last_direction is null:
50
+ # every spatial pattern starts with a turn.
51
+ turns += 1
52
+ last_direction = found_direction
52
53
  end
54
+ break
53
55
  end
54
56
  end
55
57
  # if the current pattern continued, extend j and try to grow again
@@ -59,13 +61,13 @@ module Zxcvbn
59
61
  # otherwise push the pattern discovered so far, if any...
60
62
  if j - i > 2 # don't consider length 1 or 2 chains.
61
63
  result << Match.new(
62
- :pattern => 'spatial',
63
- :i => i,
64
- :j => j-1,
65
- :token => password[i...j],
66
- :graph => graph_name,
67
- :turns => turns,
68
- :shifted_count => shifted_count
64
+ pattern: 'spatial',
65
+ i: i,
66
+ j: j - 1,
67
+ token: password[i...j],
68
+ graph: graph_name,
69
+ turns: turns,
70
+ shifted_count: shifted_count
69
71
  )
70
72
  end
71
73
  # ...and then start a new search for the rest of the password.
@@ -78,4 +80,4 @@ module Zxcvbn
78
80
  end
79
81
  end
80
82
  end
81
- end
83
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'zxcvbn/matchers/regex_helpers'
2
4
 
3
5
  module Zxcvbn
@@ -5,7 +7,7 @@ module Zxcvbn
5
7
  class Year
6
8
  include RegexHelpers
7
9
 
8
- YEAR_REGEX = /19\d\d|200\d|201\d/
10
+ YEAR_REGEX = /19\d\d|200\d|201\d/.freeze
9
11
 
10
12
  def matches(password)
11
13
  result = []
@@ -17,4 +19,4 @@ module Zxcvbn
17
19
  end
18
20
  end
19
21
  end
20
- end
22
+ end
data/lib/zxcvbn/math.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zxcvbn
2
4
  module Math
3
5
  def bruteforce_cardinality(password)
@@ -30,11 +32,12 @@ module Zxcvbn
30
32
 
31
33
  def nCk(n, k)
32
34
  return 0 if k > n
33
- return 1 if k == 0
35
+ return 1 if k.zero?
36
+
34
37
  r = 1
35
38
  (1..k).each do |d|
36
- r = r * n
37
- r = r / d
39
+ r *= n
40
+ r /= d
38
41
  n -= 1
39
42
  end
40
43
  r
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'zxcvbn/dictionary_ranker'
2
4
  require 'zxcvbn/matchers/dictionary'
3
5
  require 'zxcvbn/matchers/l33t'
@@ -26,13 +28,13 @@ module Zxcvbn
26
28
 
27
29
  def user_input_matchers(user_inputs)
28
30
  return [] unless user_inputs.any?
31
+
29
32
  user_ranked_dictionary = DictionaryRanker.rank_dictionary(user_inputs)
30
33
  dictionary_matcher = Matchers::Dictionary.new('user_inputs', user_ranked_dictionary)
31
34
  l33t_matcher = Matchers::L33t.new([dictionary_matcher])
32
35
  [dictionary_matcher, l33t_matcher]
33
36
  end
34
37
 
35
-
36
38
  def build_matchers
37
39
  matchers = []
38
40
  dictionary_matchers = @data.ranked_dictionaries.map do |name, dictionary|
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'zxcvbn/clock'
2
4
  require 'zxcvbn/feedback_giver'
3
5
  require 'zxcvbn/omnimatch'
@@ -11,7 +13,7 @@ module Zxcvbn
11
13
  end
12
14
 
13
15
  def test(password, user_inputs = [])
14
- password = password || ''
16
+ password ||= ''
15
17
  result = nil
16
18
  calc_time = Clock.realtime do
17
19
  matches = @omnimatch.matches(password, user_inputs)
data/lib/zxcvbn/score.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zxcvbn
2
4
  class Score
3
5
  attr_accessor :entropy, :crack_time, :crack_time_display, :score, :pattern,
@@ -12,4 +14,4 @@ module Zxcvbn
12
14
  @password = options[:password]
13
15
  end
14
16
  end
15
- end
17
+ end
data/lib/zxcvbn/scorer.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'zxcvbn/entropy'
2
4
  require 'zxcvbn/crack_time'
3
5
  require 'zxcvbn/score'
@@ -16,19 +18,22 @@ module Zxcvbn
16
18
 
17
19
  def minimum_entropy_match_sequence(password, matches)
18
20
  bruteforce_cardinality = bruteforce_cardinality(password) # e.g. 26 for lowercase
19
- up_to_k = [] # minimum entropy up to k.
20
- backpointers = [] # for the optimal sequence of matches up to k, holds the final match (match.j == k). null means the sequence ends w/ a brute-force character.
21
+ up_to_k = [] # minimum entropy up to k.
22
+ # for the optimal sequence of matches up to k, holds the final match (match.j == k).
23
+ # null means the sequence ends w/ a brute-force character.
24
+ backpointers = []
21
25
  (0...password.length).each do |k|
22
26
  # starting scenario to try and beat: adding a brute-force character to the minimum entropy sequence at k-1.
23
- previous_k_entropy = k > 0 ? up_to_k[k-1] : 0
27
+ previous_k_entropy = k.positive? ? up_to_k[k - 1] : 0
24
28
  up_to_k[k] = previous_k_entropy + lg(bruteforce_cardinality)
25
29
  backpointers[k] = nil
26
30
  matches.select do |match|
27
31
  match.j == k
28
32
  end.each do |match|
29
- i, j = match.i, match.j
33
+ i = match.i
34
+ j = match.j
30
35
  # see if best entropy up to i-1 + entropy of this match is less than the current minimum at j.
31
- previous_i_entropy = i > 0 ? up_to_k[i-1] : 0
36
+ previous_i_entropy = i.positive? ? up_to_k[i - 1] : 0
32
37
  candidate_entropy = previous_i_entropy + calc_entropy(match)
33
38
  if up_to_k[j] && candidate_entropy < up_to_k[j]
34
39
  up_to_k[j] = candidate_entropy
@@ -54,29 +59,26 @@ module Zxcvbn
54
59
  score_for(password, match_sequence, up_to_k)
55
60
  end
56
61
 
57
- def score_for password, match_sequence, up_to_k
58
- min_entropy = up_to_k[password.length - 1] || 0 # or 0 corner case is for an empty password ''
62
+ def score_for(password, match_sequence, up_to_k)
63
+ min_entropy = up_to_k[password.length - 1] || 0 # or 0 corner case is for an empty password ''
59
64
  crack_time = entropy_to_crack_time(min_entropy)
60
65
 
61
66
  # final result object
62
67
  Score.new(
63
- :password => password,
64
- :entropy => min_entropy.round(3),
65
- :match_sequence => match_sequence,
66
- :crack_time => crack_time.round(3),
67
- :crack_time_display => display_time(crack_time),
68
- :score => crack_time_to_score(crack_time)
68
+ password: password,
69
+ entropy: min_entropy.round(3),
70
+ match_sequence: match_sequence,
71
+ crack_time: crack_time.round(3),
72
+ crack_time_display: display_time(crack_time),
73
+ score: crack_time_to_score(crack_time)
69
74
  )
70
75
  end
71
76
 
72
-
73
77
  def pad_with_bruteforce_matches(match_sequence, password, bruteforce_cardinality)
74
78
  k = 0
75
79
  match_sequence_copy = []
76
80
  match_sequence.each do |match|
77
- if match.i > k
78
- match_sequence_copy << make_bruteforce_match(password, k, match.i - 1, bruteforce_cardinality)
79
- end
81
+ match_sequence_copy << make_bruteforce_match(password, k, match.i - 1, bruteforce_cardinality) if match.i > k
80
82
  k = match.j + 1
81
83
  match_sequence_copy << match
82
84
  end
@@ -85,17 +87,18 @@ module Zxcvbn
85
87
  end
86
88
  match_sequence_copy
87
89
  end
90
+
88
91
  # fill in the blanks between pattern matches with bruteforce "matches"
89
92
  # that way the match sequence fully covers the password:
90
93
  # match1.j == match2.i - 1 for every adjacent match1, match2.
91
94
  def make_bruteforce_match(password, i, j, bruteforce_cardinality)
92
95
  Match.new(
93
- :pattern => 'bruteforce',
94
- :i => i,
95
- :j => j,
96
- :token => password[i..j],
97
- :entropy => lg(bruteforce_cardinality ** (j - i + 1)),
98
- :cardinality => bruteforce_cardinality
96
+ pattern: 'bruteforce',
97
+ i: i,
98
+ j: j,
99
+ token: password[i..j],
100
+ entropy: lg(bruteforce_cardinality**(j - i + 1)),
101
+ cardinality: bruteforce_cardinality
99
102
  )
100
103
  end
101
104
  end
data/lib/zxcvbn/tester.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "zxcvbn/data"
4
- require "zxcvbn/password_strength"
3
+ require 'zxcvbn/data'
4
+ require 'zxcvbn/password_strength'
5
5
 
6
6
  module Zxcvbn
7
7
  # Allows you to test the strength of multiple passwords without reading and
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Zxcvbn
4
- VERSION = '1.2.1'
4
+ VERSION = '1.2.3'
5
5
  end
data/lib/zxcvbn.rb CHANGED
@@ -1,11 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'pathname'
2
4
  require 'zxcvbn/version'
3
5
  require 'zxcvbn/tester'
4
6
 
5
7
  module Zxcvbn
6
- extend self
8
+ module_function
7
9
 
8
- DATA_PATH = Pathname(File.expand_path('../../data', __FILE__))
10
+ DATA_PATH = Pathname(File.expand_path('../data', __dir__))
9
11
 
10
12
  # Returns a Zxcvbn::Score for the given password
11
13
  #
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: 1.2.1
4
+ version: 1.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steve Hodgkiss
@@ -9,35 +9,7 @@ authors:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
  date: 1980-01-02 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: mini_racer
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: rspec
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
12
+ dependencies: []
41
13
  description: Ruby port of Dropboxs zxcvbn.js
42
14
  email:
43
15
  - steve@hodgkiss.me