mihari 5.1.0 → 5.1.2
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/.gitmodules +0 -3
- data/.rubocop.yml +6 -0
- data/README.md +0 -1
- data/lib/mihari/analyzers/base.rb +32 -27
- data/lib/mihari/analyzers/binaryedge.rb +17 -9
- data/lib/mihari/analyzers/censys.rb +10 -54
- data/lib/mihari/analyzers/circl.rb +7 -6
- data/lib/mihari/analyzers/crtsh.rb +12 -7
- data/lib/mihari/analyzers/dnstwister.rb +7 -7
- data/lib/mihari/analyzers/feed.rb +33 -10
- data/lib/mihari/analyzers/greynoise.rb +8 -33
- data/lib/mihari/analyzers/onyphe.rb +10 -36
- data/lib/mihari/analyzers/otx.rb +4 -3
- data/lib/mihari/analyzers/passivetotal.rb +8 -7
- data/lib/mihari/analyzers/pulsedive.rb +8 -7
- data/lib/mihari/analyzers/rule.rb +0 -1
- data/lib/mihari/analyzers/securitytrails.rb +8 -10
- data/lib/mihari/analyzers/shodan.rb +16 -90
- data/lib/mihari/analyzers/urlscan.rb +16 -6
- data/lib/mihari/analyzers/virustotal.rb +8 -6
- data/lib/mihari/analyzers/virustotal_intelligence.rb +12 -7
- data/lib/mihari/analyzers/zoomeye.rb +13 -10
- data/lib/mihari/clients/base.rb +53 -0
- data/lib/mihari/clients/binaryedge.rb +38 -0
- data/lib/mihari/clients/censys.rb +42 -0
- data/lib/mihari/clients/circl.rb +59 -0
- data/lib/mihari/clients/crtsh.rb +31 -0
- data/lib/mihari/clients/dnstwister.rb +40 -0
- data/lib/mihari/clients/greynoise.rb +34 -0
- data/lib/mihari/clients/misp.rb +29 -0
- data/lib/mihari/clients/onyphe.rb +35 -0
- data/lib/mihari/clients/otx.rb +49 -0
- data/lib/mihari/clients/passivetotal.rb +69 -0
- data/lib/mihari/clients/publsedive.rb +56 -0
- data/lib/mihari/clients/securitytrails.rb +94 -0
- data/lib/mihari/clients/shodan.rb +41 -0
- data/lib/mihari/clients/the_hive.rb +33 -0
- data/lib/mihari/clients/urlscan.rb +33 -0
- data/lib/mihari/clients/virustotal.rb +62 -0
- data/lib/mihari/clients/zoomeye.rb +74 -0
- data/lib/mihari/commands/database.rb +1 -6
- data/lib/mihari/commands/searcher.rb +1 -2
- data/lib/mihari/database.rb +9 -0
- data/lib/mihari/emitters/misp.rb +13 -20
- data/lib/mihari/emitters/the_hive.rb +3 -5
- data/lib/mihari/emitters/webhook.rb +2 -2
- data/lib/mihari/feed/reader.rb +14 -11
- data/lib/mihari/http.rb +29 -21
- data/lib/mihari/mixins/retriable.rb +3 -1
- data/lib/mihari/schemas/analyzer.rb +5 -4
- data/lib/mihari/structs/censys.rb +62 -0
- data/lib/mihari/structs/greynoise.rb +43 -0
- data/lib/mihari/structs/onyphe.rb +45 -0
- data/lib/mihari/structs/shodan.rb +83 -0
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/middleware/connection_adapter.rb +1 -3
- data/lib/mihari/web/public/assets/{index-63900d73.js → index-7d0fb8c4.js} +2 -2
- data/lib/mihari/web/public/index.html +1 -1
- data/lib/mihari/web/public/redoc-static.html +2 -2
- data/lib/mihari.rb +21 -2
- data/mihari.gemspec +15 -23
- metadata +55 -264
- data/lib/mihari/analyzers/clients/otx.rb +0 -36
- data/lib/mihari/analyzers/dnpedia.rb +0 -37
- data/lib/mihari/mixins/database.rb +0 -16
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "passivetotal"
|
4
|
-
|
5
3
|
module Mihari
|
6
4
|
module Analyzers
|
7
5
|
class PassiveTotal < Base
|
@@ -18,6 +16,9 @@ module Mihari
|
|
18
16
|
# @return [String, nil]
|
19
17
|
attr_reader :api_key
|
20
18
|
|
19
|
+
# @return [String]
|
20
|
+
attr_reader :query
|
21
|
+
|
21
22
|
def initialize(*args, **kwargs)
|
22
23
|
super(*args, **kwargs)
|
23
24
|
|
@@ -42,8 +43,8 @@ module Mihari
|
|
42
43
|
%w[passivetotal_username passivetotal_api_key]
|
43
44
|
end
|
44
45
|
|
45
|
-
def
|
46
|
-
@
|
46
|
+
def client
|
47
|
+
@client ||= Clients::PassiveTotal.new(username: username, api_key: api_key)
|
47
48
|
end
|
48
49
|
|
49
50
|
#
|
@@ -79,7 +80,7 @@ module Mihari
|
|
79
80
|
# @return [Array<String>]
|
80
81
|
#
|
81
82
|
def passive_dns_search
|
82
|
-
res =
|
83
|
+
res = client.passive_dns_search(query)
|
83
84
|
res["results"] || []
|
84
85
|
end
|
85
86
|
|
@@ -89,7 +90,7 @@ module Mihari
|
|
89
90
|
# @return [Array<Mihari::Artifact>]
|
90
91
|
#
|
91
92
|
def reverse_whois_search
|
92
|
-
res =
|
93
|
+
res = client.reverse_whois_search(query: query, field: "email")
|
93
94
|
results = res["results"] || []
|
94
95
|
results.map do |result|
|
95
96
|
data = result["domain"]
|
@@ -103,7 +104,7 @@ module Mihari
|
|
103
104
|
# @return [Array<Mihari::Artifact>]
|
104
105
|
#
|
105
106
|
def ssl_search
|
106
|
-
res =
|
107
|
+
res = client.ssl_search(query)
|
107
108
|
results = res["results"] || []
|
108
109
|
results.map do |result|
|
109
110
|
data = result["ipAddresses"]
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "pulsedive"
|
4
|
-
|
5
3
|
module Mihari
|
6
4
|
module Analyzers
|
7
5
|
class Pulsedive < Base
|
@@ -15,6 +13,9 @@ module Mihari
|
|
15
13
|
# @return [String, nil]
|
16
14
|
attr_reader :api_key
|
17
15
|
|
16
|
+
# @return [Integer]
|
17
|
+
attr_reader :query
|
18
|
+
|
18
19
|
def initialize(*args, **kwargs)
|
19
20
|
super
|
20
21
|
|
@@ -34,8 +35,8 @@ module Mihari
|
|
34
35
|
%w[pulsedive_api_key]
|
35
36
|
end
|
36
37
|
|
37
|
-
def
|
38
|
-
@
|
38
|
+
def client
|
39
|
+
@client ||= Clients::PulseDive.new(api_key: api_key)
|
39
40
|
end
|
40
41
|
|
41
42
|
#
|
@@ -55,12 +56,12 @@ module Mihari
|
|
55
56
|
def search
|
56
57
|
raise InvalidInputError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
|
57
58
|
|
58
|
-
indicator =
|
59
|
+
indicator = client.get_indicator(query)
|
59
60
|
iid = indicator["iid"]
|
60
61
|
|
61
|
-
properties =
|
62
|
+
properties = client.get_properties(iid)
|
62
63
|
(properties["dns"] || []).filter_map do |property|
|
63
|
-
if [
|
64
|
+
if %w[A PTR].include?(property["name"])
|
64
65
|
nil
|
65
66
|
else
|
66
67
|
data = property["value"]
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "securitytrails"
|
4
|
-
|
5
3
|
module Mihari
|
6
4
|
module Analyzers
|
7
5
|
class SecurityTrails < Base
|
@@ -15,6 +13,9 @@ module Mihari
|
|
15
13
|
# @return [String, nil]
|
16
14
|
attr_reader :api_key
|
17
15
|
|
16
|
+
# @return [String]
|
17
|
+
attr_reader :query
|
18
|
+
|
18
19
|
def initialize(*args, **kwargs)
|
19
20
|
super
|
20
21
|
|
@@ -34,8 +35,8 @@ module Mihari
|
|
34
35
|
%w[securitytrails_api_key]
|
35
36
|
end
|
36
37
|
|
37
|
-
def
|
38
|
-
@
|
38
|
+
def client
|
39
|
+
@client ||= Clients::SecurityTrails.new(api_key: api_key)
|
39
40
|
end
|
40
41
|
|
41
42
|
#
|
@@ -71,8 +72,7 @@ module Mihari
|
|
71
72
|
# @return [Array<String>]
|
72
73
|
#
|
73
74
|
def domain_search
|
74
|
-
|
75
|
-
records = result["records"] || []
|
75
|
+
records = client.get_all_dns_history(query, type: "a")
|
76
76
|
records.map do |record|
|
77
77
|
(record["values"] || []).map { |value| value["ip"] }
|
78
78
|
end.flatten.compact.uniq
|
@@ -84,8 +84,7 @@ module Mihari
|
|
84
84
|
# @return [Array<Mihari::Artifact>]
|
85
85
|
#
|
86
86
|
def ip_search
|
87
|
-
|
88
|
-
records = result["records"] || []
|
87
|
+
records = client.search_by_ip(query)
|
89
88
|
records.filter_map do |record|
|
90
89
|
data = record["hostname"]
|
91
90
|
Artifact.new(data: data, source: source, metadata: record)
|
@@ -98,8 +97,7 @@ module Mihari
|
|
98
97
|
# @return [Array<String>]
|
99
98
|
#
|
100
99
|
def mail_search
|
101
|
-
|
102
|
-
records = result["records"] || []
|
100
|
+
records = client.search_by_mail(query)
|
103
101
|
records.filter_map do |record|
|
104
102
|
data = record["hostname"]
|
105
103
|
Artifact.new(data: data, source: source, metadata: record)
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "shodan"
|
4
|
-
|
5
3
|
module Mihari
|
6
4
|
module Analyzers
|
7
5
|
class Shodan < Base
|
@@ -12,6 +10,12 @@ module Mihari
|
|
12
10
|
# @return [String, nil]
|
13
11
|
attr_reader :api_key
|
14
12
|
|
13
|
+
# @return [Integer]
|
14
|
+
attr_reader :interval
|
15
|
+
|
16
|
+
# @return [String]
|
17
|
+
attr_reader :query
|
18
|
+
|
15
19
|
def initialize(*args, **kwargs)
|
16
20
|
super(*args, **kwargs)
|
17
21
|
|
@@ -20,13 +24,9 @@ module Mihari
|
|
20
24
|
|
21
25
|
def artifacts
|
22
26
|
results = search
|
23
|
-
return []
|
27
|
+
return [] if results.empty?
|
24
28
|
|
25
|
-
results
|
26
|
-
matches = results.map { |result| result.matches || [] }.flatten
|
27
|
-
|
28
|
-
uniq_matches = matches.uniq(&:ip_str)
|
29
|
-
uniq_matches.map { |match| build_artifact(match, matches) }
|
29
|
+
results.map { |result| result.to_artifacts(source) }.flatten.uniq(&:data)
|
30
30
|
end
|
31
31
|
|
32
32
|
private
|
@@ -37,40 +37,32 @@ module Mihari
|
|
37
37
|
%w[shodan_api_key]
|
38
38
|
end
|
39
39
|
|
40
|
-
def
|
41
|
-
@
|
40
|
+
def client
|
41
|
+
@client ||= Clients::Shodan.new(api_key: api_key)
|
42
42
|
end
|
43
43
|
|
44
44
|
#
|
45
45
|
# Search with pagination
|
46
46
|
#
|
47
|
-
# @param [String] query
|
48
47
|
# @param [Integer] page
|
49
48
|
#
|
50
|
-
# @return [
|
49
|
+
# @return [Structs::Shodan::Result]
|
51
50
|
#
|
52
|
-
def search_with_page(
|
53
|
-
|
54
|
-
rescue ::Shodan::Error => e
|
55
|
-
raise RetryableError, e if e.message.include?("request timed out")
|
56
|
-
|
57
|
-
raise e
|
51
|
+
def search_with_page(page: 1)
|
52
|
+
client.search(query, page: page)
|
58
53
|
end
|
59
54
|
|
60
55
|
#
|
61
56
|
# Search
|
62
57
|
#
|
63
|
-
# @return [Array<
|
58
|
+
# @return [Array<Structs::Shodan::Result>]
|
64
59
|
#
|
65
60
|
def search
|
66
61
|
responses = []
|
67
62
|
(1..Float::INFINITY).each do |page|
|
68
|
-
res = search_with_page(
|
69
|
-
|
70
|
-
break unless res
|
71
|
-
|
63
|
+
res = search_with_page(page: page)
|
72
64
|
responses << res
|
73
|
-
break if res
|
65
|
+
break if res.total <= page * PAGE_SIZE
|
74
66
|
|
75
67
|
# sleep #{interval} seconds to avoid the rate limitation (if it is set)
|
76
68
|
sleep interval
|
@@ -82,42 +74,6 @@ module Mihari
|
|
82
74
|
responses
|
83
75
|
end
|
84
76
|
|
85
|
-
#
|
86
|
-
# Collect metadata from matches
|
87
|
-
#
|
88
|
-
# @param [Array<Structs::Shodan::Match>] matches
|
89
|
-
# @param [String] ip
|
90
|
-
#
|
91
|
-
# @return [Array<Hash>]
|
92
|
-
#
|
93
|
-
def collect_metadata_by_ip(matches, ip)
|
94
|
-
matches.select { |match| match.ip_str == ip }.map(&:metadata)
|
95
|
-
end
|
96
|
-
|
97
|
-
#
|
98
|
-
# Collect ports from matches
|
99
|
-
#
|
100
|
-
# @param [Array<Structs::Shodan::Match>] matches
|
101
|
-
# @param [String] ip
|
102
|
-
#
|
103
|
-
# @return [Array<String>]
|
104
|
-
#
|
105
|
-
def collect_ports_by_ip(matches, ip)
|
106
|
-
matches.select { |match| match.ip_str == ip }.map(&:port)
|
107
|
-
end
|
108
|
-
|
109
|
-
#
|
110
|
-
# Collect hostnames from matches
|
111
|
-
#
|
112
|
-
# @param [Array<Structs::Shodan::Match>] matches
|
113
|
-
# @param [String] ip
|
114
|
-
#
|
115
|
-
# @return [Array<String>]
|
116
|
-
#
|
117
|
-
def collect_hostnames_by_ip(matches, ip)
|
118
|
-
matches.select { |match| match.ip_str == ip }.map(&:hostnames).flatten.uniq
|
119
|
-
end
|
120
|
-
|
121
77
|
#
|
122
78
|
# Build an artifact from a Shodan search API response
|
123
79
|
#
|
@@ -127,36 +83,6 @@ module Mihari
|
|
127
83
|
# @return [Artifact]
|
128
84
|
#
|
129
85
|
def build_artifact(match, matches)
|
130
|
-
as = nil
|
131
|
-
as = AutonomousSystem.new(asn: normalize_asn(match.asn)) unless match.asn.nil?
|
132
|
-
|
133
|
-
geolocation = nil
|
134
|
-
if !match.location.country_name.nil? && !match.location.country_code.nil?
|
135
|
-
geolocation = Geolocation.new(
|
136
|
-
country: match.location.country_name,
|
137
|
-
country_code: match.location.country_code
|
138
|
-
)
|
139
|
-
end
|
140
|
-
|
141
|
-
metadata = collect_metadata_by_ip(matches, match.ip_str)
|
142
|
-
|
143
|
-
ports = collect_ports_by_ip(matches, match.ip_str).map do |port|
|
144
|
-
Port.new(port: port)
|
145
|
-
end
|
146
|
-
|
147
|
-
reverse_dns_names = collect_hostnames_by_ip(matches, match.ip_str).map do |name|
|
148
|
-
ReverseDnsName.new(name: name)
|
149
|
-
end
|
150
|
-
|
151
|
-
Artifact.new(
|
152
|
-
data: match.ip_str,
|
153
|
-
source: source,
|
154
|
-
metadata: metadata,
|
155
|
-
autonomous_system: as,
|
156
|
-
geolocation: geolocation,
|
157
|
-
ports: ports,
|
158
|
-
reverse_dns_names: reverse_dns_names
|
159
|
-
)
|
160
86
|
end
|
161
87
|
end
|
162
88
|
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "urlscan"
|
4
|
-
|
5
3
|
module Mihari
|
6
4
|
module Analyzers
|
7
5
|
class Urlscan < Base
|
@@ -17,10 +15,22 @@ module Mihari
|
|
17
15
|
# @return [String, nil]
|
18
16
|
attr_reader :api_key
|
19
17
|
|
18
|
+
# @return [String]
|
19
|
+
attr_reader :query
|
20
|
+
|
21
|
+
# @return [Integer]
|
22
|
+
attr_reader :interval
|
23
|
+
|
24
|
+
# @return [String]
|
25
|
+
attr_reader :allowed_data_types
|
26
|
+
|
20
27
|
def initialize(*args, **kwargs)
|
21
28
|
super
|
22
29
|
|
23
|
-
|
30
|
+
unless valid_alllowed_data_types?
|
31
|
+
raise InvalidInputError,
|
32
|
+
"allowed_data_types should be any of url, domain and ip."
|
33
|
+
end
|
24
34
|
|
25
35
|
@api_key = kwargs[:api_key] || Mihari.config.urlscan_api_key
|
26
36
|
end
|
@@ -44,8 +54,8 @@ module Mihari
|
|
44
54
|
%w[urlscan_api_key]
|
45
55
|
end
|
46
56
|
|
47
|
-
def
|
48
|
-
@
|
57
|
+
def client
|
58
|
+
@client ||= Clients::UrlScan.new(api_key: api_key)
|
49
59
|
end
|
50
60
|
|
51
61
|
#
|
@@ -54,7 +64,7 @@ module Mihari
|
|
54
64
|
# @return [Structs::Urlscan::Response]
|
55
65
|
#
|
56
66
|
def search_with_search_after(search_after: nil)
|
57
|
-
res =
|
67
|
+
res = client.search(query, size: SIZE, search_after: search_after)
|
58
68
|
Structs::Urlscan::Response.from_dynamic! res
|
59
69
|
end
|
60
70
|
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "virustotal"
|
4
|
-
|
5
3
|
module Mihari
|
6
4
|
module Analyzers
|
7
5
|
class VirusTotal < Base
|
@@ -9,11 +7,15 @@ module Mihari
|
|
9
7
|
|
10
8
|
param :query
|
11
9
|
|
10
|
+
# @return [String]
|
12
11
|
attr_reader :type
|
13
12
|
|
14
13
|
# @return [String, nil]
|
15
14
|
attr_reader :api_key
|
16
15
|
|
16
|
+
# @return [String]
|
17
|
+
attr_reader :query
|
18
|
+
|
17
19
|
def initialize(*args, **kwargs)
|
18
20
|
super(*args, **kwargs)
|
19
21
|
|
@@ -33,8 +35,8 @@ module Mihari
|
|
33
35
|
%w[virustotal_api_key]
|
34
36
|
end
|
35
37
|
|
36
|
-
def
|
37
|
-
@
|
38
|
+
def client
|
39
|
+
@client = Clients::VirusTotal.new(api_key: api_key)
|
38
40
|
end
|
39
41
|
|
40
42
|
#
|
@@ -68,7 +70,7 @@ module Mihari
|
|
68
70
|
# @return [Array<Mihari::Artifact>]
|
69
71
|
#
|
70
72
|
def domain_search
|
71
|
-
res =
|
73
|
+
res = client.domain_search(query)
|
72
74
|
|
73
75
|
data = res["data"] || []
|
74
76
|
data.filter_map do |item|
|
@@ -83,7 +85,7 @@ module Mihari
|
|
83
85
|
# @return [Array<Mihari::Artifact>]
|
84
86
|
#
|
85
87
|
def ip_search
|
86
|
-
res =
|
88
|
+
res = client.ip_search(query)
|
87
89
|
|
88
90
|
data = res["data"] || []
|
89
91
|
data.filter_map do |item|
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "virustotal"
|
4
|
-
|
5
3
|
module Mihari
|
6
4
|
module Analyzers
|
7
5
|
class VirusTotalIntelligence < Base
|
@@ -12,6 +10,12 @@ module Mihari
|
|
12
10
|
# @return [String, nil]
|
13
11
|
attr_reader :api_key
|
14
12
|
|
13
|
+
# @return [String]
|
14
|
+
attr_reader :query
|
15
|
+
|
16
|
+
# @return [Integer]
|
17
|
+
attr_reader :interval
|
18
|
+
|
15
19
|
def initialize(*args, **kwargs)
|
16
20
|
super
|
17
21
|
|
@@ -21,7 +25,7 @@ module Mihari
|
|
21
25
|
end
|
22
26
|
|
23
27
|
def artifacts
|
24
|
-
responses =
|
28
|
+
responses = search_with_cursor
|
25
29
|
responses.map do |response|
|
26
30
|
response.data.map do |datum|
|
27
31
|
Artifact.new(data: datum.value, source: source, metadata: datum.metadata)
|
@@ -40,8 +44,8 @@ module Mihari
|
|
40
44
|
#
|
41
45
|
# @return [::VirusTotal::API]
|
42
46
|
#
|
43
|
-
def
|
44
|
-
@
|
47
|
+
def client
|
48
|
+
@client = Clients::VirusTotal.new(api_key: api_key)
|
45
49
|
end
|
46
50
|
|
47
51
|
#
|
@@ -49,12 +53,13 @@ module Mihari
|
|
49
53
|
#
|
50
54
|
# @return [Array<Structs::VirusTotalIntelligence::Response>]
|
51
55
|
#
|
52
|
-
def
|
56
|
+
def search_with_cursor
|
53
57
|
cursor = nil
|
54
58
|
responses = []
|
55
59
|
|
56
60
|
loop do
|
57
|
-
response = Structs::VirusTotalIntelligence::Response.from_dynamic!(
|
61
|
+
response = Structs::VirusTotalIntelligence::Response.from_dynamic!(client.intel_search(query,
|
62
|
+
cursor: cursor))
|
58
63
|
responses << response
|
59
64
|
|
60
65
|
break if response.meta.cursor.nil?
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "zoomeye"
|
4
|
-
|
5
3
|
module Mihari
|
6
4
|
module Analyzers
|
7
5
|
class ZoomEye < Base
|
@@ -14,6 +12,15 @@ module Mihari
|
|
14
12
|
# @return [String, nil]
|
15
13
|
attr_reader :api_key
|
16
14
|
|
15
|
+
# @return [String]
|
16
|
+
attr_reader :query
|
17
|
+
|
18
|
+
# @return [String]
|
19
|
+
attr_reader :type
|
20
|
+
|
21
|
+
# @return [Integer]
|
22
|
+
attr_reader :interval
|
23
|
+
|
17
24
|
def initialize(*args, **kwargs)
|
18
25
|
super(*args, **kwargs)
|
19
26
|
|
@@ -48,8 +55,8 @@ module Mihari
|
|
48
55
|
%w[zoomeye_api_key]
|
49
56
|
end
|
50
57
|
|
51
|
-
def
|
52
|
-
@
|
58
|
+
def client
|
59
|
+
@client ||= Clients::ZoomEye.new(api_key: api_key)
|
53
60
|
end
|
54
61
|
|
55
62
|
#
|
@@ -83,9 +90,7 @@ module Mihari
|
|
83
90
|
# @return [Hash, nil]
|
84
91
|
#
|
85
92
|
def _host_search(query, page: 1)
|
86
|
-
|
87
|
-
rescue ::ZoomEye::Error => _e
|
88
|
-
nil
|
93
|
+
client.host_search(query, page: page)
|
89
94
|
end
|
90
95
|
|
91
96
|
#
|
@@ -118,9 +123,7 @@ module Mihari
|
|
118
123
|
# @return [Hash, nil]
|
119
124
|
#
|
120
125
|
def _web_search(query, page: 1)
|
121
|
-
|
122
|
-
rescue ::ZoomEye::Error => _e
|
123
|
-
nil
|
126
|
+
client.web_search(query, page: page)
|
124
127
|
end
|
125
128
|
|
126
129
|
#
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mihari
|
4
|
+
module Clients
|
5
|
+
class Base
|
6
|
+
# @return [String]
|
7
|
+
attr_reader :base_url
|
8
|
+
|
9
|
+
# @return [Hash]
|
10
|
+
attr_reader :headers
|
11
|
+
|
12
|
+
#
|
13
|
+
# @param [String] base_url
|
14
|
+
# @param [Hash] headers
|
15
|
+
#
|
16
|
+
def initialize(base_url, headers: {})
|
17
|
+
@base_url = base_url
|
18
|
+
@headers = headers || {}
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
#
|
24
|
+
# @param [String] path
|
25
|
+
#
|
26
|
+
# @return [String]
|
27
|
+
#
|
28
|
+
def url_for(path)
|
29
|
+
base_url + path
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# @param [String] path
|
34
|
+
# @param [Hashk, nil] params
|
35
|
+
#
|
36
|
+
# @return [String] <description>
|
37
|
+
#
|
38
|
+
def get(path, params: nil)
|
39
|
+
HTTP.get(url_for(path), headers: headers, params: params)
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# @param [String] path
|
44
|
+
# @param [Hash, nil] json
|
45
|
+
#
|
46
|
+
# @return [String] <description>
|
47
|
+
#
|
48
|
+
def post(path, json: {})
|
49
|
+
HTTP.post(url_for(path), headers: headers, json: json)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mihari
|
4
|
+
module Clients
|
5
|
+
class BinaryEdge < Base
|
6
|
+
#
|
7
|
+
# @param [String] base_url
|
8
|
+
# @param [String, nil] api_key
|
9
|
+
# @param [Hash] headers
|
10
|
+
#
|
11
|
+
def initialize(base_url = "https://api.binaryedge.io/v2", api_key:, headers: {})
|
12
|
+
raise(ArgumentError, "'api_key' argument is required") unless api_key
|
13
|
+
|
14
|
+
headers["x-key"] = api_key
|
15
|
+
|
16
|
+
super(base_url, headers: headers)
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# @param [String] query String used to query our data
|
21
|
+
# @param [Integer] page Default 1, Maximum: 500
|
22
|
+
# @param [Integer, nil] only_ips If selected, only output IP addresses, ports and protocols.
|
23
|
+
#
|
24
|
+
# @return [Hash]
|
25
|
+
#
|
26
|
+
def search(query, page: 1, only_ips: nil)
|
27
|
+
params = {
|
28
|
+
query: query,
|
29
|
+
page: page,
|
30
|
+
only_ips: only_ips
|
31
|
+
}.compact
|
32
|
+
|
33
|
+
res = get("/query/search", params: params)
|
34
|
+
JSON.parse(res.body.to_s)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "base64"
|
4
|
+
|
5
|
+
module Mihari
|
6
|
+
module Clients
|
7
|
+
class Censys < Base
|
8
|
+
#
|
9
|
+
# @param [String] base_url
|
10
|
+
# @param [String, nil] id
|
11
|
+
# @param [String, nil] secret
|
12
|
+
# @param [Hash] headers
|
13
|
+
#
|
14
|
+
def initialize(base_url = "https://search.censys.io", id:, secret:, headers: {})
|
15
|
+
raise(ArgumentError, "'id' argument is required") if id.nil?
|
16
|
+
raise(ArgumentError, "'secret' argument is required") if secret.nil?
|
17
|
+
|
18
|
+
headers["authorization"] = "Basic #{Base64.strict_encode64("#{id}:#{secret}")}"
|
19
|
+
|
20
|
+
super(base_url, headers: headers)
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# Search current index.
|
25
|
+
#
|
26
|
+
# Searches the given index for all records that match the given query.
|
27
|
+
# For more details, see our documentation: https://search.censys.io/api/v2/docs
|
28
|
+
#
|
29
|
+
# @param [String] query the query to be executed.
|
30
|
+
# @params [Integer, nil] per_page the number of results to be returned for each page.
|
31
|
+
# @params [Integer, nil] cursor the cursor of the desired result set.
|
32
|
+
#
|
33
|
+
# @return [Structs::Censys::Response]
|
34
|
+
#
|
35
|
+
def search(query, per_page: nil, cursor: nil)
|
36
|
+
params = { q: query, per_page: per_page, cursor: cursor }.compact
|
37
|
+
res = get("/api/v2/hosts/search", params: params)
|
38
|
+
Structs::Censys::Response.from_dynamic! JSON.parse(res.body.to_s)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|