shodanz 2.0.1 → 2.0.5

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: 32aa0999f1cb087637e0abe8428bd70592f22a3761da87be09bcf1a2bd8c2d37
4
- data.tar.gz: a6d8a053446c3163bb3ee7630c5663510e6568469cd0569e6c6d4ad67d1873a3
3
+ metadata.gz: 6e20a19dd003d869e2c49037298314d78161c8b4d64d63057b90e34654a695dc
4
+ data.tar.gz: a3478a19294e1b3a8bba8d97b9903429ebefa8d0520c300802eb8293fcb8b578
5
5
  SHA512:
6
- metadata.gz: d3c5e27187e510f5b811bee421194b269bcc1092bc4b212c7a172c342f1b6a4c932314e5198e89b16509ce6c1345714e232461e4c3b34de1bf2262797a435bdf
7
- data.tar.gz: d66f4fc7e68963a54870b789a1b63ada8ffe3174947f6b641dfa8b43547d77ef37b73fc16ff7c4f8715244be8449037dcc5fa2b7028cf50424b43a6004c4297b
6
+ metadata.gz: 07aa5fa81751be8cf85ac4c048c1343bccc2f22fe1ef315d56eaa9fb69bdea8a0c0f9c02f0fd431f4480f7b4c10bc102346cb7169784f6cbadf3c71a47744376
7
+ data.tar.gz: 0b31ab27f9fc15b49d1b0ca7ba73c4964aa4569fddd65db6ed887ffefb1f9dff593b3b26ad26e9d8d2d0bd7e1762f017424ac56036e0b0378f78730140b0da25
@@ -0,0 +1,15 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: bundler
4
+ directory: "/"
5
+ schedule:
6
+ interval: daily
7
+ time: "10:00"
8
+ open-pull-requests-limit: 10
9
+ ignore:
10
+ - dependency-name: async-http
11
+ versions:
12
+ - 0.55.0
13
+ - dependency-name: pry
14
+ versions:
15
+ - 0.14.0
@@ -1,18 +1,22 @@
1
1
  name: CI
2
2
 
3
3
  on:
4
- member:
5
4
  push:
6
5
  branches:
7
6
  - master
7
+ pull_request:
8
+ branches:
9
+ - master
8
10
  schedule:
9
11
  - cron: "0 9 * * *"
10
12
 
11
13
  jobs:
12
14
  test:
13
-
15
+ if: |
16
+ github.actor == 'picatz' ||
17
+ github.actor == 'dependabot[bot]' ||
18
+ github.actor == 'dependabot-preview[bot]'
14
19
  runs-on: ubuntu-latest
15
-
16
20
  steps:
17
21
  - uses: actions/checkout@v1
18
22
  - name: Set up Ruby 2.7
