shodanz 2.0.1 → 2.0.5

Sign up to get free protection for your applications and to get access to all the features.
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.