zxcvbn-ruby 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -1
- data/lib/zxcvbn/case_helpers.rb +25 -0
- data/lib/zxcvbn/dictionary_ranker.rb +5 -1
- data/lib/zxcvbn/matchers/dictionary.rb +3 -1
- data/lib/zxcvbn/matchers/l33t.rb +4 -1
- data/lib/zxcvbn/scorer.rb +7 -1
- data/lib/zxcvbn/version.rb +1 -1
- data/sig/zxcvbn/case_helpers.rbs +5 -0
- data/sig/zxcvbn/matchers/dictionary.rbs +2 -0
- data/sig/zxcvbn/matchers/l33t.rbs +2 -0
- metadata +6 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ba194667b2557bfc70fe0bae1294067b9d0ce0806bd02d9fdca7d7a4e5c13d0f
|
|
4
|
+
data.tar.gz: 2cf420153522153586d434a30d216ae7b115971e97ff76ec33e10870eaef1fcd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 85b798676bab221fd1c1bcd3d822305a1f0772ff13291e2b8a475d2dba5f6b0c3bd1ff0cf724913cf63b7ce7f7db14a95b563e06079c1e34d06497779a7c4368
|
|
7
|
+
data.tar.gz: 5d97602b0d05a1b4bd5afe263cc5ecc1f384df5bc4470d9321fb6029981cc5a4c5d35c4c87474154eba6545d63a66a1d0e5ff9e30224f45343b104ad9a72a221
|
data/CHANGELOG.md
CHANGED
|
@@ -6,7 +6,15 @@ 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/v2.0.
|
|
9
|
+
[Unreleased]: https://github.com/envato/zxcvbn-ruby/compare/v2.0.1...HEAD
|
|
10
|
+
|
|
11
|
+
## [2.0.1] - 2026-07-11
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
- `NoMethodError` in `Scorer` for passwords containing characters whose lowercased form is longer than the original (e.g. "İ" U+0130 downcases to "i" plus a combining dot). Dictionary and l33t matchers now lowercase per character, keeping match indices aligned with the original password. Dictionary word ranking applies the same normalisation, so user-input words containing such characters now match ([#124])
|
|
15
|
+
|
|
16
|
+
[2.0.1]: https://github.com/envato/zxcvbn-ruby/compare/v2.0.0...v2.0.1
|
|
17
|
+
[#124]: https://github.com/envato/zxcvbn-ruby/pull/124
|
|
10
18
|
|
|
11
19
|
## [2.0.0] - 2026-05-28
|
|
12
20
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Zxcvbn
|
|
4
|
+
# Shared helper for lowercasing strings without changing their length.
|
|
5
|
+
# @api private
|
|
6
|
+
module CaseHelpers
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
# String#downcase can grow a string — "İ" (U+0130) downcases to "i" plus
|
|
10
|
+
# combining dot above (U+0069 U+0307). Matchers rely on the lowercased
|
|
11
|
+
# password sharing the original's character indices, so a grown string
|
|
12
|
+
# sends out-of-range match positions to the scorer. When lengths diverge,
|
|
13
|
+
# fall back to per-character downcasing, truncating any multi-character
|
|
14
|
+
# mapping to its first character to keep positions aligned 1:1.
|
|
15
|
+
#
|
|
16
|
+
# @param string [String]
|
|
17
|
+
# @return [String] lowercased string with the same character length
|
|
18
|
+
def downcase_preserving_length(string)
|
|
19
|
+
lowercased = string.downcase
|
|
20
|
+
return lowercased if lowercased.length == string.length
|
|
21
|
+
|
|
22
|
+
string.each_char.map { |char| char.downcase[0] }.join
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'zxcvbn/case_helpers'
|
|
4
|
+
|
|
3
5
|
module Zxcvbn
|
|
4
6
|
# Converts raw word lists into frequency-ranked dictionaries for matcher use.
|
|
5
7
|
# @api private
|
|
@@ -21,7 +23,9 @@ module Zxcvbn
|
|
|
21
23
|
def self.rank_dictionary(words)
|
|
22
24
|
words
|
|
23
25
|
.each_with_index
|
|
24
|
-
.with_object({})
|
|
26
|
+
.with_object({}) do |(word, i), dictionary|
|
|
27
|
+
dictionary[CaseHelpers.downcase_preserving_length(word)] = i + 1
|
|
28
|
+
end
|
|
25
29
|
end
|
|
26
30
|
end
|
|
27
31
|
end
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'zxcvbn/case_helpers'
|
|
3
4
|
require 'zxcvbn/match_builder'
|
|
4
5
|
|
|
5
6
|
module Zxcvbn
|
|
@@ -8,6 +9,7 @@ module Zxcvbn
|
|
|
8
9
|
# a ranked dictionary.
|
|
9
10
|
# @api private
|
|
10
11
|
class Dictionary
|
|
12
|
+
include CaseHelpers
|
|
11
13
|
# @param name [String] dictionary identifier used in match results
|
|
12
14
|
# @param ranked_dictionary [Hash{String => Integer}] lowercased word → rank
|
|
13
15
|
# @param trie [Trie, nil] optional prefix trie for faster lookups
|
|
@@ -22,7 +24,7 @@ module Zxcvbn
|
|
|
22
24
|
# @param password [String]
|
|
23
25
|
# @return [Array<MatchBuilder>] matches with pattern "dictionary"
|
|
24
26
|
def matches(password)
|
|
25
|
-
lowercased_password = password
|
|
27
|
+
lowercased_password = downcase_preserving_length(password)
|
|
26
28
|
|
|
27
29
|
if @trie
|
|
28
30
|
trie_matches(password, lowercased_password)
|
data/lib/zxcvbn/matchers/l33t.rb
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'zxcvbn/case_helpers'
|
|
4
|
+
|
|
3
5
|
module Zxcvbn
|
|
4
6
|
module Matchers
|
|
5
7
|
# Matches dictionary words after substituting common l33t-speak character
|
|
6
8
|
# replacements (e.g. "@" for "a", "3" for "e").
|
|
7
9
|
# @api private
|
|
8
10
|
class L33t
|
|
11
|
+
include CaseHelpers
|
|
9
12
|
# Mapping from plain letter to the l33t characters that can represent it.
|
|
10
13
|
L33T_TABLE = {
|
|
11
14
|
'a' => ['4', '@'].freeze,
|
|
@@ -33,7 +36,7 @@ module Zxcvbn
|
|
|
33
36
|
# @return [Array<MatchBuilder>] matches with pattern "dictionary" and l33t: true
|
|
34
37
|
def matches(password)
|
|
35
38
|
matches = []
|
|
36
|
-
lowercased_password = password
|
|
39
|
+
lowercased_password = downcase_preserving_length(password)
|
|
37
40
|
relevent_subtable = relevent_l33t_subtable(lowercased_password)
|
|
38
41
|
|
|
39
42
|
# Early bailout: if no l33t characters present, return empty matches
|
data/lib/zxcvbn/scorer.rb
CHANGED
|
@@ -61,7 +61,13 @@ module Zxcvbn
|
|
|
61
61
|
|
|
62
62
|
return build_score(password, [], 1.0) if n.zero?
|
|
63
63
|
|
|
64
|
-
#
|
|
64
|
+
# Index matches by their last character (j). matches_by_j is sized to the
|
|
65
|
+
# password, so this relies on every match satisfying 0 <= m.j < n (and
|
|
66
|
+
# 0 <= m.i <= m.j) — match indices must be positions in `password`.
|
|
67
|
+
# Matchers uphold this by matching against a lowercased view that keeps the
|
|
68
|
+
# original character length; see CaseHelpers.downcase_preserving_length. A
|
|
69
|
+
# match with j >= n reads a nil slot below and raises NoMethodError, so any
|
|
70
|
+
# new matcher must preserve this invariant.
|
|
65
71
|
matches_by_j = Array.new(n) { [] }
|
|
66
72
|
matches.each { |m| matches_by_j[m.j] << m }
|
|
67
73
|
matches_by_j.each { |arr| arr.sort_by!(&:i) }
|
data/lib/zxcvbn/version.rb
CHANGED
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: 2.0.
|
|
4
|
+
version: 2.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Steve Hodgkiss
|
|
@@ -32,6 +32,7 @@ files:
|
|
|
32
32
|
- data/frequency_lists/surnames.txt
|
|
33
33
|
- data/frequency_lists/us_tv_and_film.txt
|
|
34
34
|
- lib/zxcvbn.rb
|
|
35
|
+
- lib/zxcvbn/case_helpers.rb
|
|
35
36
|
- lib/zxcvbn/clock.rb
|
|
36
37
|
- lib/zxcvbn/crack_time.rb
|
|
37
38
|
- lib/zxcvbn/data.rb
|
|
@@ -61,6 +62,7 @@ files:
|
|
|
61
62
|
- lib/zxcvbn/version.rb
|
|
62
63
|
- sig/README.md
|
|
63
64
|
- sig/zxcvbn.rbs
|
|
65
|
+
- sig/zxcvbn/case_helpers.rbs
|
|
64
66
|
- sig/zxcvbn/clock.rbs
|
|
65
67
|
- sig/zxcvbn/crack_time.rbs
|
|
66
68
|
- sig/zxcvbn/data.rbs
|
|
@@ -93,9 +95,9 @@ metadata:
|
|
|
93
95
|
allowed_push_host: https://rubygems.org
|
|
94
96
|
bug_tracker_uri: https://github.com/envato/zxcvbn-ruby/issues
|
|
95
97
|
changelog_uri: https://github.com/envato/zxcvbn-ruby/blob/HEAD/CHANGELOG.md
|
|
96
|
-
documentation_uri: https://www.rubydoc.info/gems/zxcvbn-ruby/2.0.
|
|
98
|
+
documentation_uri: https://www.rubydoc.info/gems/zxcvbn-ruby/2.0.1
|
|
97
99
|
homepage_uri: https://github.com/envato/zxcvbn-ruby
|
|
98
|
-
source_code_uri: https://github.com/envato/zxcvbn-ruby/tree/v2.0.
|
|
100
|
+
source_code_uri: https://github.com/envato/zxcvbn-ruby/tree/v2.0.1
|
|
99
101
|
rubygems_mfa_required: 'true'
|
|
100
102
|
rdoc_options: []
|
|
101
103
|
require_paths:
|
|
@@ -111,7 +113,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
111
113
|
- !ruby/object:Gem::Version
|
|
112
114
|
version: '0'
|
|
113
115
|
requirements: []
|
|
114
|
-
rubygems_version: 4.0.
|
|
116
|
+
rubygems_version: 4.0.15
|
|
115
117
|
specification_version: 4
|
|
116
118
|
summary: Ruby port of Dropbox's zxcvbn.js JavaScript password strength estimator
|
|
117
119
|
test_files: []
|