shodanz 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +2 -3
- data/examples/debug.rb +5 -3
- data/examples/top_10_countries_running_apache_chart.rb +28 -0
- data/lib/shodanz.rb +10 -5
- data/lib/shodanz/api.rb +16 -14
- data/lib/shodanz/apis/exploits.rb +25 -22
- data/lib/shodanz/apis/rest.rb +30 -21
- data/lib/shodanz/client.rb +13 -13
- data/lib/shodanz/version.rb +1 -1
- data/shodanz.gemspec +5 -5
- metadata +16 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e5da697873b94e412d3732b7a6ed529f83b9aa70bc2e14ae4b9a3ac8738659d4
|
4
|
+
data.tar.gz: f6636ec96aa077123f9e312e14259e1ddfadf6709225bc7c9b3b64b06c0ef611
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4ca0447aa1e55d340da2959730edfb982565771adfe80bdc3a2206f4215669bf998c7cfc4c981c77635a3a58abb830f30b96ce29db46d7f72654166982d46f4e
|
7
|
+
data.tar.gz: f814bf859bbd66b80e85e3c9be339c4e68dde1717f389f49940910acfd648bd26178fd140d19d83935b632097d3525ff5c4688d860d8a30468b7d324e0754214
|
data/README.md
CHANGED
@@ -54,9 +54,8 @@ client.rest_api.host_count("apache")
|
|
54
54
|
client.rest_api.host_count("apache", country: "US")
|
55
55
|
client.rest_api.host_count("apache", country: "US", state: "MI")
|
56
56
|
client.rest_api.host_count("apache", country: "US", state: "MI", city: "Detroit")
|
57
|
-
client.rest_api.host_count("
|
58
|
-
client.rest_api.host_count("
|
59
|
-
client.rest_api.host_count("apache". facets: { country: 5 })
|
57
|
+
client.rest_api.host_count("nginx", facets: { country: 5 })
|
58
|
+
client.rest_api.host_count("apache", facets: { country: 5 })
|
60
59
|
```
|
61
60
|
|
62
61
|
#### Scan Targets
|
data/examples/debug.rb
CHANGED
@@ -2,8 +2,10 @@ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
|
2
2
|
require 'shodanz'
|
3
3
|
require 'pry'
|
4
4
|
|
5
|
-
rest_api = Shodanz.api.rest.new
|
6
|
-
streaming_api = Shodanz.api.streaming.new
|
7
|
-
exploits_api = Shodanz.api.exploits.new
|
5
|
+
# rest_api = Shodanz.api.rest.new
|
6
|
+
# streaming_api = Shodanz.api.streaming.new
|
7
|
+
# exploits_api = Shodanz.api.exploits.new
|
8
|
+
|
9
|
+
client = Shodanz.client.new
|
8
10
|
|
9
11
|
binding.pry
|
@@ -0,0 +1,28 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
2
|
+
require "shodanz"
|
3
|
+
require "chart_js"
|
4
|
+
|
5
|
+
module Top10
|
6
|
+
@rest_api = Shodanz.api.rest.new
|
7
|
+
def self.check(product)
|
8
|
+
begin
|
9
|
+
@rest_api.host_count(product: product, facets: { country: 10 })["facets"]["country"].collect { |x| x.values }.to_h.invert
|
10
|
+
rescue
|
11
|
+
puts "Unable to succesffully check the Shodan API."
|
12
|
+
exit 1
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
results = Top10.check("apache")
|
18
|
+
|
19
|
+
ChartJS.bar do
|
20
|
+
file "top_10_apache.html"
|
21
|
+
data do
|
22
|
+
labels results.keys
|
23
|
+
dataset "Countries" do
|
24
|
+
color :random
|
25
|
+
data results.values
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/shodanz.rb
CHANGED
@@ -1,13 +1,18 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
1
|
+
require 'unirest'
|
2
|
+
require 'oj'
|
3
|
+
require 'shodanz/version'
|
4
|
+
require 'shodanz/api'
|
5
|
+
require 'shodanz/client'
|
6
6
|
|
7
|
+
# Shodanz is a modern Ruby gem for Shodan, the world's
|
8
|
+
# first search engine for Internet-connected devices.
|
7
9
|
module Shodanz
|
10
|
+
# Shortcut for {Shodanz::API}
|
8
11
|
def self.api
|
9
12
|
API
|
10
13
|
end
|
14
|
+
|
15
|
+
# Shortcut for {Shodanz::Client}
|
11
16
|
def self.client
|
12
17
|
Client
|
13
18
|
end
|
data/lib/shodanz/api.rb
CHANGED
@@ -1,29 +1,31 @@
|
|
1
|
-
require_relative
|
2
|
-
require_relative
|
3
|
-
require_relative
|
1
|
+
require_relative 'apis/rest.rb'
|
2
|
+
require_relative 'apis/streaming.rb'
|
3
|
+
require_relative 'apis/exploits.rb'
|
4
4
|
|
5
5
|
module Shodanz
|
6
|
-
# There are 2 APIs for accessing Shodan: the REST API
|
7
|
-
# and the Streaming API. The REST API provides methods
|
8
|
-
# to search Shodan, look up hosts, get summary information
|
9
|
-
# on queries and a variety of utility methods to make
|
10
|
-
# developing easier. The Streaming API provides a raw,
|
11
|
-
# real-time feed of the data that Shodan is currently
|
12
|
-
# collecting. There are several feeds that can be subscribed
|
13
|
-
# to, but the data can't be searched or otherwise interacted
|
14
|
-
# with; it's a live feed of data meant for large-scale
|
6
|
+
# There are 2 APIs for accessing Shodan: the REST API
|
7
|
+
# and the Streaming API. The REST API provides methods
|
8
|
+
# to search Shodan, look up hosts, get summary information
|
9
|
+
# on queries and a variety of utility methods to make
|
10
|
+
# developing easier. The Streaming API provides a raw,
|
11
|
+
# real-time feed of the data that Shodan is currently
|
12
|
+
# collecting. There are several feeds that can be subscribed
|
13
|
+
# to, but the data can't be searched or otherwise interacted
|
14
|
+
# with; it's a live feed of data meant for large-scale
|
15
15
|
# consumption of Shodan's information.
|
16
|
+
#
|
17
|
+
# @author Kent 'picat' Gruber
|
16
18
|
module API
|
17
19
|
# REST API class.
|
18
20
|
def self.rest
|
19
21
|
REST
|
20
22
|
end
|
21
|
-
|
23
|
+
|
22
24
|
# Streaming API class.
|
23
25
|
def self.streaming
|
24
26
|
Streaming
|
25
27
|
end
|
26
|
-
|
28
|
+
|
27
29
|
# Exploits API class.
|
28
30
|
def self.exploits
|
29
31
|
Exploits
|
@@ -1,8 +1,7 @@
|
|
1
1
|
module Shodanz
|
2
|
-
|
3
2
|
module API
|
4
|
-
# The Exploits API provides access to several exploit
|
5
|
-
# and vulnerability data sources. At the moment, it
|
3
|
+
# The Exploits API provides access to several exploit
|
4
|
+
# and vulnerability data sources. At the moment, it
|
6
5
|
# searches across the following:
|
7
6
|
# - Exploit DB
|
8
7
|
# - Metasploit
|
@@ -10,76 +9,80 @@ module Shodanz
|
|
10
9
|
#
|
11
10
|
# @author Kent 'picat' Gruber
|
12
11
|
class Exploits
|
12
|
+
# @return [String]
|
13
13
|
attr_accessor :key
|
14
|
-
|
14
|
+
|
15
15
|
# The path to the REST API endpoint.
|
16
|
-
URL =
|
16
|
+
URL = 'https://exploits.shodan.io/api/'.freeze
|
17
17
|
|
18
|
-
# @param key [String] SHODAN API key, defaulted to
|
18
|
+
# @param key [String] SHODAN API key, defaulted to
|
19
|
+
# the *SHODAN_API_KEY* enviroment variable.
|
19
20
|
def initialize(key: ENV['SHODAN_API_KEY'])
|
20
21
|
self.key = key
|
21
|
-
warn
|
22
|
+
warn 'No key has been found or provided!' unless key?
|
22
23
|
end
|
23
24
|
|
24
25
|
# Check if there's an API key.
|
26
|
+
# @return [String]
|
25
27
|
def key?
|
26
|
-
return true if @key
|
28
|
+
return true if @key
|
29
|
+
false
|
27
30
|
end
|
28
31
|
|
29
|
-
# Search across a variety of data sources for exploits and
|
32
|
+
# Search across a variety of data sources for exploits and
|
30
33
|
# use facets to get summary information.
|
31
34
|
# == Example
|
32
35
|
# api.search("SQL", port: 443)
|
33
36
|
# api.search(port: 22)
|
34
37
|
# api.search(type: "dos")
|
35
|
-
def search(query =
|
38
|
+
def search(query = '', page: 1, **params)
|
36
39
|
params[:query] = query
|
37
40
|
params = turn_into_query(params)
|
38
41
|
facets = turn_into_facets(facets)
|
39
42
|
params[:page] = page
|
40
|
-
get(
|
43
|
+
get('search', params.merge(facets))
|
41
44
|
end
|
42
45
|
|
43
|
-
# This method behaves identical to the "/search" method with
|
46
|
+
# This method behaves identical to the "/search" method with
|
44
47
|
# the difference that it doesn't return any results.
|
45
48
|
# == Example
|
46
49
|
# api.count(type: "dos")
|
47
|
-
def count(query =
|
50
|
+
def count(query = '', page: 1, **params)
|
48
51
|
params[:query] = query
|
49
52
|
params = turn_into_query(params)
|
50
53
|
facets = turn_into_facets(params)
|
51
54
|
params[:page] = page
|
52
|
-
get(
|
55
|
+
get('count', params.merge(facets))
|
53
56
|
end
|
54
57
|
|
55
58
|
# Perform a direct GET HTTP request to the REST API.
|
56
59
|
def get(path, **params)
|
57
60
|
resp = Unirest.get "#{URL}#{path}?key=#{@key}", parameters: params
|
58
|
-
|
61
|
+
if resp.code != 200 && resp.body.key?('error')
|
62
|
+
raise resp.body['error']
|
63
|
+
end
|
59
64
|
resp.body
|
60
65
|
end
|
61
66
|
|
62
67
|
private
|
63
68
|
|
64
69
|
def turn_into_query(params)
|
65
|
-
filters = params.reject { |key,
|
70
|
+
filters = params.reject { |key, _| key == :query }
|
66
71
|
filters.each do |key, value|
|
67
72
|
params[:query] << " #{key}:#{value}"
|
68
73
|
end
|
69
|
-
params.select { |key,
|
74
|
+
params.select { |key, _| key == :query }
|
70
75
|
end
|
71
76
|
|
72
77
|
def turn_into_facets(facets)
|
73
|
-
filters = facets.reject { |key,
|
78
|
+
filters = facets.reject { |key, _| key == :facets }
|
74
79
|
facets[:facets] = []
|
75
80
|
filters.each do |key, value|
|
76
81
|
facets[:facets] << "#{key}:#{value}"
|
77
82
|
end
|
78
|
-
facets[:facets] = facets[:facets].join(
|
79
|
-
facets.select { |key,
|
83
|
+
facets[:facets] = facets[:facets].join(',')
|
84
|
+
facets.select { |key, _| key == :facets }
|
80
85
|
end
|
81
|
-
|
82
86
|
end
|
83
|
-
|
84
87
|
end
|
85
88
|
end
|
data/lib/shodanz/apis/rest.rb
CHANGED
@@ -5,6 +5,7 @@ module Shodanz
|
|
5
5
|
# hosts, get summary information on queries and a variety
|
6
6
|
# of other utilities. This requires you to have an API key
|
7
7
|
# which you can get from Shodan.
|
8
|
+
#
|
8
9
|
# @author Kent 'picat' Gruber
|
9
10
|
class REST
|
10
11
|
attr_accessor :key
|
@@ -112,7 +113,7 @@ module Shodanz
|
|
112
113
|
def crawl_for(**params)
|
113
114
|
params[:query] = ""
|
114
115
|
params = turn_into_query(params)
|
115
|
-
post(
|
116
|
+
post('shodan/scan/internet', params)
|
116
117
|
end
|
117
118
|
|
118
119
|
# Check the progress of a previously submitted scan request.
|
@@ -122,49 +123,52 @@ module Shodanz
|
|
122
123
|
|
123
124
|
# Use this method to obtain a list of search queries that users have saved in Shodan.
|
124
125
|
def community_queries(**params)
|
125
|
-
get(
|
126
|
+
get('shodan/query', params)
|
126
127
|
end
|
127
128
|
|
128
129
|
# Use this method to search the directory of search queries that users have saved in Shodan.
|
129
130
|
def search_for_community_query(query, **params)
|
130
131
|
params[:query] = query
|
131
132
|
params = turn_into_query(params)
|
132
|
-
get(
|
133
|
+
get('shodan/query/search', params)
|
133
134
|
end
|
134
135
|
|
135
136
|
# Use this method to obtain a list of popular tags for the saved search queries in Shodan.
|
136
137
|
def popular_query_tags(size = 10)
|
137
138
|
params = {}
|
138
139
|
params[:size] = size
|
139
|
-
get(
|
140
|
+
get('shodan/query/tags', params)
|
140
141
|
end
|
141
142
|
|
142
143
|
# Returns information about the Shodan account linked to this API key.
|
143
144
|
def profile
|
144
|
-
get(
|
145
|
+
get('account/profile')
|
145
146
|
end
|
146
147
|
|
147
148
|
# Look up the IP address for the provided list of hostnames.
|
148
149
|
def resolve(*hostnames)
|
149
|
-
get(
|
150
|
+
get('dns/resolve', hostnames: hostnames.join(","))
|
150
151
|
end
|
151
152
|
|
152
|
-
# Look up the hostnames that have been defined for the
|
153
|
+
# Look up the hostnames that have been defined for the
|
154
|
+
# given list of IP addresses.
|
153
155
|
def reverse_lookup(*ips)
|
154
|
-
get(
|
156
|
+
get('dns/reverse', ips: ips.join(","))
|
155
157
|
end
|
156
158
|
|
157
|
-
# Shows the HTTP headers that your client sends when
|
159
|
+
# Shows the HTTP headers that your client sends when
|
160
|
+
# connecting to a webserver.
|
158
161
|
def http_headers
|
159
|
-
get(
|
162
|
+
get('tools/httpheaders')
|
160
163
|
end
|
161
164
|
|
162
165
|
# Get your current IP address as seen from the Internet.
|
163
166
|
def my_ip
|
164
|
-
get(
|
167
|
+
get('tools/myip')
|
165
168
|
end
|
166
169
|
|
167
|
-
# Calculates a honeypot probability score ranging
|
170
|
+
# Calculates a honeypot probability score ranging
|
171
|
+
# from 0 (not a honeypot) to 1.0 (is a honeypot).
|
168
172
|
def honeypot_score(ip)
|
169
173
|
get("labs/honeyscore/#{ip}")
|
170
174
|
end
|
@@ -177,38 +181,43 @@ module Shodanz
|
|
177
181
|
# Perform a direct GET HTTP request to the REST API.
|
178
182
|
def get(path, **params)
|
179
183
|
resp = Unirest.get "#{URL}#{path}?key=#{@key}", parameters: params
|
180
|
-
|
184
|
+
if resp.code != 200
|
185
|
+
binding.pry
|
186
|
+
raise resp.body['error'] if resp.body.key?('error')
|
187
|
+
raise resp
|
188
|
+
end
|
181
189
|
resp.body
|
182
190
|
end
|
183
191
|
|
184
192
|
# Perform a direct POST HTTP request to the REST API.
|
185
193
|
def post(path, **params)
|
186
194
|
resp = Unirest.post "#{URL}#{path}?key=#{@key}", parameters: params
|
187
|
-
|
195
|
+
if resp.code != 200
|
196
|
+
raise resp.body['error'] if resp.body.key?('error')
|
197
|
+
raise resp
|
198
|
+
end
|
188
199
|
resp.body
|
189
200
|
end
|
190
201
|
|
191
202
|
private
|
192
203
|
|
193
204
|
def turn_into_query(params)
|
194
|
-
filters = params.reject { |key,
|
205
|
+
filters = params.reject { |key, _| key == :query }
|
195
206
|
filters.each do |key, value|
|
196
207
|
params[:query] << " #{key}:#{value}"
|
197
208
|
end
|
198
|
-
params.select { |key,
|
209
|
+
params.select { |key, _| key == :query }
|
199
210
|
end
|
200
211
|
|
201
212
|
def turn_into_facets(facets)
|
202
|
-
filters = facets.reject { |key,
|
213
|
+
filters = facets.reject { |key, _| key == :facets }
|
203
214
|
facets[:facets] = []
|
204
215
|
filters.each do |key, value|
|
205
216
|
facets[:facets] << "#{key}:#{value}"
|
206
217
|
end
|
207
|
-
facets[:facets] = facets[:facets].join(
|
208
|
-
facets.select { |key,
|
218
|
+
facets[:facets] = facets[:facets].join(',')
|
219
|
+
facets.select { |key, _| key == :facets }
|
209
220
|
end
|
210
|
-
|
211
221
|
end
|
212
|
-
|
213
222
|
end
|
214
223
|
end
|
data/lib/shodanz/client.rb
CHANGED
@@ -1,22 +1,22 @@
|
|
1
1
|
module Shodanz
|
2
|
+
# General client container class for all three
|
3
|
+
# of Shodan's available API endpoints in a
|
4
|
+
# convient place to use.
|
5
|
+
#
|
6
|
+
# @author Kent 'picat' Gruber
|
2
7
|
class Client
|
8
|
+
# @return [Shodanz::API::REST]
|
9
|
+
attr_reader :rest_api
|
10
|
+
# @return [Shodanz::API::Streaming]
|
11
|
+
attr_reader :streaming_api
|
12
|
+
# @return [Shodanz::API::Exploits]
|
13
|
+
attr_reader :exploits_api
|
14
|
+
|
3
15
|
# Create a new client to connect to any of the APIs.
|
4
16
|
def initialize
|
5
17
|
@rest_api = Shodanz.api.rest.new
|
6
18
|
@streaming_api = Shodanz.api.streaming.new
|
7
|
-
@exploits_api = Shodanz.api.exploits.new
|
8
|
-
end
|
9
|
-
|
10
|
-
def rest_api
|
11
|
-
@rest_api
|
12
|
-
end
|
13
|
-
|
14
|
-
def streaming_api
|
15
|
-
@streaming_api
|
16
|
-
end
|
17
|
-
|
18
|
-
def exploits_api
|
19
|
-
@exploits_api
|
19
|
+
@exploits_api = Shodanz.api.exploits.new
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
data/lib/shodanz/version.rb
CHANGED
data/shodanz.gemspec
CHANGED
@@ -20,10 +20,10 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
22
|
spec.add_dependency "unirest", "1.1.2"
|
23
|
-
spec.add_dependency "json",
|
24
|
-
spec.add_dependency "oj", "3.
|
23
|
+
spec.add_dependency "json" # specifying version breaks things, what?
|
24
|
+
spec.add_dependency "oj", "3.5.0"
|
25
25
|
|
26
|
-
spec.add_development_dependency "bundler", "~> 1.
|
27
|
-
spec.add_development_dependency "rake", "~>
|
28
|
-
spec.add_development_dependency "rspec", "~> 3.0"
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.16.1"
|
27
|
+
spec.add_development_dependency "rake", "~> 12.3.1"
|
28
|
+
spec.add_development_dependency "rspec", "~> 3.7.0"
|
29
29
|
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: 1.0.
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kent 'picatz' Gruber
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-03-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: unirest
|
@@ -28,72 +28,72 @@ dependencies:
|
|
28
28
|
name: json
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: '0'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: oj
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - '='
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 3.
|
47
|
+
version: 3.5.0
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - '='
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 3.
|
54
|
+
version: 3.5.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: bundler
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: 1.16.1
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
68
|
+
version: 1.16.1
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rake
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
75
|
+
version: 12.3.1
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
82
|
+
version: 12.3.1
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: rspec
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
89
|
+
version: 3.7.0
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
96
|
+
version: 3.7.0
|
97
97
|
description: Featuring full support for the REST, Streaming and Exploits API
|
98
98
|
email:
|
99
99
|
- kgruber1@emich.edu
|
@@ -111,6 +111,7 @@ files:
|
|
111
111
|
- Rakefile
|
112
112
|
- examples/debug.rb
|
113
113
|
- examples/top_10_countries_running.rb
|
114
|
+
- examples/top_10_countries_running_apache_chart.rb
|
114
115
|
- lib/shodanz.rb
|
115
116
|
- lib/shodanz/api.rb
|
116
117
|
- lib/shodanz/apis/exploits.rb
|
@@ -139,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
139
140
|
version: '0'
|
140
141
|
requirements: []
|
141
142
|
rubyforge_project:
|
142
|
-
rubygems_version: 2.
|
143
|
+
rubygems_version: 2.7.3
|
143
144
|
signing_key:
|
144
145
|
specification_version: 4
|
145
146
|
summary: A modern Ruby gem for Shodan, the world's first search engine for Internet-connected
|