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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c8ea296db01a984ba8d7229954dc752f1daf4a8c53ac9316724a7ed563d89f49
4
- data.tar.gz: 189505d178d0f44e8622cedc5104aeb84d9849a1c1d440051c9b75fa9c8bcb61
3
+ metadata.gz: b7a8b3d5d555b9e2144338cbac64916afe040753ab2bdb4c79d7cfb02f72d72c
4
+ data.tar.gz: ef4d623e45c017b03fa384e7d82a40caf3a00917405d0b49078a03ad006ccd42
5
5
  SHA512:
6
- metadata.gz: a6a9d4b0640499a25f03939275d1d133fa709351a9159472da883d6860810f4445e67e3d44e3a53936cb00510931a843ebaf1d2a0afecedc4ed674723d5821a4
7
- data.tar.gz: 66727a60ddf55a4499edbc3b4be1a8bcd3be7b05aafe8446d27055bbb274587e5d35925220aaa16790518568ae1ea1cca57da0bec4ab1023ba076812de6454dd
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
 
@@ -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
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module IPScanner
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
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.1.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: