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 +4 -4
- data/.github/dependabot.yml +15 -0
- data/.github/workflows/ci.yml +7 -3
- data/README.md +9 -8
- data/lib/shodanz/apis/exploits.rb +4 -4
- data/lib/shodanz/apis/rest.rb +1 -1
- data/lib/shodanz/apis/streaming.rb +1 -1
- data/lib/shodanz/apis/utils.rb +57 -29
- data/lib/shodanz/errors.rb +13 -1
- data/lib/shodanz/version.rb +1 -1
- data/shodanz.gemspec +6 -6
- metadata +19 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6e20a19dd003d869e2c49037298314d78161c8b4d64d63057b90e34654a695dc
|
4
|
+
data.tar.gz: a3478a19294e1b3a8bba8d97b9903429ebefa8d0520c300802eb8293fcb8b578
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/.github/workflows/ci.yml
CHANGED
@@ -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
|
[](https://badge.fury.io/rb/shodanz)
|
4
4
|
[](https://www.rubydoc.info/gems/shodanz/)
|
5
|
+

|
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
|
293
|
-
client.search(
|
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
|
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/
|
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
|
-
@
|
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
|
data/lib/shodanz/apis/rest.rb
CHANGED
@@ -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
|
-
@
|
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
|
-
@
|
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?
|
data/lib/shodanz/apis/utils.rb
CHANGED
@@ -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,
|
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,
|
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
|
65
|
-
NOINFO
|
66
|
-
NOQUERY
|
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
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
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 = "
|
85
|
+
url = "/#{path}?key=#{@key}"
|
83
86
|
# special params
|
84
87
|
params.each do |param,value|
|
85
|
-
|
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
|
-
|
90
|
-
json = JSON.parse(resp.body.join)
|
93
|
+
resp = @client.get(url)
|
91
94
|
|
92
|
-
|
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,
|
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 = @
|
123
|
+
resp = @client.post(path, nil, json_body)
|
124
|
+
|
125
|
+
if resp.success?
|
126
|
+
json = JSON.parse(resp.body.join)
|
102
127
|
|
103
|
-
|
104
|
-
json = JSON.parse(resp.body.join)
|
128
|
+
handle_any_json_errors(json)
|
105
129
|
|
106
|
-
|
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 = @
|
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,
|
176
|
+
def async_post(path, params: nil, body: nil)
|
149
177
|
Async::Task.current.async do
|
150
|
-
poster(path,
|
178
|
+
poster(path, params: params, body: body)
|
151
179
|
end
|
152
180
|
end
|
153
181
|
|
154
|
-
def sync_post(path,
|
182
|
+
def sync_post(path, params: nil, body: nil)
|
155
183
|
Async do
|
156
|
-
poster(path,
|
184
|
+
poster(path, params: params, body: body)
|
157
185
|
end.wait
|
158
186
|
end
|
159
187
|
|
data/lib/shodanz/errors.rb
CHANGED
@@ -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
|
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
|
data/lib/shodanz/version.rb
CHANGED
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.
|
24
|
-
spec.add_dependency 'async', '>= 1.17.1', '< 1.
|
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.
|
27
|
-
spec.add_development_dependency 'bundler', '~> 2.
|
28
|
-
spec.add_development_dependency 'pry', '~> 0.
|
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.
|
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.
|
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:
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|