pwned 1.2.1 → 2.2.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/.github/FUNDING.yml +1 -0
- data/.github/workflows/tests.yml +39 -0
- data/CHANGELOG.md +75 -18
- data/README.md +153 -10
- data/bin/pwned +52 -0
- data/lib/pwned.rb +22 -6
- data/lib/pwned/hashed_password.rb +36 -0
- data/lib/pwned/not_pwned_validator.rb +3 -3
- data/lib/pwned/password.rb +12 -96
- data/lib/pwned/password_base.rb +141 -0
- data/lib/pwned/version.rb +1 -1
- data/pwned.gemspec +11 -2
- metadata +29 -34
- data/.travis.yml +0 -23
- data/docs/NotPwnedValidator.html +0 -488
- 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 -294
- data/docs/file_list.html +0 -56
- data/docs/frames.html +0 -17
- data/docs/index.html +0 -294
- 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,36 @@
|
|
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
|
+
@request_proxy = URI(request_options.delete(:proxy)) if request_options.key?(:proxy)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -79,12 +79,12 @@ class NotPwnedValidator < ActiveModel::EachValidator
|
|
79
79
|
begin
|
80
80
|
pwned_check = Pwned::Password.new(value, request_options)
|
81
81
|
if pwned_check.pwned_count > threshold
|
82
|
-
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))
|
83
83
|
end
|
84
84
|
rescue Pwned::Error => error
|
85
85
|
case on_error
|
86
86
|
when :invalid
|
87
|
-
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]))
|
88
88
|
when :valid
|
89
89
|
# Do nothing, consider the record valid
|
90
90
|
when Proc
|
@@ -129,4 +129,4 @@ end
|
|
129
129
|
#
|
130
130
|
# @since 1.1.0
|
131
131
|
class PwnedValidator < NotPwnedValidator
|
132
|
-
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,25 @@ 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
|
-
|
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)
|
40
|
+
@request_proxy = URI(request_options.delete(:proxy)) if request_options.key?(:proxy)
|
125
41
|
end
|
126
42
|
end
|
127
43
|
end
|
@@ -0,0 +1,141 @@
|
|
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, :request_proxy
|
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(
|
112
|
+
uri.host,
|
113
|
+
uri.port,
|
114
|
+
request_proxy&.host,
|
115
|
+
request_proxy&.port,
|
116
|
+
request_proxy&.user,
|
117
|
+
request_proxy&.password,
|
118
|
+
request_options
|
119
|
+
) do |http|
|
120
|
+
http.request(request, &block)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Stream a Net::HTTPResponse by line, handling lines that cross chunks.
|
125
|
+
def stream_response_lines(response, &block)
|
126
|
+
last_line = ""
|
127
|
+
|
128
|
+
response.read_body do |chunk|
|
129
|
+
chunk_lines = (last_line + chunk).lines
|
130
|
+
# This could end with half a line, so save it for next time. If
|
131
|
+
# chunk_lines is empty, pop returns nil, so this also ensures last_line
|
132
|
+
# is always a string.
|
133
|
+
last_line = chunk_lines.pop || ""
|
134
|
+
chunk_lines.each(&block)
|
135
|
+
end
|
136
|
+
|
137
|
+
yield last_line unless last_line.empty?
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
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:
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Phil Nash
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-03-27 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:
|
97
|
+
- ".github/FUNDING.yml"
|
98
|
+
- ".github/workflows/tests.yml"
|
90
99
|
- ".gitignore"
|
91
100
|
- ".rspec"
|
92
|
-
- ".travis.yml"
|
93
101
|
- ".yardopts"
|
94
102
|
- CHANGELOG.md
|
95
103
|
- CODE_OF_CONDUCT.md
|
@@ -98,39 +106,27 @@ files:
|
|
98
106
|
- README.md
|
99
107
|
- Rakefile
|
100
108
|
- bin/console
|
109
|
+
- bin/pwned
|
101
110
|
- bin/setup
|
102
|
-
- docs/NotPwnedValidator.html
|
103
|
-
- docs/Pwned.html
|
104
|
-
- docs/Pwned/Error.html
|
105
|
-
- docs/Pwned/Password.html
|
106
|
-
- docs/Pwned/TimeoutError.html
|
107
|
-
- docs/PwnedValidator.html
|
108
|
-
- docs/_index.html
|
109
|
-
- docs/class_list.html
|
110
|
-
- docs/css/common.css
|
111
|
-
- docs/css/full_list.css
|
112
|
-
- docs/css/style.css
|
113
|
-
- docs/file.README.html
|
114
|
-
- docs/file_list.html
|
115
|
-
- docs/frames.html
|
116
|
-
- docs/index.html
|
117
|
-
- docs/js/app.js
|
118
|
-
- docs/js/full_list.js
|
119
|
-
- docs/js/jquery.js
|
120
|
-
- docs/method_list.html
|
121
|
-
- docs/top-level-namespace.html
|
122
111
|
- lib/locale/en.yml
|
123
112
|
- lib/pwned.rb
|
124
113
|
- lib/pwned/error.rb
|
114
|
+
- lib/pwned/hashed_password.rb
|
125
115
|
- lib/pwned/not_pwned_validator.rb
|
126
116
|
- lib/pwned/password.rb
|
117
|
+
- lib/pwned/password_base.rb
|
127
118
|
- lib/pwned/version.rb
|
128
119
|
- pwned.gemspec
|
129
120
|
homepage: https://github.com/philnash/pwned
|
130
121
|
licenses:
|
131
122
|
- MIT
|
132
|
-
metadata:
|
133
|
-
|
123
|
+
metadata:
|
124
|
+
bug_tracker_uri: https://github.com/philnash/pwned/issues
|
125
|
+
change_log_uri: https://github.com/philnash/pwned/blob/master/CHANGELOG.md
|
126
|
+
documentation_uri: https://www.rubydoc.info/gems/pwned
|
127
|
+
homepage_uri: https://github.com/philnash/pwned
|
128
|
+
source_code_uri: https://github.com/philnash/pwned
|
129
|
+
post_install_message:
|
134
130
|
rdoc_options: []
|
135
131
|
require_paths:
|
136
132
|
- lib
|
@@ -145,9 +141,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
145
141
|
- !ruby/object:Gem::Version
|
146
142
|
version: '0'
|
147
143
|
requirements: []
|
148
|
-
|
149
|
-
|
150
|
-
signing_key:
|
144
|
+
rubygems_version: 3.2.3
|
145
|
+
signing_key:
|
151
146
|
specification_version: 4
|
152
147
|
summary: Tools to use the Pwned Passwords API.
|
153
148
|
test_files: []
|