pwned 1.2.0 → 2.1.0
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/.travis.yml +13 -9
- data/.yardopts +1 -0
- data/CHANGELOG.md +70 -15
- data/README.md +139 -10
- data/bin/pwned +52 -0
- data/lib/pwned.rb +22 -6
- data/lib/pwned/hashed_password.rb +35 -0
- data/lib/pwned/not_pwned_validator.rb +17 -3
- data/lib/pwned/password.rb +11 -96
- data/lib/pwned/password_base.rb +133 -0
- data/lib/pwned/version.rb +1 -1
- data/pwned.gemspec +11 -2
- metadata +25 -30
- data/docs/NotPwnedValidator.html +0 -425
- data/docs/Pwned.html +0 -513
- data/docs/Pwned/Error.html +0 -149
- data/docs/Pwned/Password.html +0 -925
- data/docs/Pwned/TimeoutError.html +0 -152
- data/docs/PwnedValidator.html +0 -192
- data/docs/_index.html +0 -162
- data/docs/class_list.html +0 -51
- data/docs/css/common.css +0 -1
- data/docs/css/full_list.css +0 -58
- data/docs/css/style.css +0 -499
- data/docs/file.README.html +0 -292
- data/docs/file_list.html +0 -56
- data/docs/frames.html +0 -17
- data/docs/index.html +0 -292
- data/docs/js/app.js +0 -248
- data/docs/js/full_list.js +0 -216
- data/docs/js/jquery.js +0 -4
- data/docs/method_list.html +0 -115
- data/docs/top-level-namespace.html +0 -112
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pwned/password_base"
|
4
|
+
|
5
|
+
module Pwned
|
6
|
+
##
|
7
|
+
# This class represents a hashed password. It does all the work of talking to the
|
8
|
+
# Pwned Passwords API to find out if the password has been pwned.
|
9
|
+
# @see https://haveibeenpwned.com/API/v2#PwnedPasswords
|
10
|
+
class HashedPassword
|
11
|
+
include PasswordBase
|
12
|
+
##
|
13
|
+
# Creates a new hashed password object.
|
14
|
+
#
|
15
|
+
# @example A simple password with the default request options
|
16
|
+
# password = Pwned::HashedPassword.new("ABC123")
|
17
|
+
# @example Setting the user agent and the read timeout of the request
|
18
|
+
# password = Pwned::HashedPassword.new("ABC123", headers: { "User-Agent" => "My user agent" }, read_timout: 10)
|
19
|
+
#
|
20
|
+
# @param hashed_password [String] The hash of the password you want to check against the API.
|
21
|
+
# @param [Hash] request_options Options that can be passed to +Net::HTTP.start+ when
|
22
|
+
# calling the API
|
23
|
+
# @option request_options [Symbol] :headers ({ "User-Agent" => "Ruby Pwned::Password #{Pwned::VERSION}" })
|
24
|
+
# HTTP headers to include in the request
|
25
|
+
# @raise [TypeError] if the password is not a string.
|
26
|
+
# @since 2.1.0
|
27
|
+
def initialize(hashed_password, request_options={})
|
28
|
+
raise TypeError, "hashed_password must be of type String" unless hashed_password.is_a? String
|
29
|
+
@hashed_password = hashed_password.upcase
|
30
|
+
@request_options = Hash(request_options).dup
|
31
|
+
@request_headers = Hash(request_options.delete(:headers))
|
32
|
+
@request_headers = DEFAULT_REQUEST_HEADERS.merge(@request_headers)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -61,16 +61,30 @@ class NotPwnedValidator < ActiveModel::EachValidator
|
|
61
61
|
# In the case of an API error the validator will either mark the
|
62
62
|
# record as valid or invalid. Alternatively it will run an associated proc or
|
63
63
|
# re-raise the original error.
|
64
|
+
#
|
65
|
+
# The validation will short circuit and return with no errors added if the
|
66
|
+
# password is blank. The +Pwned::Password+ initializer expects the password to
|
67
|
+
# be a string and will throw a +TypeError+ if it is +nil+. Also, technically
|
68
|
+
# the empty string is not a password that is reported to be found in data
|
69
|
+
# breaches, so returns +false+, short circuiting that using +value.blank?+
|
70
|
+
# saves us a trip to the API.
|
71
|
+
#
|
72
|
+
# @param record [ActiveModel::Validations] The object being validated
|
73
|
+
# @param attribute [Symbol] The attribute on the record that is currently
|
74
|
+
# being validated.
|
75
|
+
# @param value [String] The value of the attribute on the record that is the
|
76
|
+
# subject of the validation
|
64
77
|
def validate_each(record, attribute, value)
|
78
|
+
return if value.blank?
|
65
79
|
begin
|
66
80
|
pwned_check = Pwned::Password.new(value, request_options)
|
67
81
|
if pwned_check.pwned_count > threshold
|
68
|
-
record.errors.add(attribute, :not_pwned, options.merge(count: pwned_check.pwned_count))
|
82
|
+
record.errors.add(attribute, :not_pwned, **options.merge(count: pwned_check.pwned_count))
|
69
83
|
end
|
70
84
|
rescue Pwned::Error => error
|
71
85
|
case on_error
|
72
86
|
when :invalid
|
73
|
-
record.errors.add(attribute, :pwned_error, options.merge(message: options[:error_message]))
|
87
|
+
record.errors.add(attribute, :pwned_error, **options.merge(message: options[:error_message]))
|
74
88
|
when :valid
|
75
89
|
# Do nothing, consider the record valid
|
76
90
|
when Proc
|
@@ -115,4 +129,4 @@ end
|
|
115
129
|
#
|
116
130
|
# @since 1.1.0
|
117
131
|
class PwnedValidator < NotPwnedValidator
|
118
|
-
end
|
132
|
+
end
|
data/lib/pwned/password.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
4
|
-
require "open-uri"
|
3
|
+
require "pwned/password_base"
|
5
4
|
|
6
5
|
module Pwned
|
7
6
|
##
|
@@ -9,27 +8,7 @@ module Pwned
|
|
9
8
|
# Pwned Passwords API to find out if the password has been pwned.
|
10
9
|
# @see https://haveibeenpwned.com/API/v2#PwnedPasswords
|
11
10
|
class Password
|
12
|
-
|
13
|
-
# The base URL for the Pwned Passwords API
|
14
|
-
API_URL = "https://api.pwnedpasswords.com/range/"
|
15
|
-
|
16
|
-
##
|
17
|
-
# The number of characters from the start of the hash of the password that
|
18
|
-
# are used to search for the range of passwords.
|
19
|
-
HASH_PREFIX_LENGTH = 5
|
20
|
-
|
21
|
-
##
|
22
|
-
# The total length of a SHA1 hash
|
23
|
-
SHA1_LENGTH = 40
|
24
|
-
|
25
|
-
##
|
26
|
-
# The default request options that are used to make HTTP requests to the
|
27
|
-
# API. A user agent is provided as requested in the documentation.
|
28
|
-
# @see https://haveibeenpwned.com/API/v2#UserAgent
|
29
|
-
DEFAULT_REQUEST_OPTIONS = {
|
30
|
-
"User-Agent" => "Ruby Pwned::Password #{Pwned::VERSION}"
|
31
|
-
}.freeze
|
32
|
-
|
11
|
+
include PasswordBase
|
33
12
|
##
|
34
13
|
# @return [String] the password that is being checked.
|
35
14
|
# @since 1.0.0
|
@@ -40,88 +19,24 @@ module Pwned
|
|
40
19
|
#
|
41
20
|
# @example A simple password with the default request options
|
42
21
|
# password = Pwned::Password.new("password")
|
43
|
-
# @example Setting the user agent and the read timeout of the
|
44
|
-
# password = Pwned::Password.new("password", "User-Agent" => "My user agent", :
|
22
|
+
# @example Setting the user agent and the read timeout of the request
|
23
|
+
# password = Pwned::Password.new("password", headers: { "User-Agent" => "My user agent" }, read_timout: 10)
|
45
24
|
#
|
46
25
|
# @param password [String] The password you want to check against the API.
|
47
|
-
# @param [Hash] request_options Options that can be passed to +
|
26
|
+
# @param [Hash] request_options Options that can be passed to +Net::HTTP.start+ when
|
48
27
|
# calling the API
|
49
|
-
# @option request_options [
|
50
|
-
#
|
28
|
+
# @option request_options [Symbol] :headers ({ "User-Agent" => "Ruby Pwned::Password #{Pwned::VERSION}" })
|
29
|
+
# HTTP headers to include in the request
|
51
30
|
# @return [Boolean] Whether the password appears in the data breaches or not.
|
52
31
|
# @raise [TypeError] if the password is not a string.
|
53
32
|
# @since 1.1.0
|
54
33
|
def initialize(password, request_options={})
|
55
34
|
raise TypeError, "password must be of type String" unless password.is_a? String
|
56
35
|
@password = password
|
57
|
-
@
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
# Returns the full SHA1 hash of the given password in uppercase.
|
62
|
-
# @return [String] The full SHA1 hash of the given password.
|
63
|
-
# @since 1.0.0
|
64
|
-
def hashed_password
|
65
|
-
@hashed_password ||= Digest::SHA1.hexdigest(password).upcase
|
66
|
-
end
|
67
|
-
|
68
|
-
##
|
69
|
-
# @example
|
70
|
-
# password = Pwned::Password.new("password")
|
71
|
-
# password.pwned? #=> true
|
72
|
-
#
|
73
|
-
# @return [Boolean] +true+ when the password has been pwned.
|
74
|
-
# @raise [Pwned::Error] if there are errors with the HTTP request.
|
75
|
-
# @raise [Pwned::TimeoutError] if the HTTP request times out.
|
76
|
-
# @since 1.0.0
|
77
|
-
def pwned?
|
78
|
-
pwned_count > 0
|
79
|
-
end
|
80
|
-
|
81
|
-
##
|
82
|
-
# @example
|
83
|
-
# password = Pwned::Password.new("password")
|
84
|
-
# password.pwned_count #=> 3303003
|
85
|
-
#
|
86
|
-
# @return [Integer] the number of times the password has been pwned.
|
87
|
-
# @raise [Pwned::Error] if there are errors with the HTTP request.
|
88
|
-
# @raise [Pwned::TimeoutError] if the HTTP request times out.
|
89
|
-
# @since 1.0.0
|
90
|
-
def pwned_count
|
91
|
-
@pwned_count ||= fetch_pwned_count
|
92
|
-
end
|
93
|
-
|
94
|
-
private
|
95
|
-
|
96
|
-
def fetch_pwned_count
|
97
|
-
for_each_response_line do |line|
|
98
|
-
next unless line.start_with?(hashed_password_suffix)
|
99
|
-
# Count starts after the suffix, followed by a colon
|
100
|
-
return line[(SHA1_LENGTH-HASH_PREFIX_LENGTH+1)..-1].to_i
|
101
|
-
end
|
102
|
-
|
103
|
-
# The hash was not found, we can assume the password is not pwned [yet]
|
104
|
-
0
|
105
|
-
end
|
106
|
-
|
107
|
-
def for_each_response_line(&block)
|
108
|
-
begin
|
109
|
-
open("#{API_URL}#{hashed_password_prefix}", @request_options) do |io|
|
110
|
-
io.each_line(&block)
|
111
|
-
end
|
112
|
-
rescue Timeout::Error => e
|
113
|
-
raise Pwned::TimeoutError, e.message
|
114
|
-
rescue => e
|
115
|
-
raise Pwned::Error, e.message
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
def hashed_password_prefix
|
120
|
-
hashed_password[0...HASH_PREFIX_LENGTH]
|
121
|
-
end
|
122
|
-
|
123
|
-
def hashed_password_suffix
|
124
|
-
hashed_password[HASH_PREFIX_LENGTH..-1]
|
36
|
+
@hashed_password = Pwned.hash_password(password)
|
37
|
+
@request_options = Hash(request_options).dup
|
38
|
+
@request_headers = Hash(request_options.delete(:headers))
|
39
|
+
@request_headers = DEFAULT_REQUEST_HEADERS.merge(@request_headers)
|
125
40
|
end
|
126
41
|
end
|
127
42
|
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "digest"
|
4
|
+
require "net/http"
|
5
|
+
|
6
|
+
module Pwned
|
7
|
+
##
|
8
|
+
# This class represents a password. It does all the work of talking to the
|
9
|
+
# Pwned Passwords API to find out if the password has been pwned.
|
10
|
+
# @see https://haveibeenpwned.com/API/v2#PwnedPasswords
|
11
|
+
module PasswordBase
|
12
|
+
##
|
13
|
+
# The base URL for the Pwned Passwords API
|
14
|
+
API_URL = "https://api.pwnedpasswords.com/range/"
|
15
|
+
|
16
|
+
##
|
17
|
+
# The number of characters from the start of the hash of the password that
|
18
|
+
# are used to search for the range of passwords.
|
19
|
+
HASH_PREFIX_LENGTH = 5
|
20
|
+
|
21
|
+
##
|
22
|
+
# The total length of a SHA1 hash
|
23
|
+
SHA1_LENGTH = 40
|
24
|
+
|
25
|
+
##
|
26
|
+
# The default request headers that are used to make HTTP requests to the
|
27
|
+
# API. A user agent is provided as requested in the documentation.
|
28
|
+
# @see https://haveibeenpwned.com/API/v2#UserAgent
|
29
|
+
DEFAULT_REQUEST_HEADERS = {
|
30
|
+
"User-Agent" => "Ruby Pwned::Password #{Pwned::VERSION}"
|
31
|
+
}.freeze
|
32
|
+
|
33
|
+
##
|
34
|
+
# @example
|
35
|
+
# password = Pwned::Password.new("password")
|
36
|
+
# password.pwned? #=> true
|
37
|
+
# password.pwned? #=> true
|
38
|
+
#
|
39
|
+
# @return [Boolean] +true+ when the password has been pwned.
|
40
|
+
# @raise [Pwned::Error] if there are errors with the HTTP request.
|
41
|
+
# @raise [Pwned::TimeoutError] if the HTTP request times out.
|
42
|
+
# @since 1.0.0
|
43
|
+
def pwned?
|
44
|
+
pwned_count > 0
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# @example
|
49
|
+
# password = Pwned::Password.new("password")
|
50
|
+
# password.pwned_count #=> 3303003
|
51
|
+
#
|
52
|
+
# @return [Integer] the number of times the password has been pwned.
|
53
|
+
# @raise [Pwned::Error] if there are errors with the HTTP request.
|
54
|
+
# @raise [Pwned::TimeoutError] if the HTTP request times out.
|
55
|
+
# @since 1.0.0
|
56
|
+
def pwned_count
|
57
|
+
@pwned_count ||= fetch_pwned_count
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# Returns the full SHA1 hash of the given password in uppercase.
|
62
|
+
# @return [String] The full SHA1 hash of the given password.
|
63
|
+
# @since 1.0.0
|
64
|
+
attr_reader :hashed_password
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
attr_reader :request_options, :request_headers
|
69
|
+
|
70
|
+
def fetch_pwned_count
|
71
|
+
for_each_response_line do |line|
|
72
|
+
next unless line.start_with?(hashed_password_suffix)
|
73
|
+
# Count starts after the suffix, followed by a colon
|
74
|
+
return line[(SHA1_LENGTH-HASH_PREFIX_LENGTH+1)..-1].to_i
|
75
|
+
end
|
76
|
+
|
77
|
+
# The hash was not found, we can assume the password is not pwned [yet]
|
78
|
+
0
|
79
|
+
end
|
80
|
+
|
81
|
+
def for_each_response_line(&block)
|
82
|
+
begin
|
83
|
+
with_http_response "#{API_URL}#{hashed_password_prefix}" do |response|
|
84
|
+
response.value # raise if request was unsuccessful
|
85
|
+
stream_response_lines(response, &block)
|
86
|
+
end
|
87
|
+
rescue Timeout::Error => e
|
88
|
+
raise Pwned::TimeoutError, e.message
|
89
|
+
rescue => e
|
90
|
+
raise Pwned::Error, e.message
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def hashed_password_prefix
|
95
|
+
@hashed_password[0...HASH_PREFIX_LENGTH]
|
96
|
+
end
|
97
|
+
|
98
|
+
def hashed_password_suffix
|
99
|
+
@hashed_password[HASH_PREFIX_LENGTH..-1]
|
100
|
+
end
|
101
|
+
|
102
|
+
# Make a HTTP GET request given the url and headers.
|
103
|
+
# Yields a `Net::HTTPResponse`.
|
104
|
+
def with_http_response(url, &block)
|
105
|
+
uri = URI(url)
|
106
|
+
|
107
|
+
request = Net::HTTP::Get.new(uri)
|
108
|
+
request.initialize_http_header(request_headers)
|
109
|
+
request_options[:use_ssl] = true
|
110
|
+
|
111
|
+
Net::HTTP.start(uri.host, uri.port, request_options) do |http|
|
112
|
+
http.request(request, &block)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Stream a Net::HTTPResponse by line, handling lines that cross chunks.
|
117
|
+
def stream_response_lines(response, &block)
|
118
|
+
last_line = ""
|
119
|
+
|
120
|
+
response.read_body do |chunk|
|
121
|
+
chunk_lines = (last_line + chunk).lines
|
122
|
+
# This could end with half a line, so save it for next time. If
|
123
|
+
# chunk_lines is empty, pop returns nil, so this also ensures last_line
|
124
|
+
# is always a string.
|
125
|
+
last_line = chunk_lines.pop || ""
|
126
|
+
chunk_lines.each(&block)
|
127
|
+
end
|
128
|
+
|
129
|
+
yield last_line unless last_line.empty?
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|
data/lib/pwned/version.rb
CHANGED
data/pwned.gemspec
CHANGED
@@ -13,13 +13,22 @@ Gem::Specification.new do |spec|
|
|
13
13
|
spec.homepage = "https://github.com/philnash/pwned"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
|
+
spec.metadata = {
|
17
|
+
"bug_tracker_uri" => "https://github.com/philnash/pwned/issues",
|
18
|
+
"change_log_uri" => "https://github.com/philnash/pwned/blob/master/CHANGELOG.md",
|
19
|
+
"documentation_uri" => "https://www.rubydoc.info/gems/pwned",
|
20
|
+
"homepage_uri" => "https://github.com/philnash/pwned",
|
21
|
+
"source_code_uri" => "https://github.com/philnash/pwned"
|
22
|
+
}
|
23
|
+
|
16
24
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
17
25
|
f.match(%r{^(test|spec|features)/})
|
18
26
|
end
|
19
27
|
spec.require_paths = ["lib"]
|
28
|
+
spec.executables = ["pwned"]
|
20
29
|
|
21
|
-
spec.add_development_dependency "bundler", "
|
22
|
-
spec.add_development_dependency "rake", "~>
|
30
|
+
spec.add_development_dependency "bundler", ">= 1.16", "< 3.0"
|
31
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
23
32
|
spec.add_development_dependency "rspec", "~> 3.0"
|
24
33
|
spec.add_development_dependency "webmock", "~> 3.3"
|
25
34
|
spec.add_development_dependency "yard", "~> 0.9.12"
|
metadata
CHANGED
@@ -1,43 +1,49 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pwned
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Phil Nash
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-07-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '1.16'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '3.0'
|
20
23
|
type: :development
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
|
-
- - "
|
27
|
+
- - ">="
|
25
28
|
- !ruby/object:Gem::Version
|
26
29
|
version: '1.16'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '3.0'
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
34
|
name: rake
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
30
36
|
requirements:
|
31
37
|
- - "~>"
|
32
38
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
39
|
+
version: '13.0'
|
34
40
|
type: :development
|
35
41
|
prerelease: false
|
36
42
|
version_requirements: !ruby/object:Gem::Requirement
|
37
43
|
requirements:
|
38
44
|
- - "~>"
|
39
45
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
46
|
+
version: '13.0'
|
41
47
|
- !ruby/object:Gem::Dependency
|
42
48
|
name: rspec
|
43
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -83,13 +89,15 @@ dependencies:
|
|
83
89
|
description: Tools to use the Pwned Passwords API.
|
84
90
|
email:
|
85
91
|
- philnash@gmail.com
|
86
|
-
executables:
|
92
|
+
executables:
|
93
|
+
- pwned
|
87
94
|
extensions: []
|
88
95
|
extra_rdoc_files: []
|
89
96
|
files:
|
90
97
|
- ".gitignore"
|
91
98
|
- ".rspec"
|
92
99
|
- ".travis.yml"
|
100
|
+
- ".yardopts"
|
93
101
|
- CHANGELOG.md
|
94
102
|
- CODE_OF_CONDUCT.md
|
95
103
|
- Gemfile
|
@@ -97,38 +105,26 @@ files:
|
|
97
105
|
- README.md
|
98
106
|
- Rakefile
|
99
107
|
- bin/console
|
108
|
+
- bin/pwned
|
100
109
|
- bin/setup
|
101
|
-
- docs/NotPwnedValidator.html
|
102
|
-
- docs/Pwned.html
|
103
|
-
- docs/Pwned/Error.html
|
104
|
-
- docs/Pwned/Password.html
|
105
|
-
- docs/Pwned/TimeoutError.html
|
106
|
-
- docs/PwnedValidator.html
|
107
|
-
- docs/_index.html
|
108
|
-
- docs/class_list.html
|
109
|
-
- docs/css/common.css
|
110
|
-
- docs/css/full_list.css
|
111
|
-
- docs/css/style.css
|
112
|
-
- docs/file.README.html
|
113
|
-
- docs/file_list.html
|
114
|
-
- docs/frames.html
|
115
|
-
- docs/index.html
|
116
|
-
- docs/js/app.js
|
117
|
-
- docs/js/full_list.js
|
118
|
-
- docs/js/jquery.js
|
119
|
-
- docs/method_list.html
|
120
|
-
- docs/top-level-namespace.html
|
121
110
|
- lib/locale/en.yml
|
122
111
|
- lib/pwned.rb
|
123
112
|
- lib/pwned/error.rb
|
113
|
+
- lib/pwned/hashed_password.rb
|
124
114
|
- lib/pwned/not_pwned_validator.rb
|
125
115
|
- lib/pwned/password.rb
|
116
|
+
- lib/pwned/password_base.rb
|
126
117
|
- lib/pwned/version.rb
|
127
118
|
- pwned.gemspec
|
128
119
|
homepage: https://github.com/philnash/pwned
|
129
120
|
licenses:
|
130
121
|
- MIT
|
131
|
-
metadata:
|
122
|
+
metadata:
|
123
|
+
bug_tracker_uri: https://github.com/philnash/pwned/issues
|
124
|
+
change_log_uri: https://github.com/philnash/pwned/blob/master/CHANGELOG.md
|
125
|
+
documentation_uri: https://www.rubydoc.info/gems/pwned
|
126
|
+
homepage_uri: https://github.com/philnash/pwned
|
127
|
+
source_code_uri: https://github.com/philnash/pwned
|
132
128
|
post_install_message:
|
133
129
|
rdoc_options: []
|
134
130
|
require_paths:
|
@@ -144,8 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
144
140
|
- !ruby/object:Gem::Version
|
145
141
|
version: '0'
|
146
142
|
requirements: []
|
147
|
-
|
148
|
-
rubygems_version: 2.7.6
|
143
|
+
rubygems_version: 3.0.3
|
149
144
|
signing_key:
|
150
145
|
specification_version: 4
|
151
146
|
summary: Tools to use the Pwned Passwords API.
|