shodanz 2.0.0 → 2.0.1
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/workflows/ci.yml +29 -0
- data/README.md +5 -3
- data/examples/async_honeypot_detector.rb +3 -1
- data/lib/shodanz/apis/exploits.rb +8 -8
- data/lib/shodanz/apis/rest.rb +15 -15
- data/lib/shodanz/apis/streaming.rb +2 -2
- data/lib/shodanz/apis/utils.rb +16 -18
- data/lib/shodanz/version.rb +1 -1
- data/shodanz.gemspec +6 -6
- metadata +34 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 32aa0999f1cb087637e0abe8428bd70592f22a3761da87be09bcf1a2bd8c2d37
|
4
|
+
data.tar.gz: a6d8a053446c3163bb3ee7630c5663510e6568469cd0569e6c6d4ad67d1873a3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d3c5e27187e510f5b811bee421194b269bcc1092bc4b212c7a172c342f1b6a4c932314e5198e89b16509ce6c1345714e232461e4c3b34de1bf2262797a435bdf
|
7
|
+
data.tar.gz: d66f4fc7e68963a54870b789a1b63ada8ffe3174947f6b641dfa8b43547d77ef37b73fc16ff7c4f8715244be8449037dcc5fa2b7028cf50424b43a6004c4297b
|
@@ -0,0 +1,29 @@
|
|
1
|
+
name: CI
|
2
|
+
|
3
|
+
on:
|
4
|
+
member:
|
5
|
+
push:
|
6
|
+
branches:
|
7
|
+
- master
|
8
|
+
schedule:
|
9
|
+
- cron: "0 9 * * *"
|
10
|
+
|
11
|
+
jobs:
|
12
|
+
test:
|
13
|
+
|
14
|
+
runs-on: ubuntu-latest
|
15
|
+
|
16
|
+
steps:
|
17
|
+
- uses: actions/checkout@v1
|
18
|
+
- name: Set up Ruby 2.7
|
19
|
+
uses: actions/setup-ruby@v1
|
20
|
+
with:
|
21
|
+
ruby-version: 2.7.x
|
22
|
+
- name: Rspec
|
23
|
+
env:
|
24
|
+
SHODAN_API_KEY: ${{secrets.SHODAN_API_KEY}}
|
25
|
+
run: |
|
26
|
+
gem install bundler
|
27
|
+
bundle install --jobs 4 --retry 3
|
28
|
+
bundle exec rspec
|
29
|
+
|
data/README.md
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# Shodanz
|
2
2
|
|
3
|
+
[](https://badge.fury.io/rb/shodanz)
|
4
|
+
[](https://www.rubydoc.info/gems/shodanz/)
|
5
|
+
|
3
6
|
A modern, async Ruby [gem](https://rubygems.org/) for [Shodan](https://www.shodan.io/), the world's first search engine for Internet-connected devices.
|
4
7
|
|
5
8
|
## Installation
|
@@ -24,9 +27,8 @@ require 'shodanz'
|
|
24
27
|
|
25
28
|
client = Shodanz.client.new
|
26
29
|
|
27
|
-
#
|
28
|
-
#
|
29
|
-
# honeypot scoring service to find potential honeypots.
|
30
|
+
# Asynchronously stream banner info from shodan and check any
|
31
|
+
# IP addresses against the experimental honeypot scoring service.
|
30
32
|
client.streaming_api.banners do |banner|
|
31
33
|
if ip = banner['ip_str']
|
32
34
|
Async do
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
|
2
4
|
require 'async'
|
3
5
|
require 'shodanz'
|
@@ -12,7 +14,7 @@ client.streaming_api.banners do |banner|
|
|
12
14
|
rescue Shodanz::Errors::RateLimited
|
13
15
|
sleep rand
|
14
16
|
retry
|
15
|
-
rescue # any other errors
|
17
|
+
rescue StandardError # any other errors
|
16
18
|
next
|
17
19
|
end
|
18
20
|
end
|
@@ -45,24 +45,24 @@ module Shodanz
|
|
45
45
|
# api.search("SQL", port: 443)
|
46
46
|
# api.search(port: 22)
|
47
47
|
# api.search(type: "dos")
|
48
|
-
def search(query = '', page: 1, **params)
|
48
|
+
def search(query = '', facets: {}, page: 1, **params)
|
49
49
|
params[:query] = query
|
50
|
-
params = turn_into_query(params)
|
51
|
-
facets = turn_into_facets(facets)
|
50
|
+
params = turn_into_query(**params)
|
51
|
+
facets = turn_into_facets(**facets)
|
52
52
|
params[:page] = page
|
53
|
-
get('search', params.merge(facets))
|
53
|
+
get('search', **params.merge(**facets))
|
54
54
|
end
|
55
55
|
|
56
56
|
# This method behaves identical to the "/search" method with
|
57
57
|
# the difference that it doesn't return any results.
|
58
58
|
# == Example
|
59
59
|
# api.count(type: "dos")
|
60
|
-
def count(query = '', page: 1, **params)
|
60
|
+
def count(query = '', facets: {}, page: 1, **params)
|
61
61
|
params[:query] = query
|
62
|
-
params = turn_into_query(params)
|
63
|
-
facets = turn_into_facets(
|
62
|
+
params = turn_into_query(**params)
|
63
|
+
facets = turn_into_facets(**facets)
|
64
64
|
params[:page] = page
|
65
|
-
get('count', params.merge(facets))
|
65
|
+
get('count', **params.merge(**facets))
|
66
66
|
end
|
67
67
|
end
|
68
68
|
end
|
data/lib/shodanz/apis/rest.rb
CHANGED
@@ -49,7 +49,7 @@ module Shodanz
|
|
49
49
|
# # Only return the list of ports and the general host information, no banners.
|
50
50
|
# rest_api.host("8.8.8.8", minify: true)
|
51
51
|
def host(ip, **params)
|
52
|
-
get("shodan/host/#{ip}", params)
|
52
|
+
get("shodan/host/#{ip}", **params)
|
53
53
|
end
|
54
54
|
|
55
55
|
# This method behaves identical to "/shodan/host/search" with the only
|
@@ -64,9 +64,9 @@ module Shodanz
|
|
64
64
|
# rest_api.host_count("apache", country: "US", state: "MI", city: "Detroit")
|
65
65
|
def host_count(query = '', facets: {}, **params)
|
66
66
|
params[:query] = query
|
67
|
-
params = turn_into_query(params)
|
68
|
-
facets = turn_into_facets(facets)
|
69
|
-
get('shodan/host/count', params.merge(facets))
|
67
|
+
params = turn_into_query(**params)
|
68
|
+
facets = turn_into_facets(**facets)
|
69
|
+
get('shodan/host/count', **params.merge(**facets))
|
70
70
|
end
|
71
71
|
|
72
72
|
# Search Shodan using the same query syntax as the website and use facets
|
@@ -75,19 +75,19 @@ module Shodanz
|
|
75
75
|
# rest_api.host_search("apache", country: "US", facets: { city: "Detroit" }, page: 1, minify: false)
|
76
76
|
def host_search(query = '', facets: {}, page: 1, minify: true, **params)
|
77
77
|
params[:query] = query
|
78
|
-
params = turn_into_query(params)
|
79
|
-
facets = turn_into_facets(facets)
|
78
|
+
params = turn_into_query(**params)
|
79
|
+
facets = turn_into_facets(**facets)
|
80
80
|
params[:page] = page
|
81
81
|
params[:minify] = minify
|
82
|
-
get('shodan/host/search', params.merge(facets))
|
82
|
+
get('shodan/host/search', **params.merge(**facets))
|
83
83
|
end
|
84
84
|
|
85
85
|
# This method lets you determine which filters are being used by
|
86
86
|
# the query string and what parameters were provided to the filters.
|
87
87
|
def host_search_tokens(query = '', **params)
|
88
88
|
params[:query] = query
|
89
|
-
params = turn_into_query(params)
|
90
|
-
get('shodan/host/search/tokens', params)
|
89
|
+
params = turn_into_query(**params)
|
90
|
+
get('shodan/host/search/tokens', **params)
|
91
91
|
end
|
92
92
|
|
93
93
|
# This method returns a list of port numbers that the crawlers are looking for.
|
@@ -122,8 +122,8 @@ module Shodanz
|
|
122
122
|
# rest_api.crawl_for(port: 80, protocol: "http")
|
123
123
|
def crawl_for(**params)
|
124
124
|
params[:query] = ''
|
125
|
-
params = turn_into_query(params)
|
126
|
-
post('shodan/scan/internet', params)
|
125
|
+
params = turn_into_query(**params)
|
126
|
+
post('shodan/scan/internet', **params)
|
127
127
|
end
|
128
128
|
|
129
129
|
# Check the progress of a previously submitted scan request.
|
@@ -133,21 +133,21 @@ module Shodanz
|
|
133
133
|
|
134
134
|
# Use this method to obtain a list of search queries that users have saved in Shodan.
|
135
135
|
def community_queries(**params)
|
136
|
-
get('shodan/query', params)
|
136
|
+
get('shodan/query', **params)
|
137
137
|
end
|
138
138
|
|
139
139
|
# Use this method to search the directory of search queries that users have saved in Shodan.
|
140
140
|
def search_for_community_query(query, **params)
|
141
141
|
params[:query] = query
|
142
|
-
params = turn_into_query(params)
|
143
|
-
get('shodan/query/search', params)
|
142
|
+
params = turn_into_query(**params)
|
143
|
+
get('shodan/query/search', **params)
|
144
144
|
end
|
145
145
|
|
146
146
|
# Use this method to obtain a list of popular tags for the saved search queries in Shodan.
|
147
147
|
def popular_query_tags(size = 10)
|
148
148
|
params = {}
|
149
149
|
params[:size] = size
|
150
|
-
get('shodan/query/tags', params)
|
150
|
+
get('shodan/query/tags', **params)
|
151
151
|
end
|
152
152
|
|
153
153
|
# Returns information about the Shodan account linked to this API key.
|
@@ -54,7 +54,7 @@ module Shodanz
|
|
54
54
|
# puts data
|
55
55
|
# end
|
56
56
|
def banners(**params)
|
57
|
-
slurp_stream('shodan/banners', params) do |data|
|
57
|
+
slurp_stream('shodan/banners', **params) do |data|
|
58
58
|
yield data
|
59
59
|
end
|
60
60
|
end
|
@@ -67,7 +67,7 @@ module Shodanz
|
|
67
67
|
# puts data
|
68
68
|
# end
|
69
69
|
def banners_within_asns(*asns, **params)
|
70
|
-
slurp_stream("shodan/asn/#{asns.join(',')}", params) do |data|
|
70
|
+
slurp_stream("shodan/asn/#{asns.join(',')}", **params) do |data|
|
71
71
|
yield data
|
72
72
|
end
|
73
73
|
end
|
data/lib/shodanz/apis/utils.rb
CHANGED
@@ -14,32 +14,32 @@ module Shodanz
|
|
14
14
|
module Utils
|
15
15
|
# Perform a direct GET HTTP request to the REST API.
|
16
16
|
def get(path, **params)
|
17
|
-
return sync_get(path, params) unless Async::Task.current?
|
17
|
+
return sync_get(path, **params) unless Async::Task.current?
|
18
18
|
|
19
|
-
async_get(path, params)
|
19
|
+
async_get(path, **params)
|
20
20
|
end
|
21
21
|
|
22
22
|
# Perform a direct POST HTTP request to the REST API.
|
23
23
|
def post(path, **params)
|
24
|
-
return sync_post(path, params) unless Async::Task.current?
|
24
|
+
return sync_post(path, **params) unless Async::Task.current?
|
25
25
|
|
26
|
-
async_post(path, params)
|
26
|
+
async_post(path, **params)
|
27
27
|
end
|
28
28
|
|
29
29
|
# Perform the main function of consuming the streaming API.
|
30
30
|
def slurp_stream(path, **params)
|
31
31
|
if Async::Task.current?
|
32
|
-
async_slurp_stream(path, params) do |result|
|
32
|
+
async_slurp_stream(path, **params) do |result|
|
33
33
|
yield result
|
34
34
|
end
|
35
35
|
else
|
36
|
-
sync_slurp_stream(path, params) do |result|
|
36
|
+
sync_slurp_stream(path, **params) do |result|
|
37
37
|
yield result
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
def turn_into_query(params)
|
42
|
+
def turn_into_query(**params)
|
43
43
|
filters = params.reject { |key, _| key == :query }
|
44
44
|
filters.each do |key, value|
|
45
45
|
params[:query] << " #{key}:#{value}"
|
@@ -47,7 +47,7 @@ module Shodanz
|
|
47
47
|
params.select { |key, _| key == :query }
|
48
48
|
end
|
49
49
|
|
50
|
-
def turn_into_facets(facets)
|
50
|
+
def turn_into_facets(**facets)
|
51
51
|
return {} if facets.nil?
|
52
52
|
|
53
53
|
filters = facets.reject { |key, _| key == :facets }
|
@@ -81,10 +81,8 @@ module Shodanz
|
|
81
81
|
# build up url string based on special params
|
82
82
|
url = "#{@url}#{path}?key=#{@key}"
|
83
83
|
# special params
|
84
|
-
|
85
|
-
|
86
|
-
url += "&#{param}=#{value}"
|
87
|
-
end
|
84
|
+
params.each do |param,value|
|
85
|
+
url += "&#{param}=#{value}" unless value.is_a?(String) && value.empty?
|
88
86
|
end
|
89
87
|
resp = @internet.get(url)
|
90
88
|
|
@@ -137,37 +135,37 @@ module Shodanz
|
|
137
135
|
|
138
136
|
def async_get(path, **params)
|
139
137
|
Async::Task.current.async do
|
140
|
-
getter(path, params)
|
138
|
+
getter(path, **params)
|
141
139
|
end
|
142
140
|
end
|
143
141
|
|
144
142
|
def sync_get(path, **params)
|
145
143
|
Async do
|
146
|
-
getter(path, params)
|
144
|
+
getter(path, **params)
|
147
145
|
end.wait
|
148
146
|
end
|
149
147
|
|
150
148
|
def async_post(path, **params)
|
151
149
|
Async::Task.current.async do
|
152
|
-
poster(path, params)
|
150
|
+
poster(path, **params)
|
153
151
|
end
|
154
152
|
end
|
155
153
|
|
156
154
|
def sync_post(path, **params)
|
157
155
|
Async do
|
158
|
-
poster(path, params)
|
156
|
+
poster(path, **params)
|
159
157
|
end.wait
|
160
158
|
end
|
161
159
|
|
162
160
|
def async_slurp_stream(path, **params)
|
163
161
|
Async::Task.current.async do
|
164
|
-
slurper(path, params) { |data| yield data }
|
162
|
+
slurper(path, **params) { |data| yield data }
|
165
163
|
end
|
166
164
|
end
|
167
165
|
|
168
166
|
def sync_slurp_stream(path, **params)
|
169
167
|
Async do
|
170
|
-
slurper(path, params) { |data| yield data }
|
168
|
+
slurper(path, **params) { |data| yield data }
|
171
169
|
end.wait
|
172
170
|
end
|
173
171
|
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', '
|
24
|
-
spec.add_dependency 'async
|
23
|
+
spec.add_dependency 'async-http', '>= 0.38.1', '< 0.51.0'
|
24
|
+
spec.add_dependency 'async', '>= 1.17.1', '< 1.25.0'
|
25
25
|
|
26
|
-
spec.add_development_dependency 'async-rspec', '~> 1.
|
27
|
-
spec.add_development_dependency 'bundler', '~> 1.
|
26
|
+
spec.add_development_dependency 'async-rspec', '~> 1.14.0'
|
27
|
+
spec.add_development_dependency 'bundler', '~> 2.1.2'
|
28
28
|
spec.add_development_dependency 'pry', '~> 0.12.2'
|
29
|
-
spec.add_development_dependency 'rake', '~>
|
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.9.0'
|
32
32
|
end
|
metadata
CHANGED
@@ -1,71 +1,83 @@
|
|
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.1
|
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: 2020-01-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name: async
|
14
|
+
name: async-http
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 0.38.1
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.51.0
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
|
-
- - "
|
27
|
+
- - ">="
|
25
28
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
29
|
+
version: 0.38.1
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.51.0
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
|
-
name: async
|
34
|
+
name: async
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
30
36
|
requirements:
|
31
|
-
- - "
|
37
|
+
- - ">="
|
32
38
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
39
|
+
version: 1.17.1
|
40
|
+
- - "<"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 1.25.0
|
34
43
|
type: :runtime
|
35
44
|
prerelease: false
|
36
45
|
version_requirements: !ruby/object:Gem::Requirement
|
37
46
|
requirements:
|
38
|
-
- - "
|
47
|
+
- - ">="
|
39
48
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
49
|
+
version: 1.17.1
|
50
|
+
- - "<"
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 1.25.0
|
41
53
|
- !ruby/object:Gem::Dependency
|
42
54
|
name: async-rspec
|
43
55
|
requirement: !ruby/object:Gem::Requirement
|
44
56
|
requirements:
|
45
57
|
- - "~>"
|
46
58
|
- !ruby/object:Gem::Version
|
47
|
-
version: 1.
|
59
|
+
version: 1.14.0
|
48
60
|
type: :development
|
49
61
|
prerelease: false
|
50
62
|
version_requirements: !ruby/object:Gem::Requirement
|
51
63
|
requirements:
|
52
64
|
- - "~>"
|
53
65
|
- !ruby/object:Gem::Version
|
54
|
-
version: 1.
|
66
|
+
version: 1.14.0
|
55
67
|
- !ruby/object:Gem::Dependency
|
56
68
|
name: bundler
|
57
69
|
requirement: !ruby/object:Gem::Requirement
|
58
70
|
requirements:
|
59
71
|
- - "~>"
|
60
72
|
- !ruby/object:Gem::Version
|
61
|
-
version: 1.
|
73
|
+
version: 2.1.2
|
62
74
|
type: :development
|
63
75
|
prerelease: false
|
64
76
|
version_requirements: !ruby/object:Gem::Requirement
|
65
77
|
requirements:
|
66
78
|
- - "~>"
|
67
79
|
- !ruby/object:Gem::Version
|
68
|
-
version: 1.
|
80
|
+
version: 2.1.2
|
69
81
|
- !ruby/object:Gem::Dependency
|
70
82
|
name: pry
|
71
83
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,14 +98,14 @@ dependencies:
|
|
86
98
|
requirements:
|
87
99
|
- - "~>"
|
88
100
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
101
|
+
version: 13.0.0
|
90
102
|
type: :development
|
91
103
|
prerelease: false
|
92
104
|
version_requirements: !ruby/object:Gem::Requirement
|
93
105
|
requirements:
|
94
106
|
- - "~>"
|
95
107
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
108
|
+
version: 13.0.0
|
97
109
|
- !ruby/object:Gem::Dependency
|
98
110
|
name: rb-readline
|
99
111
|
requirement: !ruby/object:Gem::Requirement
|
@@ -114,14 +126,14 @@ dependencies:
|
|
114
126
|
requirements:
|
115
127
|
- - "~>"
|
116
128
|
- !ruby/object:Gem::Version
|
117
|
-
version: 3.
|
129
|
+
version: 3.9.0
|
118
130
|
type: :development
|
119
131
|
prerelease: false
|
120
132
|
version_requirements: !ruby/object:Gem::Requirement
|
121
133
|
requirements:
|
122
134
|
- - "~>"
|
123
135
|
- !ruby/object:Gem::Version
|
124
|
-
version: 3.
|
136
|
+
version: 3.9.0
|
125
137
|
description: Featuring full support for the REST, Streaming and Exploits API
|
126
138
|
email:
|
127
139
|
- kgruber1@emich.edu
|
@@ -129,6 +141,7 @@ executables: []
|
|
129
141
|
extensions: []
|
130
142
|
extra_rdoc_files: []
|
131
143
|
files:
|
144
|
+
- ".github/workflows/ci.yml"
|
132
145
|
- ".gitignore"
|
133
146
|
- ".rspec"
|
134
147
|
- ".travis.yml"
|
@@ -173,8 +186,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
173
186
|
- !ruby/object:Gem::Version
|
174
187
|
version: '0'
|
175
188
|
requirements: []
|
176
|
-
|
177
|
-
rubygems_version: 3.0.0.beta1
|
189
|
+
rubygems_version: 3.1.2
|
178
190
|
signing_key:
|
179
191
|
specification_version: 4
|
180
192
|
summary: A modern, async Ruby gem for Shodan, the world's first search engine for
|