data/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/shodanz.svg)](https://badge.fury.io/rb/shodanz)
4
4
  [![Yard Docs](http://img.shields.io/badge/shodanz-docs-blue.svg)](https://www.rubydoc.info/gems/shodanz/)
5
+ ![CI](https://github.com/picatz/shodanz/workflows/CI/badge.svg)
5
6
 
6
7
  A modern, async Ruby [gem](https://rubygems.org/) for [Shodan](https://www.shodan.io/), the world's first search engine for Internet-connected devices.
7
8
 
@@ -289,10 +290,10 @@ The Exploits API provides access to several exploit/ vulnerability data sources.
289
290
  Search across a variety of data sources for exploits and use facets to get summary information.
290
291
 
291
292
  ```ruby
292
- client.search("python") # Search for Snek vulns.
293
- client.search(post: 22) # Port number for the affected service if the exploit is remote.
294
- client.search(type: "shellcode") # A category of exploit to search for.
295
- client.search(osvdb: "100007") # Open Source Vulnerability Database ID for the exploit.
293
+ client.exploits_api.search("python") # Search for python vulns.
294
+ client.exploits_api.search(port: 22) # Port number for the affected service if the exploit is remote.
295
+ client.exploits_api.search(type: "shellcode") # A category of exploit to search for.
296
+ client.exploits_api.search(osvdb: "100007") # Open Source Vulnerability Database ID for the exploit.
296
297
  ```
297
298
 
298
299
  #### Count
@@ -300,10 +301,10 @@ client.search(osvdb: "100007") # Open Source Vulnerability Database ID for
300
301
  This method behaves identical to the Exploits API `search` method with the difference that it doesn't return any results.
301
302
 
302
303
  ```ruby
303
- client.count("python") # Count Snek vulns.
304
- client.count(port: 22) # Port number for the affected service if the exploit is remote.
305
- client.count(type: "shellcode") # A category of exploit to search for.
306
- client.count(osvdb: "100007") # Open Source Vulnerability Database ID for the exploit.
304
+ client.exploits_api.count("python") # Count python vulns.
305
+ client.exploits_api.count(port: 22) # Port number for the affected service if the exploit is remote.
306
+ client.exploits_api.count(type: "shellcode") # A category of exploit to search for.
307
+ client.exploits_api.count(osvdb: "100007") # Open Source Vulnerability Database ID for the exploit.
307
308
  ```
308
309
 
309
310
  ## License
@@ -19,13 +19,13 @@ module Shodanz
19
19
  attr_accessor :key
20
20
 
21
21
  # The path to the REST API endpoint.
22
- URL = 'https://exploits.shodan.io/api/'
22
+ URL = 'https://exploits.shodan.io/'
23
23
 
24
24
  # @param key [String] SHODAN API key, defaulted to
25
25
  # the *SHODAN_API_KEY* enviroment variable.
26
26
  def initialize(key: ENV['SHODAN_API_KEY'])
27
27
  @url = URL
28
- @internet = Async::HTTP::Internet.new
28
+ @client = Async::HTTP::Client.new(Async::HTTP::Endpoint.parse(@url))
29
29
  self.key = key
30
30
 
31
31
  warn 'No key has been found or provided!' unless key?
@@ -50,7 +50,7 @@ module Shodanz
50
50
  params = turn_into_query(**params)
51
51
  facets = turn_into_facets(**facets)
52
52
  params[:page] = page
53
- get('search', **params.merge(**facets))
53
+ get('api/search', **params.merge(**facets))
54
54
  end
55
55
 
56
56
  # This method behaves identical to the "/search" method with
@@ -62,7 +62,7 @@ module Shodanz
62
62
  params = turn_into_query(**params)
63
63
  facets = turn_into_facets(**facets)
64
64
  params[:page] = page
65
- get('count', **params.merge(**facets))
65
+ get('api/count', **params.merge(**facets))
66
66
  end
67
67
  end
68
68
  end
@@ -22,7 +22,7 @@ module Shodanz
22
22
  # @param key [String] SHODAN API key, defaulted to the *SHODAN_API_KEY* enviroment variable.
23
23
  def initialize(key: ENV['SHODAN_API_KEY'])
24
24
  @url = URL
25
- @internet = Async::HTTP::Internet.new
25
+ @client = Async::HTTP::Client.new(Async::HTTP::Endpoint.parse(@url))
26
26
  self.key = key
27
27
 
28
28
  warn 'No key has been found or provided!' unless key?
@@ -28,7 +28,7 @@ module Shodanz
28
28
  # @param key [String] SHODAN API key, defaulted to the *SHODAN_API_KEY* enviroment variable.
29
29
  def initialize(key: ENV['SHODAN_API_KEY'])
30
30
  @url = URL
31
- @internet = Async::HTTP::Internet.new
31
+ @client = Async::HTTP::Client.new(Async::HTTP::Endpoint.parse(@url))
32
32
  self.key = key
33
33
 
34
34
  warn 'No key has been found or provided!' unless key?
@@ -20,10 +20,10 @@ module Shodanz
20
20
  end
21
21
 
22
22
  # Perform a direct POST HTTP request to the REST API.
23
- def post(path, **params)
24
- return sync_post(path, **params) unless Async::Task.current?
23
+ def post(path, body: nil, **params)
24
+ return sync_post(path, params: params, body: body) unless Async::Task.current?
25
25
 
26
- async_post(path, **params)
26
+ async_post(path, params: params, body: body)
27
27
  end
28
28
 
29
29
  # Perform the main function of consuming the streaming API.
@@ -61,50 +61,78 @@ module Shodanz
61
61
 
62
62
  private
63
63
 
64
- RATELIMIT = 'rate limit reached'
65
- NOINFO = 'no information available'
66
- NOQUERY = 'empty search query'
64
+ RATELIMIT = 'rate limit reached'
65
+ NOINFO = 'no information available'
66
+ NOQUERY = 'empty search query'
67
+ ACCESSDENIED = 'access denied'
68
+ INVALIDKEY = 'invalid API key'
67
69
 
68
70
  def handle_any_json_errors(json)
69
- return json unless json.is_a?(Hash) && json.key?('error')
70
-
71
- raise Shodanz::Errors::RateLimited if json['error'].casecmp(RATELIMIT) >= 0
72
- raise Shodanz::Errors::NoInformation if json['error'].casecmp(NOINFO) >= 0
73
- raise Shodanz::Errors::NoQuery if json['error'].casecmp(NOQUERY) >= 0
74
-
75
- json
71
+ if json.is_a?(Hash) && json.key?('error')
72
+ raise Shodanz::Errors::RateLimited if json['error'].casecmp(RATELIMIT) >= 0
73
+ raise Shodanz::Errors::NoInformation if json['error'].casecmp(NOINFO) >= 0
74
+ raise Shodanz::Errors::NoQuery if json['error'].casecmp(NOQUERY) >= 0
75
+ raise Shodanz::Errors::AccessDenied if json['error'].casecmp(ACCESSDENIED) >= 0
76
+ raise Shodanz::Errors::InvalidKey if json['error'].casecmp(INVALIDKEY) >= 0
77
+ end
78
+ return json
76
79
  end
77
80
 
78
81
  def getter(path, **params)
79
82
  # param keys should all be strings
80
83
  params = params.transform_keys(&:to_s)
81
84
  # build up url string based on special params
82
- url = "#{@url}#{path}?key=#{@key}"
85
+ url = "/#{path}?key=#{@key}"
83
86
  # special params
84
87
  params.each do |param,value|
85
- url += "&#{param}=#{value}" unless value.is_a?(String) && value.empty?
88
+ next if value.is_a?(String) && value.empty?
89
+ value = URI.encode_www_form_component("#{value}")
90
+ url += "&#{param}=#{value}"
86
91
  end
87
- resp = @internet.get(url)
88
92
 
89
- # parse all lines in the response body as JSON
90
- json = JSON.parse(resp.body.join)
93
+ resp = @client.get(url)
91
94
 
92
- handle_any_json_errors(json)
95
+ if resp.success?
96
+ # parse all lines in the response body as JSON
97
+ json = JSON.parse(resp.body.join)
98
+
99
+ handle_any_json_errors(json)
100
+
101
+ return json
102
+ else
103
+ raise "Got response status #{resp.status}"
104
+ end
93
105
  ensure
106
+ @client.pool.close
94
107
  resp&.close
95
108
  end
96
109
 
97
- def poster(path, **params)
110
+ def poster(path, one_shot: false, params: nil, body: nil)
98
111
  # param keys should all be strings
99
112
  params = params.transform_keys(&:to_s)
113
+ # and the key param is constant
114
+ params["key"] = @key
115
+ # encode as a URL string
116
+ params = URI.encode_www_form(params)
117
+ # optional JSON body string
118
+ json_body = body.nil? ? nil : JSON.dump(body)
119
+ # build URL path
120
+ path = "/#{path}?#{params}"
121
+
100
122
  # make POST request to server
101
- resp = @internet.post("#{@url}#{path}?key=#{@key}", params)
123
+ resp = @client.post(path, nil, json_body)
124
+
125
+ if resp.success?
126
+ json = JSON.parse(resp.body.join)
102
127
 
103
- # parse all lines in the response body as JSON
104
- json = JSON.parse(resp.body.join)
128
+ handle_any_json_errors(json)
105
129
 
106
- handle_any_json_errors(json)
130
+ return json
131
+ else
132
+ raise "Got response status #{resp.status}"
133
+ end
107
134
  ensure
135
+ @client.pool.close
108
136
  resp&.close
109
137
  end
110
138
 
@@ -116,7 +144,7 @@ module Shodanz
116
144
  counter = 0
117
145
  end
118
146
  # make GET request to server
119
- resp = @internet.get("#{@url}#{path}?key=#{@key}", params)
147
+ resp = @client.get("/#{path}?key=#{@key}", params)
120
148
  # read body line-by-line
121
149
  until resp.body.nil? || resp.body.empty?
122
150
  resp.body.read.each_line do |line|
@@ -145,15 +173,15 @@ module Shodanz
145
173
  end.wait
146
174
  end
147
175
 
148
- def async_post(path, **params)
176
+ def async_post(path, params: nil, body: nil)
149
177
  Async::Task.current.async do
150
- poster(path, **params)
178
+ poster(path, params: params, body: body)
151
179
  end
152
180
  end
153
181
 
154
- def sync_post(path, **params)
182
+ def sync_post(path, params: nil, body: nil)
155
183
  Async do
156
- poster(path, **params)
184
+ poster(path, params: params, body: body)
157
185
  end.wait
158
186
  end
159
187
 
@@ -15,7 +15,7 @@ module Shodanz
15
15
  end
16
16
 
17
17
  class NoAPIKey < StandardError
18
- def initialize(msg = 'No API key has been found or provided! ( setup your SHODAN_API_KEY environment varialbe )')
18
+ def initialize(msg = 'No API key has been found or provided! ( setup your SHODAN_API_KEY environment variable )')
19
19
  super
20
20
  end
21
21
  end
@@ -25,5 +25,17 @@ module Shodanz
25
25
  super
26
26
  end
27
27
  end
28
+
29
+ class AccessDenied < StandardError
30
+ def initialize(msg = 'Shodan subscription doesn\'t support action, check API permissions!')
31
+ super
32
+ end
33
+ end
34
+
35
+ class InvalidKey < StandardError
36
+ def initialize(msg = 'Invalid API key used, or none given!')
37
+ super
38
+ end
39
+ end
28
40
  end
29
41
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Shodanz
4
- VERSION = '2.0.1'
4
+ VERSION = '2.0.5'
5
5
  end
data/shodanz.gemspec CHANGED
@@ -20,13 +20,13 @@ Gem::Specification.new do |spec|
20
20
  end
21
21
  spec.require_paths = ['lib']
22
22
 
23
- spec.add_dependency 'async-http', '>= 0.38.1', '< 0.51.0'
24
- spec.add_dependency 'async', '>= 1.17.1', '< 1.25.0'
23
+ spec.add_dependency 'async-http', '>= 0.38.1', '< 0.55.0'
24
+ spec.add_dependency 'async', '>= 1.17.1', '< 1.31.0'
25
25
 
26
- spec.add_development_dependency 'async-rspec', '~> 1.14.0'
27
- spec.add_development_dependency 'bundler', '~> 2.1.2'
28
- spec.add_development_dependency 'pry', '~> 0.12.2'
26
+ spec.add_development_dependency 'async-rspec', '~> 1.16.1'
27
+ spec.add_development_dependency 'bundler', '~> 2.2.0'
28
+ spec.add_development_dependency 'pry', '~> 0.14.1'
29
29
  spec.add_development_dependency 'rake', '~> 13.0.0'
30
30
  spec.add_development_dependency 'rb-readline', '~> 0.5.5'
31
- spec.add_development_dependency 'rspec', '~> 3.9.0'
31
+ spec.add_development_dependency 'rspec', '~> 3.10.0'
32
32
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shodanz
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kent 'picatz' Gruber
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-28 00:00:00.000000000 Z
11
+ date: 2021-09-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async-http
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: 0.38.1
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: 0.51.0
22
+ version: 0.55.0
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,7 +29,7 @@ dependencies:
29
29
  version: 0.38.1
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: 0.51.0
32
+ version: 0.55.0
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: async
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -39,7 +39,7 @@ dependencies:
39
39
  version: 1.17.1
40
40
  - - "<"
41
41
  - !ruby/object:Gem::Version
42
- version: 1.25.0
42
+ version: 1.31.0
43
43
  type: :runtime
44
44
  prerelease: false
45
45
  version_requirements: !ruby/object:Gem::Requirement
@@ -49,49 +49,49 @@ dependencies:
49
49
  version: 1.17.1
50
50
  - - "<"
51
51
  - !ruby/object:Gem::Version
52
- version: 1.25.0
52
+ version: 1.31.0
53
53
  - !ruby/object:Gem::Dependency
54
54
  name: async-rspec
55
55
  requirement: !ruby/object:Gem::Requirement
56
56
  requirements:
57
57
  - - "~>"
58
58
  - !ruby/object:Gem::Version
59
- version: 1.14.0
59
+ version: 1.16.1
60
60
  type: :development
61
61
  prerelease: false
62
62
  version_requirements: !ruby/object:Gem::Requirement
63
63
  requirements:
64
64
  - - "~>"
65
65
  - !ruby/object:Gem::Version
66
- version: 1.14.0
66
+ version: 1.16.1
67
67
  - !ruby/object:Gem::Dependency
68
68
  name: bundler
69
69
  requirement: !ruby/object:Gem::Requirement
70
70
  requirements:
71
71
  - - "~>"
72
72
  - !ruby/object:Gem::Version
73
- version: 2.1.2
73
+ version: 2.2.0
74
74
  type: :development
75
75
  prerelease: false
76
76
  version_requirements: !ruby/object:Gem::Requirement
77
77
  requirements:
78
78
  - - "~>"
79
79
  - !ruby/object:Gem::Version
80
- version: 2.1.2
80
+ version: 2.2.0
81
81
  - !ruby/object:Gem::Dependency
82
82
  name: pry
83
83
  requirement: !ruby/object:Gem::Requirement
84
84
  requirements:
85
85
  - - "~>"
86
86
  - !ruby/object:Gem::Version
87
- version: 0.12.2
87
+ version: 0.14.1
88
88
  type: :development
89
89
  prerelease: false
90
90
  version_requirements: !ruby/object:Gem::Requirement
91
91
  requirements:
92
92
  - - "~>"
93
93
  - !ruby/object:Gem::Version
94
- version: 0.12.2
94
+ version: 0.14.1
95
95
  - !ruby/object:Gem::Dependency
96
96
  name: rake
97
97
  requirement: !ruby/object:Gem::Requirement
@@ -126,14 +126,14 @@ dependencies:
126
126
  requirements:
127
127
  - - "~>"
128
128
  - !ruby/object:Gem::Version
129
- version: 3.9.0
129
+ version: 3.10.0
130
130
  type: :development
131
131
  prerelease: false
132
132
  version_requirements: !ruby/object:Gem::Requirement
133
133
  requirements:
134
134
  - - "~>"
135
135
  - !ruby/object:Gem::Version
136
- version: 3.9.0
136
+ version: 3.10.0
137
137
  description: Featuring full support for the REST, Streaming and Exploits API
138
138
  email:
139
139
  - kgruber1@emich.edu
@@ -141,6 +141,7 @@ executables: []
141
141
  extensions: []
142
142
  extra_rdoc_files: []
143
143
  files:
144
+ - ".github/dependabot.yml"
144
145
  - ".github/workflows/ci.yml"
145
146
  - ".gitignore"
146
147
  - ".rspec"
@@ -171,7 +172,7 @@ homepage: https://github.com/picatz/shodanz
171
172
  licenses:
172
173
  - MIT
173
174
  metadata: {}
174
- post_install_message:
175
+ post_install_message:
175
176
  rdoc_options: []
176
177
  require_paths:
177
178
  - lib
@@ -186,8 +187,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
186
187
  - !ruby/object:Gem::Version
187
188
  version: '0'
188
189
  requirements: []
189
- rubygems_version: 3.1.2
190
- signing_key:
190
+ rubygems_version: 3.0.6
191
+ signing_key:
191
192
  specification_version: 4
192
193
  summary: A modern, async Ruby gem for Shodan, the world's first search engine for
193
194
  Internet-connected devices.