All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
2.0.0 - 2026-05-28
- User-input dictionary matchers now use the Trie path, preventing O(n²) slowdowns on long passwords (#89)
Zxcvbn::TesterBuilder: fluent builder for constructing aTesterwith custom word lists and options. Obtain a builder viaZxcvbn.tester_builder, then chainadd_word_list,max_password_length, andbuild.Zxcvbn::Guessesmodule with per-pattern guess estimation formulas matching zxcvbn.js v4: bruteforce, dictionary (with uppercase and l33t variation multipliers), spatial, repeat, sequence, digits, year, and date (#69)us_tv_and_filmfrequency list (19,160 entries) introduced in zxcvbn.js v4 (#69)- Reverse dictionary matching in
Omnimatchso reversed words (e.g. "drowssap") are detected and scored (#69) guessesandguesses_log10fields onZxcvbn::Score(#69)guesses,guesses_log10,base_token,repeat_count, andbase_guessesfields onZxcvbn::Match(#69)- YARD documentation for all public classes, modules, and methods (#72)
- Breaking:
Zxcvbn::Tester.newnow requiresdata:andmax_password_length:keyword arguments with no defaults. UseZxcvbn.tester_builder.buildto construct aTester. - Breaking: English frequency list dictionary name renamed from
englishtoenglish_wikipedia, matching the zxcvbn.js source. Affectsmatch.dictionary_namein results (#90) Zxcvbn.testnow reuses a sharedTesterinstance across calls, avoiding repeated dictionary parsing (#80)- Repeat base tokens are now scored without
user_inputs, matching zxcvbn.js v4. Previously, user-supplied words were propagated into the recursive scoring of a repeat's base token, causing repeat matches of user-supplied words to score lower than JS would report (#83) Zxcvbn::Scoreis now an immutable value object backed by Ruby'sData. Attribute setters (calc_time=,feedback=, etc.) have been removed. Instances now support structural equality (==/eql?/hash) and thewithmethod for creating modified copies (#74)- Breaking:
Zxcvbn::Matchis now an immutable value object backed by Ruby'sData. Attribute setters have been removed. Instances now support structural equality (==/eql?/hash) and thewithmethod for creating modified copies (#92) - Breaking:
Match#to_hashhas been removed. Usematch.to_hinstead — note the shape differs: keys are symbols (not strings), all 28 attributes are included (not just those that were set), and order follows the member definition rather than being sorted alphabetically. To replicate the old behaviour:match.to_h.transform_keys(&:to_s).compact.sort.to_h. Additionally,to_hashwas Ruby's implicit-conversion hook, so any code that splatted a match (**match) or passed it toHash()will now raiseTypeError— usematch.to_hexplicitly instead (#92) - Breaking:
Zxcvbn::Feedbackis now an immutable value object backed by Ruby'sData. Attribute setters (warning=,suggestions=) have been removed. Instances now support structural equality (==/eql?/hash) and thewithmethod for creating modified copies (#77) - Breaking: Scoring algorithm aligned with zxcvbn.js v4.4.2. The dynamic programming step now minimises total guesses (
factorial(l) × cumulative_product + MIN_GUESSES^(l-1)penalty) instead of entropy bits. Scores for many passwords will change (#69) - Breaking: Bruteforce cardinality is now fixed at 10 (digits only), matching zxcvbn.js v4. Previously it was computed dynamically from the character classes present in the password (10–95), so bruteforce guesses for passwords containing letters or symbols will change (#69)
- Breaking:
Repeatmatcher now detects multi-character repeating units (e.g.abcabc). Thebase_tokenfield holds the repeating unit (which may be more than one character);repeated_charhas been removed (#69) - Breaking:
Match#entropy,Match#base_entropy,Match#uppercase_entropy, andMatch#l33t_entropyhave been removed. UseMatch#guessesandMatch#guesses_log10instead (#69) crack_time_to_scorereplaced byguesses_to_scorewith v4 thresholds: 0 (<1,005 guesses), 1 (<1,000,005), 2 (<100,000,005), 3 (<10,000,000,005), 4 (≥10,000,000,005) (#69)Sequencematcher ported to the zxcvbn.js v4 delta-based algorithm. Sequences are now detected using codepoint deltas up to ±5 (was ±1 only), enabling matches like"ace"(delta 2) or Unicode runs like"αβγ". Sequence type is classified by character class (lower/upper/digits/unicode) rather than a lookup table (#69)Datematcher year range extended to 1000–2050; 2-digit years are now expanded (>50 → 1900s, ≤50 → 2000s) (#69)- All frequency lists replaced with zxcvbn.js v4.4.2 versions:
passwords(30k entries),surnames(10k),female_names(3,712),male_names(983),english_wikipedia(30k entries) (#69) - Breaking:
entropyonZxcvbn::Scorehas been removed. UseScore#guessesorMath.log2(score.guesses)instead (#69) - Breaking:
Score#crack_timeandScore#crack_time_displayreplaced byScore#crack_times_secondsandScore#crack_times_display, each a hash keyed by attack scenario (online_throttling_100_per_hour,online_no_throttling_10_per_second,offline_slow_hashing_1e4_per_second,offline_fast_hashing_1e10_per_second), matching the zxcvbn.js v4 output format (#69) - Breaking:
Score#match_sequencerenamed toScore#sequenceto match the zxcvbn.js v4 field name (#69) - Breaking:
Feedback#warningnow returns''instead ofnilwhen no warning applies, matching zxcvbn.js v4 (#69) - Breaking: The "This is similar to a commonly used password" warning is now only emitted when
match.guesses_log10 <= 4, matching the zxcvbn.js v4 threshold. Previously it was emitted unconditionally for any l33t, reversed, or non-sole-match on the passwords dictionary (#69) - Repeat feedback now distinguishes single-char repeats (
"aaa") from multi-char repeats ("abcabcabc"), matching zxcvbn.js v4 (#69) yearpattern matches now produce a "Recent years are easy to guess" warning (#69)- Sole matches from the
english_wikipediadictionary now produce an "A word by itself is easy to guess" warning (#69) - Breaking:
Tester#testandZxcvbn.testnow raiseZxcvbn::PasswordTooLong(a subclass ofArgumentError) for passwords longer than 256 characters (the default). Previously, long passwords were accepted and could cause super-quadratic runtime on adversarial repeat inputs to thepasswordargument. Theuser_inputsparameter remains unbounded. Override the limit with theZXCVBN_MAX_PASSWORD_LENGTHenvironment variable orZxcvbn.tester_builder.max_password_length(n).build.
- Breaking:
Tester#add_word_lists. UseZxcvbn.tester_builder.add_word_list(name, words).buildinstead. - Breaking:
word_lists:argument toZxcvbn.test. UseZxcvbn.tester_builder.add_word_list(name, words).buildto construct a tester with custom word lists. - Support for Ruby versions below 3.3 (#70)
1.4.0 - 2026-01-15
- RBS type signatures for improved type checking and IDE support (#68)
- Minor fixups in gem metadata (#67).
1.3.0 - 2026-01-02
- Replace OpenStruct with regular class in
Zxcvbn::Matchfor 2x performance improvement (#61) - Implement Trie data structure for dictionary matching with 1.4x additional performance improvement (#62)
- Replace range operators with
String#slicefor string slicing operations (#63) - Optimise L33t matcher with early bailout and improved deduplication (#64)
- Pre-compute spatial graph statistics during data initialisation (#65)
- Optimise nCk calculation using symmetry property (#66)
Overall performance improvement: 4.1x faster than v1.2.4 (0.722ms → 0.176ms per password)
1.2.4 - 2025-12-07
- Address security issues found by RuboCop (#57)
1.2.3 - 2025-12-07
1.2.2 - 2025-12-06
- Address layout and frozen string literal issues (#49)
1.2.1 - 2025-12-05
- Removed the dependency on the Ruby
benchmarkmodule (#44). - Tests are no longer included in the gem package (#45).
1.2.0 - 2021-01-05
- Use mini_racer for running JavaScript specs (thanks @RSO (#33))
- Moved CI to GitHub Actions (#34)
1.1.0 - 2020-07-16
- Invalid user dictionaries are handled more robustly (#28)
1.0.0 - 2019-05-14
- License info in the gemspec (#21)
- More ported password checking features to bring this gem more up to date. (#22)
- spatial - Keyboard patterns
- repeat - Repeated characters
- sequence - easily guessable sequences
- date - date associations
- This gem will no longer run on Ruby versions < 2.3 (#25)