xposedornot 1.0.2 → 1.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/README.md +36 -10
- data/lib/xposedornot/endpoints/breaches.rb +22 -2
- data/lib/xposedornot/endpoints/email.rb +17 -7
- data/lib/xposedornot/models/breach_analytics_response.rb +34 -1
- data/lib/xposedornot/models/domain_breaches_response.rb +81 -0
- data/lib/xposedornot/models/email_breach_response.rb +9 -1
- data/lib/xposedornot/version.rb +1 -1
- data/lib/xposedornot.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d5e29be49035702970f6e4420c5461f54fd862530298e3e1ab99d77cdfaccc86
|
|
4
|
+
data.tar.gz: 315dcc82cbcc4de0f3e9ad8715f960fe5f4eadcee8a0df1050b7058036f90164
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 728126a8268521381cb7a2deed69dd54b7864fbfb6da0ce943919805779050d913aea1725af04fde5c2c172d6936e18510a6058a01f5ffa685e040992d4a5efb
|
|
7
|
+
data.tar.gz: 5b946852623b383499b3934cab2db55d40cc02a21c2d8559136ae7695a38b84edd9c8168f7b35fe54e581b87d17ee08761b28e39daf99cd7f397dae107794566
|
data/README.md
CHANGED
|
@@ -30,9 +30,10 @@
|
|
|
30
30
|
- [Requirements](#requirements)
|
|
31
31
|
- [Quick Start](#quick-start)
|
|
32
32
|
- [API Reference](#api-reference)
|
|
33
|
-
- [check_email](#check_emailemail)
|
|
34
|
-
- [get_breaches](#get_breachesdomain)
|
|
35
|
-
- [
|
|
33
|
+
- [check_email](#check_emailemail-include_details-false)
|
|
34
|
+
- [get_breaches](#get_breachesdomain-breach_id)
|
|
35
|
+
- [get_domain_breaches](#get_domain_breaches)
|
|
36
|
+
- [breach_analytics](#breach_analyticsemail-token-nil)
|
|
36
37
|
- [check_password](#check_passwordpassword)
|
|
37
38
|
- [Error Handling](#error-handling)
|
|
38
39
|
- [Rate Limits](#rate-limits)
|
|
@@ -104,9 +105,12 @@ See [Configuration](#configuration) for all available options.
|
|
|
104
105
|
|
|
105
106
|
### Methods
|
|
106
107
|
|
|
107
|
-
#### `check_email(email)`
|
|
108
|
+
#### `check_email(email, include_details: false)`
|
|
108
109
|
|
|
109
|
-
Check if an email address has been exposed in any data breaches.
|
|
110
|
+
Check if an email address has been exposed in any data breaches.
|
|
111
|
+
|
|
112
|
+
- **Without API key**: Uses the free API, returns `EmailBreachResponse` with breach names only. Pass `include_details: true` to request detailed breach information.
|
|
113
|
+
- **With API key**: Uses the Plus API (`plus-api.xposedornot.com`), returns `EmailBreachDetailedResponse` with full breach details (`include_details` is ignored - the Plus API is always queried with detailed results).
|
|
110
114
|
|
|
111
115
|
```ruby
|
|
112
116
|
# Free API
|
|
@@ -122,9 +126,9 @@ puts result.breaches.first.breach_id
|
|
|
122
126
|
puts result.breaches.first.password_risk
|
|
123
127
|
```
|
|
124
128
|
|
|
125
|
-
#### `get_breaches(domain:)`
|
|
129
|
+
#### `get_breaches(domain:, breach_id:)`
|
|
126
130
|
|
|
127
|
-
Get a list of all known data breaches, optionally filtered by domain.
|
|
131
|
+
Get a list of all known data breaches, optionally filtered by domain or breach ID.
|
|
128
132
|
|
|
129
133
|
```ruby
|
|
130
134
|
# Get all breaches
|
|
@@ -133,6 +137,9 @@ breaches = client.get_breaches
|
|
|
133
137
|
# Filter by domain
|
|
134
138
|
adobe_breaches = client.get_breaches(domain: 'adobe.com')
|
|
135
139
|
|
|
140
|
+
# Fetch a specific breach by ID
|
|
141
|
+
adobe = client.get_breaches(breach_id: 'Adobe')
|
|
142
|
+
|
|
136
143
|
breaches.each do |breach|
|
|
137
144
|
puts "#{breach.breach_id} - #{breach.domain} (#{breach.exposed_records} records)"
|
|
138
145
|
end
|
|
@@ -143,18 +150,37 @@ end
|
|
|
143
150
|
| Parameter | Type | Description |
|
|
144
151
|
|-----------|------|-------------|
|
|
145
152
|
| `domain` | `String` | Optional. Filter breaches by domain |
|
|
153
|
+
| `breach_id` | `String` | Optional. Fetch a specific breach by ID |
|
|
146
154
|
|
|
147
155
|
**Returns:** `Array<Models::Breach>` with properties such as `breach_id`, `breached_date`, `domain`, `industry`, `exposed_data`, `exposed_records`, and `verified`.
|
|
148
156
|
|
|
149
|
-
#### `
|
|
157
|
+
#### `get_domain_breaches`
|
|
158
|
+
|
|
159
|
+
Get breach information for domains verified against your API key (requires an API key with verified domains configured at [console.xposedornot.com](https://console.xposedornot.com)).
|
|
160
|
+
|
|
161
|
+
```ruby
|
|
162
|
+
client = XposedOrNot::Client.new(api_key: 'your-api-key')
|
|
163
|
+
report = client.get_domain_breaches
|
|
164
|
+
|
|
165
|
+
puts report.domain_summary # Breach counts per domain
|
|
166
|
+
puts report.yearly_metrics # Breach counts by year
|
|
167
|
+
puts report.top10_breaches # Top 10 largest breaches
|
|
168
|
+
|
|
169
|
+
report.breaches_details.each do |record|
|
|
170
|
+
puts "#{record.email} #{record.domain} #{record.breach}"
|
|
171
|
+
end
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
#### `breach_analytics(email, token: nil)`
|
|
150
175
|
|
|
151
|
-
Get detailed breach analytics for an email address, including breach summaries, metrics, and paste exposures.
|
|
176
|
+
Get detailed breach analytics for an email address, including breach summaries, metrics, and paste exposures. Pass `token` to access sensitive breach data.
|
|
152
177
|
|
|
153
178
|
```ruby
|
|
154
179
|
analytics = client.breach_analytics('user@example.com')
|
|
155
180
|
|
|
181
|
+
puts analytics.breaches_count # Number of breaches
|
|
182
|
+
puts analytics.breach_names # Names of breaches the email was found in
|
|
156
183
|
puts analytics.breaches_details.length
|
|
157
|
-
puts analytics.breaches_summary
|
|
158
184
|
puts analytics.breach_metrics
|
|
159
185
|
puts analytics.exposed_pastes
|
|
160
186
|
```
|
|
@@ -4,18 +4,38 @@ module XposedOrNot
|
|
|
4
4
|
module Endpoints
|
|
5
5
|
# Breaches listing endpoint.
|
|
6
6
|
module Breaches
|
|
7
|
-
# Get a list of all known breaches, optionally filtered by domain.
|
|
7
|
+
# Get a list of all known breaches, optionally filtered by domain or breach ID.
|
|
8
8
|
#
|
|
9
9
|
# @param domain [String, nil] optional domain to filter results
|
|
10
|
+
# @param breach_id [String, nil] optional breach ID to fetch a specific breach
|
|
10
11
|
# @return [Array<Models::Breach>] list of breach records
|
|
11
|
-
def get_breaches(domain: nil)
|
|
12
|
+
def get_breaches(domain: nil, breach_id: nil)
|
|
12
13
|
params = {}
|
|
13
14
|
params[:domain] = domain if domain
|
|
15
|
+
params[:breach_id] = breach_id if breach_id
|
|
14
16
|
|
|
15
17
|
response = request(:get, "/v1/breaches", base: :free, params: params)
|
|
16
18
|
raw = response["exposedBreaches"] || []
|
|
17
19
|
raw.map { |b| Models::Breach.new(b) }
|
|
18
20
|
end
|
|
21
|
+
|
|
22
|
+
# Get breach information for domains verified against the API key.
|
|
23
|
+
#
|
|
24
|
+
# Requires an API key with verified domains configured at
|
|
25
|
+
# console.xposedornot.com.
|
|
26
|
+
#
|
|
27
|
+
# @return [Models::DomainBreachesResponse] metrics and exposed email records
|
|
28
|
+
# @raise [AuthenticationError] if no API key is configured or the key is invalid
|
|
29
|
+
def get_domain_breaches
|
|
30
|
+
unless @config.plus_api?
|
|
31
|
+
raise AuthenticationError,
|
|
32
|
+
"An API key is required for domain breach monitoring. " \
|
|
33
|
+
"Get one at console.xposedornot.com"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
response = request(:post, "/v1/domain-breaches", base: :free)
|
|
37
|
+
Models::DomainBreachesResponse.new(response)
|
|
38
|
+
end
|
|
19
39
|
end
|
|
20
40
|
end
|
|
21
41
|
end
|
|
@@ -12,37 +12,47 @@ module XposedOrNot
|
|
|
12
12
|
# Otherwise, uses the free API.
|
|
13
13
|
#
|
|
14
14
|
# @param email [String] the email address to check
|
|
15
|
+
# @param include_details [Boolean] request detailed breach information from the
|
|
16
|
+
# free API; ignored when an API key is set (the Plus API is always queried
|
|
17
|
+
# with detailed results)
|
|
15
18
|
# @return [Models::EmailBreachResponse, Models::EmailBreachDetailedResponse]
|
|
16
19
|
# @raise [ValidationError] if the email is invalid
|
|
17
|
-
# @raise [NotFoundError] if the email
|
|
18
|
-
def check_email(email)
|
|
20
|
+
# @raise [NotFoundError] if the Plus API reports the email as not found
|
|
21
|
+
def check_email(email, include_details: false)
|
|
19
22
|
Utils.validate_email(email)
|
|
20
23
|
|
|
21
24
|
if @config.plus_api?
|
|
22
25
|
check_email_detailed(email)
|
|
23
26
|
else
|
|
24
|
-
check_email_free(email)
|
|
27
|
+
check_email_free(email, include_details: include_details)
|
|
25
28
|
end
|
|
26
29
|
end
|
|
27
30
|
|
|
28
31
|
# Get breach analytics for an email address.
|
|
29
32
|
#
|
|
30
33
|
# @param email [String] the email address to analyze
|
|
34
|
+
# @param token [String, nil] optional token for accessing sensitive breach data
|
|
31
35
|
# @return [Models::BreachAnalyticsResponse]
|
|
32
36
|
# @raise [ValidationError] if the email is invalid
|
|
33
|
-
def breach_analytics(email)
|
|
37
|
+
def breach_analytics(email, token: nil)
|
|
34
38
|
Utils.validate_email(email)
|
|
35
39
|
|
|
36
|
-
|
|
40
|
+
params = { email: email }
|
|
41
|
+
params[:token] = token if token
|
|
42
|
+
|
|
43
|
+
response = request(:get, "/v1/breach-analytics", base: :free, params: params)
|
|
37
44
|
Models::BreachAnalyticsResponse.new(response)
|
|
38
45
|
end
|
|
39
46
|
|
|
40
47
|
private
|
|
41
48
|
|
|
42
49
|
# @param email [String]
|
|
50
|
+
# @param include_details [Boolean]
|
|
43
51
|
# @return [Models::EmailBreachResponse]
|
|
44
|
-
def check_email_free(email)
|
|
45
|
-
|
|
52
|
+
def check_email_free(email, include_details: false)
|
|
53
|
+
params = include_details ? { include_details: "true" } : {}
|
|
54
|
+
response = request(:get, "/v1/check-email/#{URI.encode_www_form_component(email)}", base: :free,
|
|
55
|
+
params: params)
|
|
46
56
|
Models::EmailBreachResponse.new(response)
|
|
47
57
|
rescue NotFoundError
|
|
48
58
|
# 404 means email not found in any breaches — valid result
|
|
@@ -19,6 +19,21 @@ module XposedOrNot
|
|
|
19
19
|
# @return [Array<Hash>] exposed pastes
|
|
20
20
|
attr_reader :exposed_pastes
|
|
21
21
|
|
|
22
|
+
# @return [Array<String>] names of breaches the email was found in
|
|
23
|
+
attr_reader :breach_names
|
|
24
|
+
|
|
25
|
+
# @return [Integer] number of breaches the email was found in
|
|
26
|
+
attr_reader :breaches_count
|
|
27
|
+
|
|
28
|
+
# @return [Integer] total number of exposures
|
|
29
|
+
attr_reader :exposures_count
|
|
30
|
+
|
|
31
|
+
# @return [String] date of first breach
|
|
32
|
+
attr_reader :first_breach
|
|
33
|
+
|
|
34
|
+
# @return [Integer] number of pastes the email was found in
|
|
35
|
+
attr_reader :pastes_count
|
|
36
|
+
|
|
22
37
|
# @param data [Hash] raw response data from the API
|
|
23
38
|
def initialize(data)
|
|
24
39
|
exposed = data["ExposedBreaches"] || {}
|
|
@@ -28,6 +43,19 @@ module XposedOrNot
|
|
|
28
43
|
@breach_metrics = data["BreachMetrics"] || {}
|
|
29
44
|
@pastes_summary = data["PastesSummary"] || {}
|
|
30
45
|
@exposed_pastes = data["ExposedPastes"] || []
|
|
46
|
+
|
|
47
|
+
site = @breaches_summary["site"]
|
|
48
|
+
if site.is_a?(String)
|
|
49
|
+
@breach_names = site.split(";").reject(&:empty?)
|
|
50
|
+
@breaches_count = @breach_names.length
|
|
51
|
+
else
|
|
52
|
+
@breach_names = []
|
|
53
|
+
@breaches_count = site || 0
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
@exposures_count = @breaches_summary["exposures"] || @breaches_details.length
|
|
57
|
+
@first_breach = @breaches_summary["first_breach"] || ""
|
|
58
|
+
@pastes_count = @pastes_summary["cnt"] || 0
|
|
31
59
|
end
|
|
32
60
|
|
|
33
61
|
# @return [Hash] hash representation
|
|
@@ -37,7 +65,12 @@ module XposedOrNot
|
|
|
37
65
|
breaches_summary: @breaches_summary,
|
|
38
66
|
breach_metrics: @breach_metrics,
|
|
39
67
|
pastes_summary: @pastes_summary,
|
|
40
|
-
exposed_pastes: @exposed_pastes
|
|
68
|
+
exposed_pastes: @exposed_pastes,
|
|
69
|
+
breach_names: @breach_names,
|
|
70
|
+
breaches_count: @breaches_count,
|
|
71
|
+
exposures_count: @exposures_count,
|
|
72
|
+
first_breach: @first_breach,
|
|
73
|
+
pastes_count: @pastes_count
|
|
41
74
|
}
|
|
42
75
|
end
|
|
43
76
|
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module XposedOrNot
|
|
4
|
+
module Models
|
|
5
|
+
# A single exposed email record from the domain-breaches endpoint.
|
|
6
|
+
class DomainBreachDetail
|
|
7
|
+
# @return [String] email address exposed in the breach
|
|
8
|
+
attr_reader :email
|
|
9
|
+
|
|
10
|
+
# @return [String] verified domain the email belongs to
|
|
11
|
+
attr_reader :domain
|
|
12
|
+
|
|
13
|
+
# @return [String] name of the breach the email was found in
|
|
14
|
+
attr_reader :breach
|
|
15
|
+
|
|
16
|
+
# @param data [Hash] raw record data from the API
|
|
17
|
+
def initialize(data)
|
|
18
|
+
@email = data["email"] || ""
|
|
19
|
+
@domain = data["domain"] || ""
|
|
20
|
+
@breach = data["breach"] || ""
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @return [Hash] hash representation
|
|
24
|
+
def to_h
|
|
25
|
+
{ email: @email, domain: @domain, breach: @breach }
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Response from the domain-breaches endpoint.
|
|
30
|
+
#
|
|
31
|
+
# Contains breach metrics and statistics for domains verified
|
|
32
|
+
# against the API key.
|
|
33
|
+
class DomainBreachesResponse
|
|
34
|
+
# @return [String] response status ("success" or "error")
|
|
35
|
+
attr_reader :status
|
|
36
|
+
|
|
37
|
+
# @return [Array<DomainBreachDetail>] exposed email records across the verified domains
|
|
38
|
+
attr_reader :breaches_details
|
|
39
|
+
|
|
40
|
+
# @return [Hash] breach counts by year
|
|
41
|
+
attr_reader :yearly_metrics
|
|
42
|
+
|
|
43
|
+
# @return [Hash] summary of breaches by domain
|
|
44
|
+
attr_reader :domain_summary
|
|
45
|
+
|
|
46
|
+
# @return [Hash] summary of all breaches
|
|
47
|
+
attr_reader :breach_summary
|
|
48
|
+
|
|
49
|
+
# @return [Hash] top 10 largest breaches affecting the domains
|
|
50
|
+
attr_reader :top10_breaches
|
|
51
|
+
|
|
52
|
+
# @return [Hash] detailed information about each breach
|
|
53
|
+
attr_reader :detailed_breach_info
|
|
54
|
+
|
|
55
|
+
# @param data [Hash] raw response data from the API
|
|
56
|
+
def initialize(data)
|
|
57
|
+
metrics = data["metrics"] || {}
|
|
58
|
+
@status = data["status"] || ""
|
|
59
|
+
@breaches_details = (metrics["Breaches_Details"] || []).map { |b| DomainBreachDetail.new(b) }
|
|
60
|
+
@yearly_metrics = metrics["Yearly_Metrics"] || {}
|
|
61
|
+
@domain_summary = metrics["Domain_Summary"] || {}
|
|
62
|
+
@breach_summary = metrics["Breach_Summary"] || {}
|
|
63
|
+
@top10_breaches = metrics["Top10_Breaches"] || {}
|
|
64
|
+
@detailed_breach_info = metrics["Detailed_Breach_Info"] || {}
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# @return [Hash] hash representation
|
|
68
|
+
def to_h
|
|
69
|
+
{
|
|
70
|
+
status: @status,
|
|
71
|
+
breaches_details: @breaches_details.map(&:to_h),
|
|
72
|
+
yearly_metrics: @yearly_metrics,
|
|
73
|
+
domain_summary: @domain_summary,
|
|
74
|
+
breach_summary: @breach_summary,
|
|
75
|
+
top10_breaches: @top10_breaches,
|
|
76
|
+
detailed_breach_info: @detailed_breach_info
|
|
77
|
+
}
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -7,6 +7,12 @@ module XposedOrNot
|
|
|
7
7
|
# @return [Array<String>] list of breach names
|
|
8
8
|
attr_reader :breaches
|
|
9
9
|
|
|
10
|
+
# @return [String] the email address that was checked
|
|
11
|
+
attr_reader :email
|
|
12
|
+
|
|
13
|
+
# @return [String] response status ("success" or "error")
|
|
14
|
+
attr_reader :status
|
|
15
|
+
|
|
10
16
|
# @param data [Hash] raw response data from the API
|
|
11
17
|
def initialize(data)
|
|
12
18
|
raw = data["breaches"]
|
|
@@ -17,6 +23,8 @@ module XposedOrNot
|
|
|
17
23
|
else
|
|
18
24
|
[]
|
|
19
25
|
end
|
|
26
|
+
@email = data["email"] || ""
|
|
27
|
+
@status = data["status"] || ""
|
|
20
28
|
end
|
|
21
29
|
|
|
22
30
|
# @return [Boolean] true if the email was found in any breaches
|
|
@@ -31,7 +39,7 @@ module XposedOrNot
|
|
|
31
39
|
|
|
32
40
|
# @return [Hash] hash representation
|
|
33
41
|
def to_h
|
|
34
|
-
{ breaches: @breaches, breached: breached?, count: count }
|
|
42
|
+
{ breaches: @breaches, breached: breached?, count: count, email: @email, status: @status }
|
|
35
43
|
end
|
|
36
44
|
end
|
|
37
45
|
end
|
data/lib/xposedornot/version.rb
CHANGED
data/lib/xposedornot.rb
CHANGED
|
@@ -8,6 +8,7 @@ require_relative "xposedornot/models/breach"
|
|
|
8
8
|
require_relative "xposedornot/models/email_breach_response"
|
|
9
9
|
require_relative "xposedornot/models/email_breach_detailed_response"
|
|
10
10
|
require_relative "xposedornot/models/breach_analytics_response"
|
|
11
|
+
require_relative "xposedornot/models/domain_breaches_response"
|
|
11
12
|
require_relative "xposedornot/models/password_check_response"
|
|
12
13
|
require_relative "xposedornot/endpoints/email"
|
|
13
14
|
require_relative "xposedornot/endpoints/breaches"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: xposedornot
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- XposedOrNot
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-07-18 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|
|
@@ -72,6 +72,7 @@ files:
|
|
|
72
72
|
- lib/xposedornot/errors.rb
|
|
73
73
|
- lib/xposedornot/models/breach.rb
|
|
74
74
|
- lib/xposedornot/models/breach_analytics_response.rb
|
|
75
|
+
- lib/xposedornot/models/domain_breaches_response.rb
|
|
75
76
|
- lib/xposedornot/models/email_breach_detailed_response.rb
|
|
76
77
|
- lib/xposedornot/models/email_breach_response.rb
|
|
77
78
|
- lib/xposedornot/models/password_check_response.rb
|