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 +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
|
[![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
|
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.
|