mihari 5.4.9 → 5.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/docs/analyzers/binaryedge.md +2 -2
- data/docs/analyzers/censys.md +3 -3
- data/docs/analyzers/circl.md +3 -3
- data/docs/analyzers/crtsh.md +2 -2
- data/docs/analyzers/dnstwister.md +1 -1
- data/docs/analyzers/feed.md +7 -7
- data/docs/analyzers/greynoise.md +2 -2
- data/docs/analyzers/hunterhow.md +4 -4
- data/docs/analyzers/index.md +13 -8
- data/docs/analyzers/onyphe.md +2 -2
- data/docs/analyzers/otx.md +2 -2
- data/docs/analyzers/passivetotal.md +3 -3
- data/docs/analyzers/pulsedive.md +2 -2
- data/docs/analyzers/securitytrails.md +2 -2
- data/docs/analyzers/shodan.md +2 -2
- data/docs/analyzers/urlscan.md +2 -2
- data/docs/analyzers/virustotal.md +2 -2
- data/docs/analyzers/virustotal_intelligence.md +2 -2
- data/docs/analyzers/zoomeye.md +3 -3
- data/docs/emitters/hive.md +3 -3
- data/docs/emitters/index.md +29 -0
- data/docs/emitters/misp.md +2 -2
- data/docs/emitters/slack.md +2 -2
- data/docs/emitters/webhook.md +4 -4
- data/docs/enrichers/index.md +29 -0
- data/docs/enrichers/ipinfo.md +7 -0
- data/docs/index.md +0 -2
- data/docs/installation.md +1 -1
- data/docs/rule.md +11 -11
- data/frontend/package-lock.json +294 -2772
- data/frontend/package.json +10 -10
- data/lib/mihari/analyzers/base.rb +15 -8
- data/lib/mihari/analyzers/binaryedge.rb +5 -1
- data/lib/mihari/analyzers/censys.rb +6 -1
- data/lib/mihari/analyzers/greynoise.rb +5 -1
- data/lib/mihari/analyzers/hunterhow.rb +5 -1
- data/lib/mihari/analyzers/onyphe.rb +5 -1
- data/lib/mihari/analyzers/rule.rb +43 -7
- data/lib/mihari/analyzers/shodan.rb +5 -1
- data/lib/mihari/analyzers/urlscan.rb +5 -1
- data/lib/mihari/analyzers/virustotal_intelligence.rb +5 -1
- data/lib/mihari/analyzers/zoomeye.rb +5 -1
- data/lib/mihari/clients/base.rb +7 -7
- data/lib/mihari/clients/binaryedge.rb +10 -4
- data/lib/mihari/clients/censys.rb +11 -4
- data/lib/mihari/clients/greynoise.rb +10 -4
- data/lib/mihari/clients/hunterhow.rb +10 -4
- data/lib/mihari/clients/misp.rb +3 -2
- data/lib/mihari/clients/onyphe.rb +10 -4
- data/lib/mihari/clients/shodan.rb +10 -4
- data/lib/mihari/clients/the_hive.rb +3 -2
- data/lib/mihari/clients/urlscan.rb +9 -3
- data/lib/mihari/clients/virustotal.rb +10 -4
- data/lib/mihari/clients/zoomeye.rb +11 -5
- data/lib/mihari/config.rb +8 -0
- data/lib/mihari/emitters/base.rb +49 -12
- data/lib/mihari/emitters/misp.rb +7 -6
- data/lib/mihari/emitters/slack.rb +24 -6
- data/lib/mihari/emitters/the_hive.rb +8 -7
- data/lib/mihari/emitters/webhook.rb +31 -29
- data/lib/mihari/enrichers/base.rb +53 -16
- data/lib/mihari/enrichers/google_public_dns.rb +33 -42
- data/lib/mihari/enrichers/ipinfo.rb +32 -34
- data/lib/mihari/enrichers/shodan.rb +18 -26
- data/lib/mihari/enrichers/whois.rb +121 -111
- data/lib/mihari/mixins/retriable.rb +4 -2
- data/lib/mihari/models/artifact.rb +37 -23
- data/lib/mihari/models/autonomous_system.rb +3 -2
- data/lib/mihari/models/cpe.rb +3 -2
- data/lib/mihari/models/dns.rb +3 -2
- data/lib/mihari/models/geolocation.rb +3 -2
- data/lib/mihari/models/port.rb +3 -2
- data/lib/mihari/models/reverse_dns.rb +3 -2
- data/lib/mihari/models/whois.rb +4 -3
- data/lib/mihari/schemas/analyzer.rb +2 -1
- data/lib/mihari/schemas/emitter.rb +39 -25
- data/lib/mihari/schemas/enricher.rb +28 -2
- data/lib/mihari/schemas/rule.rb +6 -2
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/endpoints/ip_addresses.rb +1 -1
- data/lib/mihari/web/public/assets/index-b5d817a3.js +1749 -0
- data/lib/mihari/web/public/index.html +1 -1
- data/lib/mihari/web/public/redoc-static.html +400 -400
- data/mihari.gemspec +2 -2
- data/mkdocs.yml +8 -6
- data/requirements.txt +1 -1
- metadata +7 -7
- data/lib/mihari/web/public/assets/index-a92abd57.js +0 -1740
@@ -11,14 +11,20 @@ module Mihari
|
|
11
11
|
# @param [String] base_url
|
12
12
|
# @param [String, nil] api_key
|
13
13
|
# @param [Hash] headers
|
14
|
-
# @param [Integer
|
14
|
+
# @param [Integer] pagination_interval
|
15
15
|
# @param [Integer, nil] timeout
|
16
16
|
#
|
17
|
-
def initialize(
|
17
|
+
def initialize(
|
18
|
+
base_url = "https://api.zoomeye.org",
|
19
|
+
api_key:,
|
20
|
+
headers: {},
|
21
|
+
pagination_interval: 0,
|
22
|
+
timeout: nil
|
23
|
+
)
|
18
24
|
raise(ArgumentError, "'api_key' argument is required") unless api_key
|
19
25
|
|
20
26
|
headers["api-key"] = api_key
|
21
|
-
super(base_url, headers: headers,
|
27
|
+
super(base_url, headers: headers, pagination_interval: pagination_interval, timeout: timeout)
|
22
28
|
end
|
23
29
|
|
24
30
|
#
|
@@ -59,7 +65,7 @@ module Mihari
|
|
59
65
|
total = res["total"].to_i
|
60
66
|
break if total <= page * PAGE_SIZE
|
61
67
|
|
62
|
-
|
68
|
+
sleep_pagination_interval
|
63
69
|
end
|
64
70
|
end
|
65
71
|
end
|
@@ -102,7 +108,7 @@ module Mihari
|
|
102
108
|
total = res["total"].to_i
|
103
109
|
break if total <= page * PAGE_SIZE
|
104
110
|
|
105
|
-
|
111
|
+
sleep_pagination_interval
|
106
112
|
end
|
107
113
|
end
|
108
114
|
end
|
data/lib/mihari/config.rb
CHANGED
@@ -90,6 +90,12 @@ module Mihari
|
|
90
90
|
# @return [Integer]
|
91
91
|
attr_reader :retry_times
|
92
92
|
|
93
|
+
# @return [Boolean]
|
94
|
+
attr_reader :retry_exponential_backoff
|
95
|
+
|
96
|
+
# @return [Integer]
|
97
|
+
attr_reader :pagination_interval
|
98
|
+
|
93
99
|
# @return [Integer]
|
94
100
|
attr_reader :pagination_limit
|
95
101
|
|
@@ -152,7 +158,9 @@ module Mihari
|
|
152
158
|
|
153
159
|
@retry_times = ENV.fetch("RETRY_TIMES", 3).to_i
|
154
160
|
@retry_interval = ENV.fetch("RETRY_INTERVAL", 5).to_i
|
161
|
+
@retry_exponential_backoff = ENV.fetch("RETRY_EXPONENTIAL_BACKOFF", true).to_s.downcase == "true"
|
155
162
|
|
163
|
+
@pagination_interval = ENV.fetch("PAGINATION_INTERVAL", 0).to_i
|
156
164
|
@pagination_limit = ENV.fetch("PAGINATION_LIMIT", 100).to_i
|
157
165
|
|
158
166
|
@ignore_error = ENV.fetch("IGNORE_ERROR", false)
|
data/lib/mihari/emitters/base.rb
CHANGED
@@ -14,39 +14,76 @@ module Mihari
|
|
14
14
|
# @return [Mihari::Services::Rule]
|
15
15
|
attr_reader :rule
|
16
16
|
|
17
|
+
# @return [Hash]
|
18
|
+
attr_reader :options
|
19
|
+
|
17
20
|
#
|
18
21
|
# @param [Array<Mihari::Artifact>] artifacts
|
19
22
|
# @param [Mihari::Services::RuleProxy] rule
|
20
|
-
# @param [Hash]
|
23
|
+
# @param [Hash, nil] options
|
24
|
+
# @param [Hash] **_params
|
21
25
|
#
|
22
|
-
def initialize(artifacts:, rule:, **
|
26
|
+
def initialize(artifacts:, rule:, options: nil, **_params)
|
23
27
|
@artifacts = artifacts
|
24
28
|
@rule = rule
|
29
|
+
@options = options || {}
|
25
30
|
end
|
26
31
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
+
#
|
33
|
+
# @return [Integer]
|
34
|
+
#
|
35
|
+
def retry_interval
|
36
|
+
options[:retry_interval] || Mihari.config.retry_interval
|
32
37
|
end
|
33
38
|
|
39
|
+
#
|
34
40
|
# @return [Boolean]
|
35
|
-
|
36
|
-
|
41
|
+
#
|
42
|
+
def retry_exponential_backoff
|
43
|
+
options[:retry_exponential_backoff] || Mihari.config.retry_exponential_backoff
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# @return [Integer]
|
48
|
+
#
|
49
|
+
def retry_times
|
50
|
+
options[:retry_times] || Mihari.config.retry_times
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# @return [Integer, nil]
|
55
|
+
#
|
56
|
+
def timeout
|
57
|
+
options[:timeout]
|
37
58
|
end
|
38
59
|
|
39
|
-
|
40
|
-
|
60
|
+
# @return [Boolean]
|
61
|
+
def valid?
|
62
|
+
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
41
63
|
end
|
42
64
|
|
43
65
|
def result
|
44
|
-
Try[StandardError]
|
66
|
+
Try[StandardError] do
|
67
|
+
retry_on_error(
|
68
|
+
times: retry_times,
|
69
|
+
interval: retry_interval,
|
70
|
+
exponential_backoff: retry_exponential_backoff
|
71
|
+
) do
|
72
|
+
emit
|
73
|
+
end
|
74
|
+
end.to_result
|
45
75
|
end
|
46
76
|
|
47
77
|
def emit
|
48
78
|
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
49
79
|
end
|
80
|
+
|
81
|
+
class << self
|
82
|
+
def inherited(child)
|
83
|
+
super
|
84
|
+
Mihari.emitters << child
|
85
|
+
end
|
86
|
+
end
|
50
87
|
end
|
51
88
|
end
|
52
89
|
end
|
data/lib/mihari/emitters/misp.rb
CHANGED
@@ -18,13 +18,14 @@ module Mihari
|
|
18
18
|
#
|
19
19
|
# @param [Array<Mihari::Artifact>] artifacts
|
20
20
|
# @param [Mihari::Services::Rule] rule
|
21
|
-
# @param [Hash]
|
21
|
+
# @param [Hash, nil] options
|
22
|
+
# @param [Hash] **params
|
22
23
|
#
|
23
|
-
def initialize(artifacts:, rule:, **
|
24
|
-
super(artifacts: artifacts, rule: rule,
|
24
|
+
def initialize(artifacts:, rule:, options: nil, **params)
|
25
|
+
super(artifacts: artifacts, rule: rule, options: options)
|
25
26
|
|
26
|
-
@url =
|
27
|
-
@api_key =
|
27
|
+
@url = params[:url] || Mihari.config.misp_url
|
28
|
+
@api_key = params[:api_key] || Mihari.config.misp_api_key
|
28
29
|
end
|
29
30
|
|
30
31
|
# @return [Boolean]
|
@@ -70,7 +71,7 @@ module Mihari
|
|
70
71
|
end
|
71
72
|
|
72
73
|
def client
|
73
|
-
@client ||= Clients::MISP.new(url, api_key: api_key)
|
74
|
+
@client ||= Clients::MISP.new(url, api_key: api_key, timeout: timeout)
|
74
75
|
end
|
75
76
|
|
76
77
|
#
|
@@ -134,13 +134,14 @@ module Mihari
|
|
134
134
|
#
|
135
135
|
# @param [Array<Mihari::Artifact>] artifacts
|
136
136
|
# @param [Mihari::Services::Rule] rule
|
137
|
-
# @param [Hash]
|
137
|
+
# @param [Hash, nil] options
|
138
|
+
# @param [Hash] **params
|
138
139
|
#
|
139
|
-
def initialize(artifacts:, rule:, **
|
140
|
-
super(artifacts: artifacts, rule: rule,
|
140
|
+
def initialize(artifacts:, rule:, options: nil, **params)
|
141
|
+
super(artifacts: artifacts, rule: rule, options: options)
|
141
142
|
|
142
|
-
@webhook_url =
|
143
|
-
@channel =
|
143
|
+
@webhook_url = params[:webhook_url] || Mihari.config.slack_webhook_url
|
144
|
+
@channel = params[:channel] || Mihari.config.slack_channel || DEFAULT_CHANNEL
|
144
145
|
@username = DEFAULT_USERNAME
|
145
146
|
end
|
146
147
|
|
@@ -162,8 +163,25 @@ module Mihari
|
|
162
163
|
webhook_url?
|
163
164
|
end
|
164
165
|
|
166
|
+
#
|
167
|
+
# @return [::Slack::Notifier]
|
168
|
+
#
|
165
169
|
def notifier
|
166
|
-
@notifier ||=
|
170
|
+
@notifier ||= [].tap do |out|
|
171
|
+
out << if timeout.nil?
|
172
|
+
::Slack::Notifier.new(
|
173
|
+
webhook_url,
|
174
|
+
channel: channel, username: username
|
175
|
+
)
|
176
|
+
else
|
177
|
+
::Slack::Notifier.new(
|
178
|
+
webhook_url,
|
179
|
+
channel: channel,
|
180
|
+
username: username,
|
181
|
+
http_options: { timeout: timeout }
|
182
|
+
)
|
183
|
+
end
|
184
|
+
end.first
|
167
185
|
end
|
168
186
|
|
169
187
|
#
|
@@ -15,14 +15,15 @@ module Mihari
|
|
15
15
|
#
|
16
16
|
# @param [Array<Mihari::Artifact>] artifacts
|
17
17
|
# @param [Mihari::Services::Rule] rule
|
18
|
-
# @param [Hash]
|
18
|
+
# @param [Hash, nil] options
|
19
|
+
# @param [Hash] **params
|
19
20
|
#
|
20
|
-
def initialize(artifacts:, rule:, **
|
21
|
-
super(artifacts: artifacts, rule: rule,
|
21
|
+
def initialize(artifacts:, rule:, options: nil, **params)
|
22
|
+
super(artifacts: artifacts, rule: rule, options: options)
|
22
23
|
|
23
|
-
@url =
|
24
|
-
@api_key =
|
25
|
-
@api_version =
|
24
|
+
@url = params[:url] || Mihari.config.thehive_url
|
25
|
+
@api_key = params[:api_key] || Mihari.config.thehive_api_key
|
26
|
+
@api_version = params[:api_version] || Mihari.config.thehive_api_version
|
26
27
|
end
|
27
28
|
|
28
29
|
# @return [Boolean]
|
@@ -79,7 +80,7 @@ module Mihari
|
|
79
80
|
end
|
80
81
|
|
81
82
|
def client
|
82
|
-
@client ||= Clients::TheHive.new(url, api_key: api_key, api_version: normalized_api_version)
|
83
|
+
@client ||= Clients::TheHive.new(url, api_key: api_key, api_version: normalized_api_version, timeout: timeout)
|
83
84
|
end
|
84
85
|
|
85
86
|
#
|
@@ -5,28 +5,30 @@ require "erb"
|
|
5
5
|
module Mihari
|
6
6
|
module Emitters
|
7
7
|
class PayloadTemplate < ERB
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
"
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
8
|
+
class << self
|
9
|
+
def template
|
10
|
+
%{
|
11
|
+
{
|
12
|
+
"rule": {
|
13
|
+
"id": "<%= @rule.id %>",
|
14
|
+
"title": "<%= @rule.title %>",
|
15
|
+
"description": "<%= @rule.description %>"
|
16
|
+
},
|
17
|
+
"artifacts": [
|
18
|
+
<% @artifacts.each_with_index do |artifact, idx| %>
|
19
|
+
"<%= artifact.data %>"
|
20
|
+
<%= ',' if idx < (@artifacts.length - 1) %>
|
21
|
+
<% end %>
|
22
|
+
],
|
23
|
+
"tags": [
|
24
|
+
<% @rule.tags.each_with_index do |tag, idx| %>
|
25
|
+
"<%= tag %>"
|
26
|
+
<%= ',' if idx < (@rule.tags.length - 1) %>
|
27
|
+
<% end %>
|
28
|
+
]
|
29
|
+
}
|
30
|
+
}
|
31
|
+
end
|
30
32
|
end
|
31
33
|
|
32
34
|
def initialize(artifacts:, rule:, options: {})
|
@@ -60,13 +62,13 @@ module Mihari
|
|
60
62
|
# @param [Mihari::Services::Rule] rule
|
61
63
|
# @param [Hash] **options
|
62
64
|
#
|
63
|
-
def initialize(artifacts:, rule:, **
|
64
|
-
super(artifacts: artifacts, rule: rule,
|
65
|
+
def initialize(artifacts:, rule:, options: nil, **params)
|
66
|
+
super(artifacts: artifacts, rule: rule, options: options)
|
65
67
|
|
66
|
-
@url = Addressable::URI.parse(
|
67
|
-
@headers =
|
68
|
-
@method =
|
69
|
-
@template =
|
68
|
+
@url = Addressable::URI.parse(params[:url])
|
69
|
+
@headers = params[:headers] || {}
|
70
|
+
@method = params[:method] || "POST"
|
71
|
+
@template = params[:template]
|
70
72
|
end
|
71
73
|
|
72
74
|
def emit
|
@@ -90,7 +92,7 @@ module Mihari
|
|
90
92
|
private
|
91
93
|
|
92
94
|
def http
|
93
|
-
HTTP::Factory.build headers: headers
|
95
|
+
HTTP::Factory.build headers: headers, timeout: timeout
|
94
96
|
end
|
95
97
|
|
96
98
|
#
|
@@ -4,31 +4,68 @@ module Mihari
|
|
4
4
|
module Enrichers
|
5
5
|
class Base
|
6
6
|
include Mixins::Configurable
|
7
|
+
include Mixins::Retriable
|
7
8
|
|
8
|
-
|
9
|
-
include Dry::Monads[:result, :try]
|
9
|
+
include Dry::Monads[:result, :try]
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
Mihari.enrichers << child
|
14
|
-
end
|
11
|
+
# @return [Hash]
|
12
|
+
attr_reader :options
|
15
13
|
|
16
|
-
|
17
|
-
|
18
|
-
|
14
|
+
def initialize(options: nil)
|
15
|
+
@options = options || {}
|
16
|
+
end
|
19
17
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
end
|
18
|
+
#
|
19
|
+
# @return [Integer]
|
20
|
+
#
|
21
|
+
def retry_interval
|
22
|
+
options[:retry_interval] || Mihari.config.retry_interval
|
26
23
|
end
|
27
24
|
|
25
|
+
#
|
28
26
|
# @return [Boolean]
|
29
|
-
|
27
|
+
#
|
28
|
+
def retry_exponential_backoff
|
29
|
+
options[:retry_exponential_backoff] || Mihari.config.retry_exponential_backoff
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# @return [Integer]
|
34
|
+
#
|
35
|
+
def retry_times
|
36
|
+
options[:retry_times] || Mihari.config.retry_times
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
# @return [Integer, nil]
|
41
|
+
#
|
42
|
+
def timeout
|
43
|
+
options[:timeout]
|
44
|
+
end
|
45
|
+
|
46
|
+
def query_result(value)
|
47
|
+
Try[StandardError] do
|
48
|
+
retry_on_error(
|
49
|
+
times: retry_times,
|
50
|
+
interval: retry_interval,
|
51
|
+
exponential_backoff: retry_exponential_backoff
|
52
|
+
) { query value }
|
53
|
+
end.to_result
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# @param [String] value
|
58
|
+
#
|
59
|
+
def query(value)
|
30
60
|
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
31
61
|
end
|
62
|
+
|
63
|
+
class << self
|
64
|
+
def inherited(child)
|
65
|
+
super
|
66
|
+
Mihari.enrichers << child
|
67
|
+
end
|
68
|
+
end
|
32
69
|
end
|
33
70
|
end
|
34
71
|
end
|
@@ -5,52 +5,43 @@ require "net/https"
|
|
5
5
|
module Mihari
|
6
6
|
module Enrichers
|
7
7
|
class GooglePublicDNS < Base
|
8
|
-
#
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
#
|
19
|
-
# @param [String] name
|
20
|
-
#
|
21
|
-
# @return [Array<Mihari::Structs::Shodan::GooglePublicDNS::Response>]
|
22
|
-
#
|
23
|
-
def query(name)
|
24
|
-
%w[A AAAA CNAME TXT NS].filter_map do |resource_type|
|
25
|
-
query_by_type(name, resource_type)
|
26
|
-
end
|
8
|
+
#
|
9
|
+
# Query Google Public DNS
|
10
|
+
#
|
11
|
+
# @param [String] name
|
12
|
+
#
|
13
|
+
# @return [Array<Mihari::Structs::Shodan::GooglePublicDNS::Response>]
|
14
|
+
#
|
15
|
+
def query(name)
|
16
|
+
%w[A AAAA CNAME TXT NS].filter_map do |resource_type|
|
17
|
+
query_by_type(name, resource_type)
|
27
18
|
end
|
19
|
+
end
|
28
20
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
21
|
+
#
|
22
|
+
# Query Google Public DNS by resource type
|
23
|
+
#
|
24
|
+
# @param [String] name
|
25
|
+
# @param [String] resource_type
|
26
|
+
#
|
27
|
+
# @return [Mihari::Structs::Shodan::GooglePublicDNS::Response, nil]
|
28
|
+
#
|
29
|
+
def query_by_type(name, resource_type)
|
30
|
+
url = "https://dns.google/resolve"
|
31
|
+
params = { name: name, type: resource_type }
|
32
|
+
res = http.get(url, params: params)
|
33
|
+
|
34
|
+
data = JSON.parse(res.body.to_s)
|
35
|
+
|
36
|
+
Structs::GooglePublicDNS::Response.from_dynamic! data
|
37
|
+
rescue HTTPError
|
38
|
+
nil
|
39
|
+
end
|
48
40
|
|
49
|
-
|
41
|
+
private
|
50
42
|
|
51
|
-
|
52
|
-
|
53
|
-
end
|
43
|
+
def http
|
44
|
+
HTTP::Factory.build timeout: timeout
|
54
45
|
end
|
55
46
|
end
|
56
47
|
end
|
@@ -5,9 +5,15 @@ require "net/https"
|
|
5
5
|
module Mihari
|
6
6
|
module Enrichers
|
7
7
|
class IPInfo < Base
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
include Memist::Memoizable
|
9
|
+
|
10
|
+
# @return [String, nil]
|
11
|
+
attr_reader :api_key
|
12
|
+
|
13
|
+
def initialize(options: nil, api_key: nil)
|
14
|
+
@api_key = api_key || Mihari.config.ipinfo_api_key
|
15
|
+
|
16
|
+
super(options: options)
|
11
17
|
end
|
12
18
|
|
13
19
|
private
|
@@ -16,37 +22,29 @@ module Mihari
|
|
16
22
|
%w[ipinfo_api_key]
|
17
23
|
end
|
18
24
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
token = Mihari.config.ipinfo_api_key
|
43
|
-
authorization = token.nil? ? nil : "Bearer #{token}"
|
44
|
-
{ authorization: authorization }.compact
|
45
|
-
end
|
46
|
-
|
47
|
-
def http
|
48
|
-
HTTP::Factory.build headers: headers
|
49
|
-
end
|
25
|
+
#
|
26
|
+
# Query IPInfo
|
27
|
+
#
|
28
|
+
# @param [String] ip
|
29
|
+
#
|
30
|
+
# @return [Mihari::Structs::IPInfo::Response, nil]
|
31
|
+
#
|
32
|
+
def query(ip)
|
33
|
+
url = "https://ipinfo.io/#{ip}/json"
|
34
|
+
res = http.get(url)
|
35
|
+
data = JSON.parse(res.body.to_s)
|
36
|
+
|
37
|
+
Structs::IPInfo::Response.from_dynamic! data
|
38
|
+
end
|
39
|
+
memoize :query
|
40
|
+
|
41
|
+
def headers
|
42
|
+
authorization = api_key.nil? ? nil : "Bearer #{api_key}"
|
43
|
+
{ authorization: authorization }.compact
|
44
|
+
end
|
45
|
+
|
46
|
+
def http
|
47
|
+
HTTP::Factory.build headers: headers, timeout: timeout
|
50
48
|
end
|
51
49
|
end
|
52
50
|
end
|
@@ -5,36 +5,28 @@ require "net/https"
|
|
5
5
|
module Mihari
|
6
6
|
module Enrichers
|
7
7
|
class Shodan < Base
|
8
|
-
|
9
|
-
def valid?
|
10
|
-
true
|
11
|
-
end
|
12
|
-
|
13
|
-
class << self
|
14
|
-
include Dry::Monads[:result]
|
15
|
-
include Memist::Memoizable
|
8
|
+
include Memist::Memoizable
|
16
9
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
10
|
+
#
|
11
|
+
# Query Shodan Internet DB
|
12
|
+
#
|
13
|
+
# @param [String] ip
|
14
|
+
#
|
15
|
+
# @return [Mihari::Structs::Shodan::InternetDBResponse, nil]
|
16
|
+
#
|
17
|
+
def query(ip)
|
18
|
+
url = "https://internetdb.shodan.io/#{ip}"
|
19
|
+
res = http.get(url)
|
20
|
+
data = JSON.parse(res.body.to_s)
|
28
21
|
|
29
|
-
|
30
|
-
|
31
|
-
|
22
|
+
Structs::Shodan::InternetDBResponse.from_dynamic! data
|
23
|
+
end
|
24
|
+
memoize :query
|
32
25
|
|
33
|
-
|
26
|
+
private
|
34
27
|
|
35
|
-
|
36
|
-
|
37
|
-
end
|
28
|
+
def http
|
29
|
+
HTTP::Factory.build timeout: timeout
|
38
30
|
end
|
39
31
|
end
|
40
32
|
end
|