hibp-client 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b0cfa93448ab5ba699ccf06ac4ca9804fcaa5effd45eb2f76c8a44002efd37f8
4
+ data.tar.gz: 4e62ca0302d615f411069604503823bc45224090ae6dd8c9ddce49b9a4181e22
5
+ SHA512:
6
+ metadata.gz: d3dd01fbe7202f178cd86927e17d5f54b1b6976b9afbaaa3432c0c1989c405fe58ff836ee1daab0a810bbf38bc18f85ec3da7ba19a90fd5d90fd73bb99a21ede
7
+ data.tar.gz: 52460687b7e9c93413bc2397827adcac2cbd23af7b7d99173e8a43cd34aeae26f0bee47c8d5603d31def57b1ad78e632f6ed4a9aee094cede44a30018f9512d0
@@ -0,0 +1,66 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ # Used by dotenv library to load environment variables.
14
+ # .env
15
+
16
+ # Ignore Byebug command history file.
17
+ .byebug_history
18
+
19
+ ## Specific to RubyMotion:
20
+ .dat*
21
+ .repl_history
22
+ build/
23
+ *.bridgesupport
24
+ build-iPhoneOS/
25
+ build-iPhoneSimulator/
26
+
27
+ ## Specific to RubyMotion (use of CocoaPods):
28
+ #
29
+ # We recommend against adding the Pods directory to your .gitignore. However
30
+ # you should judge for yourself, the pros and cons are mentioned at:
31
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
32
+ #
33
+ # vendor/Pods/
34
+
35
+ ## Documentation cache and generated files:
36
+ /.yardoc/
37
+ /_yardoc/
38
+ /doc/
39
+ /rdoc/
40
+
41
+ ## Environment normalization:
42
+ /.bundle/
43
+ /vendor/bundle
44
+ /lib/bundler/man/
45
+
46
+ # for a library or gem, you might want to ignore these files since the code is
47
+ # intended to run in multiple environments; otherwise, check them in:
48
+ # Gemfile.lock
49
+ # .ruby-version
50
+ # .ruby-gemset
51
+
52
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
53
+ .rvmrc
54
+
55
+ # IDE files
56
+ .idea
57
+
58
+ # rspec failure tracking
59
+ .rspec_status
60
+
61
+ # For those using rbenv
62
+ .ruby-version
63
+
64
+ # Don't checkin Gemfile.lock
65
+ # See: http://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/
66
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,13 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+
5
+ cache: bundler
6
+
7
+ before_install: gem install bundler
8
+
9
+ rvm:
10
+ - 2.6
11
+ - 2.5
12
+ - 2.4
13
+ - 2.3
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in hibp.gemspec
6
+
7
+ group :development, :test do
8
+ gem 'webmock', '~>1.24.0'
9
+ end
10
+
11
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 warshavski
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,189 @@
1
+ # Hibp
2
+
3
+ [![Build Status](https://travis-ci.com/Warshavski/hibp.svg?branch=master)](https://travis-ci.com/Warshavski/hibp)
4
+
5
+ A simple Ruby client for interacting with [Have I Been Pwned](https://haveibeenpwned.com/) REST API.
6
+
7
+ This gem based on [API v3](https://haveibeenpwned.com/API/v3)
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'hibp'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install hibp
24
+
25
+ ## Usage
26
+
27
+ This gem incapsulates all API requests and data transformation.
28
+
29
+ The top-level object needed for gem functionality is the `Hibp::Client` object.
30
+ A client may require your API key in case if you want to use services such as:
31
+
32
+ - Getting all breaches for an account.
33
+ - Getting all pastes for an account.
34
+
35
+ The API of the SDK is manipulated using `Hibp::Query` queries return different entities, but the mapping is not one to one.
36
+
37
+ Some of the methods support adding filters to them. The filters are created using the `Hibp::Query` class. After instantiating the class, you can invoke methods in the form of comparison(field, value).
38
+
39
+
40
+ ### Authorization
41
+
42
+ An authorization is required for all APIs that enable searching HIBP by email address, namely retrieving all breaches for an account and retrieving all pastes for an account.
43
+
44
+ An HIBP subscription key is required to make an authorized call and can be obtained on the API [key page](https://haveibeenpwned.com/API/Key).
45
+ The key is then passed as a client constructor argument:
46
+
47
+ ```ruby
48
+ client = Hibp::Client.new('api-key')
49
+ ```
50
+
51
+ ### Breaches
52
+
53
+ #### Getting all breached sites in the system
54
+
55
+ ```ruby
56
+ client = Hibp::Client.new
57
+
58
+ # Return the details of each breach in the system.
59
+ #
60
+ # => Array<Hibp::Breach>
61
+ #
62
+ client.breaches.fetch
63
+
64
+ # Return the details of each breach associated with a specific domain.
65
+ #
66
+ # => Array<Hibp::Breach>
67
+ client.breaches.where(domain: 'adobe.com').fetch
68
+ ```
69
+
70
+ #### Getting a single breached site
71
+
72
+ ```ruby
73
+ client = Hibp::Client.new
74
+
75
+ # Return the details of a single breach, by breach name.
76
+ #
77
+ # => Hibp::Breach
78
+ #
79
+ client.breach('000webhost').fetch
80
+ ```
81
+
82
+ #### Getting all data classes in the system
83
+
84
+ ```ruby
85
+ client = Hibp::Client.new
86
+
87
+ # Return the different types of data classes that are associated with a record in a breach. E.G, Email addresses and passwords
88
+ #
89
+ # => Array<String>
90
+ #
91
+ client.data_classes.fetch
92
+ ```
93
+
94
+ #### Getting all breaches for an account
95
+
96
+ NOTE :
97
+ - By default, only the name of the breach is returned rather than the complete breach data.
98
+ - By default, both verified and unverified breaches are returned when performing a search.
99
+
100
+ ```ruby
101
+ # API Key is required
102
+ client = Hibp::Client.new('api-key')
103
+
104
+ # Get all breaches for an account across all domains.
105
+ #
106
+ # => Array<Hibp::Breach>
107
+ #
108
+ client.account_breaches('example@email.com').fetch
109
+
110
+ # Get all breaches for an account across a specific domain.
111
+ #
112
+ # => Array<Hibp::Breach>
113
+ #
114
+ client.account_breaches('example@email.com').where(domain: 'adobe.com').fetch
115
+
116
+ # Get all breaches info for an account with detailed information.
117
+ #
118
+ # => Array<Hibp::Breach>
119
+ #
120
+ client.account_breaches('example@email.com').where(truncate: false).fetch
121
+
122
+ # Returns breaches that have been flagged as "unverified"
123
+ #
124
+ # => Array<Hibp::Breach>
125
+ #
126
+ client.account_breaches('example@email.com').where(unverified: true).fetch
127
+ ```
128
+
129
+ ### Pastes
130
+
131
+ #### Getting all pastes for an account
132
+
133
+ ```ruby
134
+ # API Key is required
135
+ client = Hibp::Client.new('api-key')
136
+
137
+ # Return any pastes that contain the given email address
138
+ #
139
+ # => Array<Hibp::Paste>
140
+ #
141
+ client.pastes('example@email.com').fetch
142
+ ```
143
+
144
+ ### Passwords
145
+
146
+ #### Getting passwords suffixes by range
147
+
148
+ ```ruby
149
+ client = Hibp::Client.new
150
+
151
+ # Get all suffixes of every hash beginning with the specified prefix, and a count of how many times it appears in the data set.
152
+ #
153
+ # => Array<Hibp::Password>
154
+ #
155
+ client.passwords('password').fetch
156
+
157
+ ```
158
+
159
+ ### Errors
160
+
161
+ This gem will throw custom exception if an API error occurred.
162
+
163
+ All service errors are wrapped in `Hibp::ServiceError`
164
+
165
+ | Code | Description |
166
+ |------|-------------------------------------------------------------------------------------------------|
167
+ | 400 | Bad request — The account does not comply with an acceptable format (i.e. it's an empty string) |
168
+ | 401 | Unauthorized - Access denied due to missing hibp-api-key. |
169
+ | 403 | Forbidden — No user agent has been specified in the request |
170
+ | 404 | Not found — The account could not be found and has therefore not been pwned |
171
+ | 429 | Too many requests — The rate limit has been exceeded |
172
+
173
+ ## Development
174
+
175
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
176
+
177
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
178
+
179
+ ## Contributing
180
+
181
+ Bug reports and pull requests are welcome on GitHub at https://github.com/warshavski/hibp. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
182
+
183
+ ## License
184
+
185
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
186
+
187
+ ## Code of Conduct
188
+
189
+ Everyone interacting in the Hibp project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/hibp/blob/master/CODE_OF_CONDUCT.md).
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'hibp'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'hibp/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'hibp-client'
9
+ spec.version = Hibp::VERSION
10
+ spec.authors = ['Warshavski']
11
+ spec.email = ['p.warshavski@gmail.com']
12
+
13
+ spec.summary = 'A simple tool to check if an account(email address and username) has been compromised in a data breach'
14
+ spec.description = 'A simple tool to check if an account(email address and username) has been compromised in a data breach'
15
+ spec.homepage = 'https://github.com/Warshavski/hibp'
16
+ spec.license = 'MIT'
17
+
18
+ spec.metadata['homepage_uri'] = spec.homepage
19
+ spec.metadata['source_code_uri'] = 'https://github.com/Warshavski/hibp'
20
+ # spec.metadata['changelog_uri'] = "TODO: Put your gem's CHANGELOG.md URL here."
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
+ end
27
+ spec.bindir = 'exe'
28
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
29
+ spec.require_paths = ['lib']
30
+
31
+ spec.add_dependency('faraday', '>= 0.9.1')
32
+ spec.add_dependency('oj', '>= 3.6.13')
33
+
34
+ spec.add_development_dependency 'bundler'
35
+ spec.add_development_dependency 'rake', '~> 10.0'
36
+ spec.add_development_dependency 'rspec', '~> 3.0'
37
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest'
4
+ require 'faraday'
5
+ require 'oj'
6
+
7
+ require 'hibp/helpers/attribute_assignment'
8
+ require 'hibp/helpers/json_conversion'
9
+
10
+ require 'hibp/models/breach'
11
+ require 'hibp/models/paste'
12
+ require 'hibp/models/password'
13
+
14
+ require 'hibp/parsers/json'
15
+ require 'hibp/parsers/breach'
16
+ require 'hibp/parsers/password'
17
+ require 'hibp/parsers/paste'
18
+
19
+ require 'hibp/client'
20
+ require 'hibp/request'
21
+ require 'hibp/query'
22
+
23
+ require 'hibp/service_error'
24
+
25
+ require 'hibp/version'
26
+
27
+ module Hibp
28
+ class Error < StandardError; end
29
+ # Your code goes here...
30
+ end
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hibp
4
+ # Hibp::Client
5
+ #
6
+ # Used to fetch data from haveibeenpwned API
7
+ #
8
+ # Public methods return `Hibp::Query` instance,
9
+ # which can be configured by applying filters
10
+ #
11
+ # Data will only be returned if the `#fetch` method is called on the `Hibp::Query` instance.
12
+ #
13
+ # @see https://haveibeenpwned.com/API/v3
14
+ #
15
+ class Client
16
+ CORE_API_HOST = 'https://haveibeenpwned.com/api/v3'
17
+ PASSWORD_API_HOST = 'https://api.pwnedpasswords.com/range'
18
+
19
+ CORE_API_SERVICES = {
20
+ breach: 'breach',
21
+ breaches: 'breaches',
22
+ account_breaches: 'breachedaccount',
23
+ data_classes: 'dataclasses',
24
+ pastes: 'pasteaccount'
25
+ }.freeze
26
+
27
+ attr_reader :authorization_header
28
+
29
+ # @param api_key [String] - (optional, default: '')
30
+ # Authorisation is required for all APIs that enable searching HIBP by email address,
31
+ # namely retrieving all breaches for an account and retrieving all pastes for an account.
32
+ # An HIBP subscription key is required to make an authorised call and can be obtained on the API key page.
33
+ # The key is then passed in a "hibp-api-key" header:
34
+ #
35
+ # @see https://haveibeenpwned.com/API/Key
36
+ #
37
+ def initialize(api_key = '')
38
+ @authorization_header = { 'hibp-api-key' => api_key }
39
+ end
40
+
41
+ # Find a single breached site
42
+ #
43
+ # @param name [String] - Breach name
44
+ #
45
+ # @note This is the stable value which may or may not be the same as the breach "title" (which can change).
46
+ #
47
+ # @return [Hibp::Query]
48
+ #
49
+ def breach(name)
50
+ configure_core_query(:breach, name)
51
+ end
52
+
53
+ # Fetch all breached sites in the system
54
+ # Available filters(domain)
55
+ #
56
+ # @note Collection is sorted alphabetically by the title of the breach.
57
+ #
58
+ # @return [Hibp::Query]
59
+ #
60
+ def breaches
61
+ configure_core_query(:breaches)
62
+ end
63
+
64
+ # Fetch a list of all breaches a particular account has been involved in.
65
+ # Available filters(truncate, unverified, domain)
66
+ #
67
+ # @param account [String] - The email address to be searched for.
68
+ #
69
+ # @note This method requires authorization. HIBP API key must be used.
70
+ # @note By default, only the name of the breach is returned rather than the complete breach data.
71
+ # @note By default, both verified and unverified breaches are returned when performing a search.
72
+ #
73
+ # @return [Hibp::Query]
74
+ #
75
+ def account_breaches(account)
76
+ configure_core_query(:account_breaches, CGI.escape(account))
77
+ end
78
+
79
+ # Fetch all data classes in the system
80
+ #
81
+ # A "data class" is an attribute of a record compromised in a breach.
82
+ # For example, many breaches expose data classes such as "Email addresses" and "Passwords".
83
+ # The values returned by this service are ordered alphabetically in
84
+ # a string array and will expand over time as new breaches expose previously unseen classes of data.
85
+ #
86
+ # @return [Hibp::Query]
87
+ #
88
+ def data_classes
89
+ configure_core_query(:data_classes)
90
+ end
91
+
92
+ # Search an account for pastes.
93
+ #
94
+ # HIBP searches through pastes that are broadcast by the @dumpmon Twitter
95
+ # account and reported as having emails that are a potential indicator of a breach.
96
+ #
97
+ # Finding an email address in a paste does not immediately mean it has been
98
+ # disclosed as the result of a breach. Review the paste and determine if your
99
+ # account has been compromised then take appropriate action such as changing passwords.
100
+ #
101
+ # @param account [String] - The email address to be searched for.
102
+ #
103
+ # @note This is an authenticated API and an HIBP API key must be passed with the request.
104
+ # @note The collection is sorted chronologically with the newest paste first.
105
+ #
106
+ # @return [Hibp::Query]
107
+ #
108
+ def pastes(account)
109
+ configure_core_query(:pastes, CGI.escape(account))
110
+ end
111
+
112
+ # Search pwned passwords
113
+ #
114
+ # @param password [String] -
115
+ # The value of the source password being searched for
116
+ #
117
+ # @note The API will respond with include the suffix of every hash beginning
118
+ # with the specified password prefix(five first chars of the password hash),
119
+ # and with a count of how many times it appears in the data set.
120
+ #
121
+ # @return [Hibp::Query]
122
+ #
123
+ def passwords(password)
124
+ configure_password_query(password)
125
+ end
126
+
127
+ private
128
+
129
+ def configure_password_query(password)
130
+ pwd_hash = ::Digest::SHA1.hexdigest(password).upcase
131
+ endpoint = "#{PASSWORD_API_HOST}/#{pwd_hash[0..4]}"
132
+
133
+ Query.new(endpoint: endpoint, parser: Parsers::Password.new)
134
+ end
135
+
136
+ def configure_core_query(service, parameter = nil)
137
+ endpoint = resolve_endpoint(service, parameter)
138
+ parser = resolve_parser(service)
139
+
140
+ Query.new(endpoint: endpoint, parser: parser, headers: @authorization_header)
141
+ end
142
+
143
+ def resolve_endpoint(service, parameter)
144
+ endpoint = "#{CORE_API_HOST}/#{CORE_API_SERVICES[service]}"
145
+
146
+ parameter ? "#{endpoint}/#{parameter}" : endpoint
147
+ end
148
+
149
+ def resolve_parser(service)
150
+ breach_services = %i[breach breaches account_bereaches]
151
+
152
+ case service
153
+ when ->(n) { breach_services.include?(n) }
154
+ Parsers::Breach.new
155
+ when :pastes
156
+ Parsers::Paste.new
157
+ when :data_classes
158
+ Parsers::Json.new
159
+ end
160
+ end
161
+ end
162
+ end