Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ $ irb
"Predictable substitutions like '@' instead of 'a' don't help very much"]>>
```

To add custom word lists, chain `add_word_list` before `build`. Use a distinct name (e.g. `"company"`) — using a built-in name (`"english_wikipedia"`, `"passwords"`, `"female_names"`, `"male_names"`, `"surnames"`, `"us_tv_and_film"`) replaces that list entirely:
To add custom word lists, chain `add_word_list` before `build`:

```ruby
>> tester = Zxcvbn.tester_builder.add_word_list('company', %w[acme corp]).build
Expand Down
6 changes: 6 additions & 0 deletions lib/zxcvbn/data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ def inspect = "#<#{self.class}:0x#{__id__.to_s(16)}>"
end
private_constant :Dictionaries

# Built-in dictionary names and the reserved per-call key.
# These names cannot be passed to {TesterBuilder#add_word_list}.
RESERVED_NAMES = %w[
english_wikipedia female_names male_names passwords surnames us_tv_and_film user_inputs
].freeze

# Loads all built-in frequency lists and adjacency graphs from disk.
def initialize
ranked = DictionaryRanker.rank_dictionaries(
Expand Down
6 changes: 6 additions & 0 deletions lib/zxcvbn/tester_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,13 @@ def initialize
# @param name [String] identifier for the word list; calling with the same name twice replaces the earlier list
# @param words [Array<String>, String] words to add; non-String elements are silently ignored during matching
# @return [self]
# @raise [ArgumentError] if name collides with a built-in dictionary name or +"user_inputs"+
def add_word_list(name, words)
if Data::RESERVED_NAMES.include?(name)
raise ArgumentError,
"#{name.inspect} is a reserved dictionary name; use a different name for custom word lists"
end

@word_lists[name] = Array(words)
self
end
Expand Down
2 changes: 2 additions & 0 deletions sig/zxcvbn/data.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ module Zxcvbn
def initialize: (ranked: Hash[String, ranked_dictionary], tries: Hash[String, Trie]) -> void
end

RESERVED_NAMES: Array[String]

@dictionaries: Dictionaries
@adjacency_graphs: Hash[String, adjacency_graph]
@graph_stats: graph_stats
Expand Down
10 changes: 10 additions & 0 deletions spec/zxcvbn/tester_builder_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,16 @@
expect { Zxcvbn.tester_builder.max_password_length(nil) }.to raise_error(ArgumentError)
end

it 'raises ArgumentError when add_word_list name collides with a built-in dictionary' do
expect { Zxcvbn.tester_builder.add_word_list('passwords', %w[secret]) }
.to raise_error(ArgumentError, /reserved/)
end

it 'raises ArgumentError when add_word_list name is "user_inputs"' do
expect { Zxcvbn.tester_builder.add_word_list('user_inputs', %w[acme]) }
.to raise_error(ArgumentError, /reserved/)
end

it 'treats nil words in add_word_list the same as an empty array' do
tester = Zxcvbn.tester_builder.add_word_list('test', nil).build
expect(tester.test('envato').guesses).to eq ZXCVBN_TESTER.test('envato').guesses
Expand Down