Class: Zxcvbn::Tester

Inherits:
Object
  • Object
show all
Defined in:
lib/zxcvbn/tester.rb

Overview

Evaluates password strength against dictionary lists, keyboard patterns, dates, sequences, and repeats. Construct via tester_builder:

tester = Zxcvbn
  .tester_builder
  .add_word_list('company', %w[acme corp])
  .build

tester.test("password 1")
tester.test("password 2")

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(data:, max_password_length:) ⇒ Tester

Returns a new instance of Tester.

Parameters:

  • data (Data)

    pre-configured data

  • max_password_length (Integer)

    passwords longer than this raise PasswordTooLong from #test

Raises:

  • (ArgumentError)

    if max_password_length is not a positive integer



29
30
31
32
33
34
35
36
37
38
# File 'lib/zxcvbn/tester.rb', line 29

def initialize(data:, max_password_length:)
  unless max_password_length.is_a?(Integer) && max_password_length.positive?
    raise ArgumentError,
          "max_password_length must be a positive integer; got #{max_password_length.inspect}"
  end

  @data = data
  @max_password_length = max_password_length
  @omnimatch = Omnimatch.new(@data).freeze
end

Instance Attribute Details

#max_password_lengthObject (readonly)

Returns the value of attribute max_password_length.



40
41
42
# File 'lib/zxcvbn/tester.rb', line 40

def max_password_length
  @max_password_length
end

Instance Method Details

#inspectString

Returns a concise representation that omits the large dictionary data.

Returns:

  • (String)

    a concise representation that omits the large dictionary data



74
75
76
# File 'lib/zxcvbn/tester.rb', line 74

def inspect
  "#<#{self.class}:0x#{__id__.to_s(16)}>"
end

#test(password, user_inputs = []) ⇒ Score

Evaluates a password and returns a Score.

Raises PasswordTooLong if the password exceeds the max_password_length passed to #initialize. The limit exists because scoring time grows super-quadratically on adversarial inputs such as short repeated sequences (e.g. "ab" * 500).

Parameters:

  • password (String)

    the password to evaluate

  • user_inputs (Array<String>) (defaults to: [])

    caller-supplied words to treat as known

Returns:

Raises:



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/zxcvbn/tester.rb', line 53

def test(password, user_inputs = [])
  password ||= ''
  user_inputs = Array(user_inputs).select { |i| i.is_a?(String) }
  if password.length > @max_password_length
    raise PasswordTooLong, "Password exceeds the maximum length of #{@max_password_length}."
  end

  result = nil
  calc_time = Clock.realtime do
    reference_year = Time.now.year
    scorer = Scorer.new(@data, @omnimatch, reference_year)
    matches = @omnimatch.matches(password, user_inputs, reference_year:)
    result = scorer.most_guessable_match_sequence(password, matches)
  end
  result.with(
    calc_time:,
    feedback: FeedbackGiver.get_feedback(result.score, result.sequence)
  )
end