ipscanner-io 0.1.0 → 0.3.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 +35 -1
- data/lib/ipscanner/client.rb +6 -1
- data/lib/ipscanner/http.rb +6 -6
- data/lib/ipscanner/resources/bulk.rb +2 -2
- data/lib/ipscanner/resources/edge.rb +19 -0
- data/lib/ipscanner/resources/email.rb +15 -0
- data/lib/ipscanner/resources/gate.rb +17 -0
- data/lib/ipscanner/resources/sites.rb +15 -0
- data/lib/ipscanner/version.rb +1 -1
- data/lib/ipscanner.rb +4 -0
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b7a8b3d5d555b9e2144338cbac64916afe040753ab2bdb4c79d7cfb02f72d72c
|
|
4
|
+
data.tar.gz: ef4d623e45c017b03fa384e7d82a40caf3a00917405d0b49078a03ad006ccd42
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e9a249af17bac70daa3d8e29a907ee6df249311b9e6d8786d36305ce3003f41af29770e5ab9cd85fbb8e6a46f8e5e944aebbf0ebd935c5ce395a5757de3dee57
|
|
7
|
+
data.tar.gz: 8a853e728a652899252ae604027244119baba699909bef53033541364b435bc0d2d7c5facce138cfb63f582aeeddc25c87b9a83165d17512567726ad39a70204
|
data/README.md
CHANGED
|
@@ -32,6 +32,8 @@ puts result["purity"]["grade"]
|
|
|
32
32
|
|
|
33
33
|
Every method returns the parsed JSON response as a Hash with the same string keys the API sends.
|
|
34
34
|
|
|
35
|
+
`vpnProvider` names the VPN brand when known. On the Free plan, premium fields come back as `nil` and are listed in `locked`, with `planRequired` naming the plan that unlocks them.
|
|
36
|
+
|
|
35
37
|
## Usage
|
|
36
38
|
|
|
37
39
|
### IP
|
|
@@ -48,6 +50,17 @@ client.ip.demo("1.1.1.1") # no key needed
|
|
|
48
50
|
client.ip.myip # no key needed
|
|
49
51
|
```
|
|
50
52
|
|
|
53
|
+
`vpn` and `proxy` carry `purity` (the score breakdown) on paid plans; a locked answer has `deductionCount` instead.
|
|
54
|
+
|
|
55
|
+
### Email
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
result = client.email.check("jane@outlok.com")
|
|
59
|
+
# result["status"] "risky", result["reason"] "typo", result["suggestion"] "jane@outlook.com"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Checks syntax, throwaway domains, whether the domain receives mail, typos of big webmail domains and role addresses. The mailbox itself is not probed. One request per address.
|
|
63
|
+
|
|
51
64
|
### Bulk
|
|
52
65
|
|
|
53
66
|
```ruby
|
|
@@ -79,6 +92,27 @@ client.agentscan.allowlist
|
|
|
79
92
|
client.agentscan.self_check
|
|
80
93
|
```
|
|
81
94
|
|
|
95
|
+
### Edge
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
client.edge.check(ip: "203.0.113.7", site: "your-site-id", user_agent: request.user_agent)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Sites
|
|
102
|
+
|
|
103
|
+
```ruby
|
|
104
|
+
client.sites.policy("your-site-id")
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Gate
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
result = client.gate.verify(secret: "gs_...", token: gate_token, remote_ip: request.remote_ip,
|
|
111
|
+
email: params["email"])
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
`gate.verify` sends the site secret instead of the API key and is never retried. `email` is optional; with it, `result["email"]` holds the check and its action, and `result["action"]` is the stricter of the network and email actions.
|
|
115
|
+
|
|
82
116
|
### Provenance
|
|
83
117
|
|
|
84
118
|
```ruby
|
|
@@ -147,7 +181,7 @@ IPScanner::Client.new(
|
|
|
147
181
|
)
|
|
148
182
|
```
|
|
149
183
|
|
|
150
|
-
Keyless endpoints (`ip.demo`, `ip.myip`, `asn_directory`, `crawlers`) work without an API key.
|
|
184
|
+
Keyless endpoints (`ip.demo`, `ip.myip`, `asn_directory`, `crawlers`, `gate.verify`) work without an API key.
|
|
151
185
|
|
|
152
186
|
## Licence
|
|
153
187
|
|
data/lib/ipscanner/client.rb
CHANGED
|
@@ -5,7 +5,8 @@ module IPScanner
|
|
|
5
5
|
class Client
|
|
6
6
|
DEFAULT_BASE_URL = "https://ipscanner.io"
|
|
7
7
|
|
|
8
|
-
attr_reader :ip, :bulk, :agentscan, :provenance, :account, :asn_directory, :crawlers
|
|
8
|
+
attr_reader :ip, :bulk, :agentscan, :provenance, :account, :asn_directory, :crawlers,
|
|
9
|
+
:edge, :sites, :gate, :email
|
|
9
10
|
|
|
10
11
|
def initialize(api_key: nil, base_url: nil, timeout: 30, max_retries: 2)
|
|
11
12
|
api_key ||= ENV.fetch("IPSCANNER_API_KEY", nil)
|
|
@@ -20,6 +21,10 @@ module IPScanner
|
|
|
20
21
|
@account = Resources::Account.new(http)
|
|
21
22
|
@asn_directory = Resources::AsnDirectory.new(http)
|
|
22
23
|
@crawlers = Resources::Crawlers.new(http)
|
|
24
|
+
@edge = Resources::Edge.new(http)
|
|
25
|
+
@sites = Resources::Sites.new(http)
|
|
26
|
+
@gate = Resources::Gate.new(http)
|
|
27
|
+
@email = Resources::Email.new(http)
|
|
23
28
|
end
|
|
24
29
|
end
|
|
25
30
|
end
|
data/lib/ipscanner/http.rb
CHANGED
|
@@ -48,8 +48,8 @@ module IPScanner
|
|
|
48
48
|
request(Net::HTTP::Get, path, query: query, raw: raw)
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
-
def post(path, body = {})
|
|
52
|
-
request(Net::HTTP::Post, path, body: body)
|
|
51
|
+
def post(path, body = {}, auth: true)
|
|
52
|
+
request(Net::HTTP::Post, path, body: body, auth: auth)
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
def stream(path, body)
|
|
@@ -78,14 +78,14 @@ module IPScanner
|
|
|
78
78
|
|
|
79
79
|
private
|
|
80
80
|
|
|
81
|
-
def request(klass, path, query: nil, body: nil, raw: false)
|
|
81
|
+
def request(klass, path, query: nil, body: nil, raw: false, auth: true)
|
|
82
82
|
uri = build_uri(path, query)
|
|
83
83
|
attempts = 0
|
|
84
84
|
loop do
|
|
85
85
|
attempts += 1
|
|
86
86
|
begin
|
|
87
87
|
res = with_errors do
|
|
88
|
-
connection(uri, @timeout).start { |http| http.request(build_request(klass, uri, body)) }
|
|
88
|
+
connection(uri, @timeout).start { |http| http.request(build_request(klass, uri, body, auth: auth)) }
|
|
89
89
|
end
|
|
90
90
|
rescue ConnectionError
|
|
91
91
|
raise unless retryable?(klass, attempts)
|
|
@@ -135,11 +135,11 @@ module IPScanner
|
|
|
135
135
|
http
|
|
136
136
|
end
|
|
137
137
|
|
|
138
|
-
def build_request(klass, uri, body)
|
|
138
|
+
def build_request(klass, uri, body, auth: true)
|
|
139
139
|
req = klass.new(uri)
|
|
140
140
|
req["Accept"] = "application/json"
|
|
141
141
|
req["User-Agent"] = "ipscanner-ruby/#{VERSION}"
|
|
142
|
-
req["Authorization"] = "Bearer #{@api_key}" if @api_key && !@api_key.empty?
|
|
142
|
+
req["Authorization"] = "Bearer #{@api_key}" if auth && @api_key && !@api_key.empty?
|
|
143
143
|
unless body.nil?
|
|
144
144
|
req["Content-Type"] = "application/json"
|
|
145
145
|
req.body = JSON.generate(body)
|
|
@@ -9,7 +9,7 @@ module IPScanner
|
|
|
9
9
|
EVENT_DEFAULTS = {
|
|
10
10
|
"type" => "", "index" => 0, "total" => 0, "input" => "", "reason" => "",
|
|
11
11
|
"message" => "", "error" => "", "ip" => "", "verdict" => "", "classification" => "",
|
|
12
|
-
"confidence" => 0.0, "anonymized" => false, "score" => 0, "grade" => "",
|
|
12
|
+
"confidence" => 0.0, "anonymized" => false, "vpnProvider" => "", "score" => 0, "grade" => "",
|
|
13
13
|
"isTorExit" => false, "asn" => "", "asnName" => "", "asnType" => "", "country" => "",
|
|
14
14
|
"processed" => 0, "failed" => 0, "metered" => 0
|
|
15
15
|
}.freeze
|
|
@@ -24,7 +24,7 @@ module IPScanner
|
|
|
24
24
|
return enum_for(:stream, ips: ips, input: input) unless block_given?
|
|
25
25
|
|
|
26
26
|
@http.stream("/v1/ip/bulk", body(ips, input)) do |line|
|
|
27
|
-
event = EVENT_DEFAULTS.merge(line)
|
|
27
|
+
event = EVENT_DEFAULTS.merge(line.compact)
|
|
28
28
|
if event["type"] == "done"
|
|
29
29
|
event["complete"] = event["reason"] == "complete" &&
|
|
30
30
|
event["processed"] + event["failed"] >= event["total"]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module IPScanner
|
|
6
|
+
module Resources
|
|
7
|
+
# Edge: one verdict per request from Agentscan and the network check.
|
|
8
|
+
class Edge < Base
|
|
9
|
+
# Classifies a request for a site. Metered at 2 units.
|
|
10
|
+
def check(ip:, site: nil, user_agent: nil, ja4: nil, headers: nil, headless_flags: nil, request_id: nil)
|
|
11
|
+
body = {
|
|
12
|
+
site: site, ip: ip, user_agent: user_agent, ja4: ja4, headers: headers,
|
|
13
|
+
headless_flags: headless_flags, request_id: request_id
|
|
14
|
+
}
|
|
15
|
+
@http.post("/v1/edge/check", compact(body))
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module IPScanner
|
|
6
|
+
module Resources
|
|
7
|
+
# Email: address checks before a signup is accepted.
|
|
8
|
+
class Email < Base
|
|
9
|
+
# Checks one address: syntax, throwaway domains, mail servers, typos and role addresses. One request.
|
|
10
|
+
def check(email)
|
|
11
|
+
@http.post("/v1/email/check", { email: email })
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module IPScanner
|
|
6
|
+
module Resources
|
|
7
|
+
# Gate: server-side redemption of form tokens.
|
|
8
|
+
class Gate < Base
|
|
9
|
+
# Redeems a gate token with the site secret. Sends no API key and is never retried.
|
|
10
|
+
# email (optional) adds an email check, one request.
|
|
11
|
+
def verify(secret:, token:, remote_ip: nil, email: nil)
|
|
12
|
+
body = compact({ secret: secret, token: token, remote_ip: remote_ip, email: email })
|
|
13
|
+
@http.post("/v1/gate/verify", body, auth: false)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module IPScanner
|
|
6
|
+
module Resources
|
|
7
|
+
# Sites registered for edge checks and the gate.
|
|
8
|
+
class Sites < Base
|
|
9
|
+
# Current policy for a site. Not metered.
|
|
10
|
+
def policy(id)
|
|
11
|
+
@http.get("/v1/sites/#{escape(id)}/policy")
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/ipscanner/version.rb
CHANGED
data/lib/ipscanner.rb
CHANGED
|
@@ -10,6 +10,10 @@ require_relative "ipscanner/resources/provenance"
|
|
|
10
10
|
require_relative "ipscanner/resources/account"
|
|
11
11
|
require_relative "ipscanner/resources/asn_directory"
|
|
12
12
|
require_relative "ipscanner/resources/crawlers"
|
|
13
|
+
require_relative "ipscanner/resources/edge"
|
|
14
|
+
require_relative "ipscanner/resources/sites"
|
|
15
|
+
require_relative "ipscanner/resources/gate"
|
|
16
|
+
require_relative "ipscanner/resources/email"
|
|
13
17
|
require_relative "ipscanner/client"
|
|
14
18
|
|
|
15
19
|
# Ruby client for the IPScanner API.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ipscanner-io
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- IPScanner
|
|
@@ -27,8 +27,12 @@ files:
|
|
|
27
27
|
- lib/ipscanner/resources/base.rb
|
|
28
28
|
- lib/ipscanner/resources/bulk.rb
|
|
29
29
|
- lib/ipscanner/resources/crawlers.rb
|
|
30
|
+
- lib/ipscanner/resources/edge.rb
|
|
31
|
+
- lib/ipscanner/resources/email.rb
|
|
32
|
+
- lib/ipscanner/resources/gate.rb
|
|
30
33
|
- lib/ipscanner/resources/ip.rb
|
|
31
34
|
- lib/ipscanner/resources/provenance.rb
|
|
35
|
+
- lib/ipscanner/resources/sites.rb
|
|
32
36
|
- lib/ipscanner/version.rb
|
|
33
37
|
homepage: https://ipscanner.io
|
|
34
38
|
licenses:
|