mihari 5.7.2 → 6.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -0
- data/config.ru +2 -0
- data/lib/mihari/actor.rb +1 -1
- data/lib/mihari/analyzers/base.rb +3 -0
- data/lib/mihari/analyzers/dnstwister.rb +2 -4
- data/lib/mihari/analyzers/hunterhow.rb +1 -1
- data/lib/mihari/analyzers/urlscan.rb +1 -4
- data/lib/mihari/cli/main.rb +2 -12
- data/lib/mihari/commands/database.rb +0 -1
- data/lib/mihari/config.rb +5 -1
- data/lib/mihari/database.rb +9 -5
- data/lib/mihari/emitters/misp.rb +2 -2
- data/lib/mihari/emitters/slack.rb +8 -11
- data/lib/mihari/emitters/the_hive.rb +5 -9
- data/lib/mihari/enrichers/base.rb +2 -0
- data/lib/mihari/enrichers/google_public_dns.rb +2 -7
- data/lib/mihari/enrichers/ipinfo.rb +2 -3
- data/lib/mihari/enrichers/shodan.rb +2 -3
- data/lib/mihari/enrichers/whois.rb +11 -20
- data/lib/mihari/entities/artifact.rb +1 -0
- data/lib/mihari/mixins/falsepositive.rb +2 -2
- data/lib/mihari/mixins/refang.rb +1 -4
- data/lib/mihari/mixins/unwrap_error.rb +27 -0
- data/lib/mihari/models/alert.rb +1 -3
- data/lib/mihari/models/artifact.rb +18 -12
- data/lib/mihari/models/rule.rb +1 -2
- data/lib/mihari/rule.rb +14 -10
- data/lib/mihari/service.rb +2 -0
- data/lib/mihari/services/rule_builder.rb +2 -4
- data/lib/mihari/structs/fofa.rb +2 -0
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/app.rb +5 -3
- data/lib/mihari/web/endpoints/alerts.rb +14 -18
- data/lib/mihari/web/endpoints/artifacts.rb +17 -22
- data/lib/mihari/web/endpoints/configs.rb +0 -1
- data/lib/mihari/web/endpoints/ip_addresses.rb +1 -1
- data/lib/mihari/web/endpoints/rules.rb +27 -32
- data/lib/mihari/web/endpoints/tags.rb +7 -9
- data/lib/mihari/web/middleware/connection_adapter.rb +3 -5
- data/lib/mihari/web/middleware/error_notification_adapter.rb +10 -6
- data/lib/mihari/web/public/assets/{index-ec641cb0.js → index-216d49d1.js} +42 -42
- data/lib/mihari/web/public/assets/{index-56fc2187.css → index-4c8509ee.css} +1 -1
- data/lib/mihari/web/public/index.html +2 -2
- data/lib/mihari/web/public/redoc-static.html +29 -49
- data/lib/mihari.rb +9 -10
- data/mihari.gemspec +11 -13
- data/mkdocs.yml +1 -0
- data/requirements.txt +1 -1
- metadata +76 -34
- data/lib/mihari/services/rule_runner.rb +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dc2d39ad14f53cf88ed2c68bb2e2668e9a43f36444099401ef9fbe1347948644
|
4
|
+
data.tar.gz: 2d08fed6ec6f82da36f72a2fb05ae1ede82ba65d6bc9bcdb7c60b8f7e244c71a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 684c4106554f9c11bc7ff7f8633b32c5fa24b4127001415e6d14bd4de73e51a04084eb802a29cac109d8b56c71bc7068c8657f4d6ec7cd53e0519ef353aa859f
|
7
|
+
data.tar.gz: 4a4c3183ca85d47b54f7a98837059a9fae585c2d079fe005336520c62290bc317aa61880695f923a2ad0ad7ba5cf18e3e888990a9394d82b4ac41939f34b3403
|
data/.rubocop.yml
CHANGED
data/config.ru
CHANGED
data/lib/mihari/actor.rb
CHANGED
@@ -60,7 +60,10 @@ module Mihari
|
|
60
60
|
# No need to set data_type manually
|
61
61
|
# It is set automatically in #initialize
|
62
62
|
artifact = artifact.is_a?(Models::Artifact) ? artifact : Models::Artifact.new(data: artifact)
|
63
|
+
|
63
64
|
artifact.source = self.class.class_key
|
65
|
+
artifact.query = query
|
66
|
+
|
64
67
|
artifact
|
65
68
|
end.select(&:valid?).uniq(&:data)
|
66
69
|
end
|
@@ -25,9 +25,7 @@ module Mihari
|
|
25
25
|
raise ValueError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
|
26
26
|
|
27
27
|
domains = client.fuzz(query)
|
28
|
-
Parallel.map(domains)
|
29
|
-
resolvable?(domain) ? domain : nil
|
30
|
-
end.compact
|
28
|
+
Parallel.map(domains) { |domain| resolvable?(domain) ? domain : nil }.compact
|
31
29
|
end
|
32
30
|
|
33
31
|
private
|
@@ -55,7 +53,7 @@ module Mihari
|
|
55
53
|
def resolvable?(domain)
|
56
54
|
Resolv.getaddress domain
|
57
55
|
true
|
58
|
-
rescue Resolv::ResolvError
|
56
|
+
rescue Resolv::ResolvError
|
59
57
|
false
|
60
58
|
end
|
61
59
|
end
|
@@ -22,7 +22,7 @@ module Mihari
|
|
22
22
|
# @param [Hash, nil] options
|
23
23
|
# @param [String, nil] api_key
|
24
24
|
#
|
25
|
-
def initialize(query, start_time
|
25
|
+
def initialize(query, start_time: nil, end_time: nil, options: nil, api_key: nil)
|
26
26
|
super(query, options: options)
|
27
27
|
|
28
28
|
@api_key = api_key || Mihari.config.hunterhow_api_key
|
@@ -34,10 +34,7 @@ module Mihari
|
|
34
34
|
def artifacts
|
35
35
|
# @type [Array<Mihari::Models::Artifact>]
|
36
36
|
artifacts = client.search_with_pagination(query, pagination_limit: pagination_limit).map(&:artifacts).flatten
|
37
|
-
|
38
|
-
artifacts.select do |artifact|
|
39
|
-
allowed_data_types.include? artifact.data_type
|
40
|
-
end
|
37
|
+
artifacts.select { |artifact| allowed_data_types.include? artifact.data_type }
|
41
38
|
end
|
42
39
|
|
43
40
|
def configuration_keys
|
data/lib/mihari/cli/main.rb
CHANGED
@@ -32,19 +32,9 @@ module Mihari
|
|
32
32
|
include Mihari::Commands::Version
|
33
33
|
include Mihari::Commands::Web
|
34
34
|
|
35
|
-
|
36
|
-
def unwrap_error(err)
|
37
|
-
return err unless err.is_a?(Dry::Monads::UnwrapError)
|
38
|
-
|
39
|
-
# NOTE: UnwrapError's receiver can be either of:
|
40
|
-
# - Dry::Monads::Try::Error
|
41
|
-
# - Dry::Monads::Result::Failure
|
42
|
-
receiver = err.receiver
|
43
|
-
return receiver.exception if receiver.is_a?(Dry::Monads::Try::Error)
|
44
|
-
|
45
|
-
receiver.failure
|
46
|
-
end
|
35
|
+
include Mihari::Mixins::UnwrapError
|
47
36
|
|
37
|
+
no_commands do
|
48
38
|
def safe_execute
|
49
39
|
yield
|
50
40
|
rescue StandardError => e
|
data/lib/mihari/config.rb
CHANGED
@@ -45,7 +45,8 @@ module Mihari
|
|
45
45
|
retry_exponential_backoff: true,
|
46
46
|
retry_interval: 5,
|
47
47
|
retry_times: 3,
|
48
|
-
sentry_dsn: nil
|
48
|
+
sentry_dsn: nil,
|
49
|
+
sentry_trace_sample_rate: 0.25
|
49
50
|
)
|
50
51
|
|
51
52
|
# @!attribute [r] binaryedge_api_key
|
@@ -132,6 +133,9 @@ module Mihari
|
|
132
133
|
# @!attribute [r] sentry_dsn
|
133
134
|
# @return [String, nil]
|
134
135
|
|
136
|
+
# @!attribute [r] sentry_trace_sample_rate
|
137
|
+
# @return [Float]
|
138
|
+
|
135
139
|
# @!attribute [r] retry_interval
|
136
140
|
# @return [Integer]
|
137
141
|
|
data/lib/mihari/database.rb
CHANGED
@@ -1,9 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Make possible to use upper case acronyms in class names
|
4
|
-
ActiveSupport::Inflector.inflections(:en)
|
5
|
-
inflect.acronym "CPE"
|
6
|
-
end
|
4
|
+
ActiveSupport::Inflector.inflections(:en) { |inflect| inflect.acronym "CPE" }
|
7
5
|
|
8
6
|
def env
|
9
7
|
ENV["APP_ENV"] || ENV["RACK_ENV"]
|
@@ -113,6 +111,12 @@ class V5Schema < ActiveRecord::Migration[7.1]
|
|
113
111
|
end
|
114
112
|
end
|
115
113
|
|
114
|
+
class V61Schema < ActiveRecord::Migration[7.1]
|
115
|
+
def change
|
116
|
+
add_column :artifacts, :query, :string
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
116
120
|
def adapter
|
117
121
|
return "postgresql" if %w[postgresql postgres].include?(Mihari.config.database_url.scheme)
|
118
122
|
return "mysql2" if Mihari.config.database_url.scheme == "mysql2"
|
@@ -124,7 +128,7 @@ end
|
|
124
128
|
# @return [Array<ActiveRecord::Migration>] schemas
|
125
129
|
#
|
126
130
|
def schemas
|
127
|
-
[V5Schema]
|
131
|
+
[V5Schema, V61Schema]
|
128
132
|
end
|
129
133
|
|
130
134
|
module Mihari
|
@@ -175,7 +179,7 @@ module Mihari
|
|
175
179
|
Mihari::Database.connect
|
176
180
|
yield
|
177
181
|
rescue ActiveRecord::StatementInvalid
|
178
|
-
Mihari.logger.error("
|
182
|
+
Mihari.logger.error("The DB migration is not yet complete. Please run 'mihari db migrate'.")
|
179
183
|
ensure
|
180
184
|
Mihari::Database.close
|
181
185
|
end
|
data/lib/mihari/emitters/misp.rb
CHANGED
@@ -6,7 +6,7 @@ require "slack-notifier"
|
|
6
6
|
module Mihari
|
7
7
|
module Emitters
|
8
8
|
class Attachment
|
9
|
-
|
9
|
+
prepend MemoWise
|
10
10
|
|
11
11
|
# @return [String]
|
12
12
|
attr_reader :data
|
@@ -76,7 +76,7 @@ module Mihari
|
|
76
76
|
"https://urlscan.io/domain/#{uri.hostname}"
|
77
77
|
end
|
78
78
|
end
|
79
|
-
|
79
|
+
memo_wise :_urlscan_link
|
80
80
|
|
81
81
|
# @return [String, nil]
|
82
82
|
def _vt_link
|
@@ -93,19 +93,19 @@ module Mihari
|
|
93
93
|
"https://www.virustotal.com/#/search/#{data}"
|
94
94
|
end
|
95
95
|
end
|
96
|
-
|
96
|
+
memo_wise :_vt_link
|
97
97
|
|
98
98
|
# @return [String, nil]
|
99
99
|
def _censys_link
|
100
100
|
(data_type == "ip") ? "https://search.censys.io/hosts/#{data}" : nil
|
101
101
|
end
|
102
|
-
|
102
|
+
memo_wise :_censys_link
|
103
103
|
|
104
104
|
# @return [String, nil]
|
105
105
|
def _shodan_link
|
106
106
|
(data_type == "ip") ? "https://www.shodan.io/host/#{data}" : nil
|
107
107
|
end
|
108
|
-
|
108
|
+
memo_wise :_shodan_link
|
109
109
|
|
110
110
|
# @return [String]
|
111
111
|
def sha256
|
@@ -192,9 +192,7 @@ module Mihari
|
|
192
192
|
# @return [Array<Mihari::Emitters::Attachment>]
|
193
193
|
#
|
194
194
|
def attachments
|
195
|
-
artifacts.map
|
196
|
-
Attachment.new(data: artifact.data, data_type: artifact.data_type).to_a
|
197
|
-
end.flatten
|
195
|
+
artifacts.map { |artifact| Attachment.new(data: artifact.data, data_type: artifact.data_type).to_a }.flatten
|
198
196
|
end
|
199
197
|
|
200
198
|
#
|
@@ -205,7 +203,6 @@ module Mihari
|
|
205
203
|
def text
|
206
204
|
tags = rule.tags
|
207
205
|
tags = ["N/A"] if tags.empty?
|
208
|
-
|
209
206
|
[
|
210
207
|
"*#{rule.title}*",
|
211
208
|
"*Desc.*: #{rule.description}",
|
@@ -217,10 +214,10 @@ module Mihari
|
|
217
214
|
# @param [Array<Mihari::Models::Artifact>] artifacts
|
218
215
|
#
|
219
216
|
def call(artifacts)
|
220
|
-
return if artifacts.empty?
|
221
|
-
|
222
217
|
@artifacts = artifacts
|
223
218
|
|
219
|
+
return if artifacts.empty?
|
220
|
+
|
224
221
|
notifier.post(text: text, attachments: attachments, mrkdwn: true)
|
225
222
|
end
|
226
223
|
|
@@ -43,10 +43,10 @@ module Mihari
|
|
43
43
|
# @param [Array<Mihari::Models::Artifact>] artifacts
|
44
44
|
#
|
45
45
|
def call(artifacts)
|
46
|
-
return if artifacts.empty?
|
47
|
-
|
48
46
|
@artifacts = artifacts
|
49
47
|
|
48
|
+
return if artifacts.empty?
|
49
|
+
|
50
50
|
client.alert payload
|
51
51
|
end
|
52
52
|
|
@@ -61,21 +61,17 @@ module Mihari
|
|
61
61
|
@normalized_api_version ||= [].tap do |out|
|
62
62
|
# v4 does not have version prefix in path (/api/)
|
63
63
|
# v5 has version prefix in path (/api/v1/)
|
64
|
-
table = {
|
65
|
-
"" => nil,
|
66
|
-
"v4" => nil,
|
67
|
-
"v5" => "v1"
|
68
|
-
}
|
64
|
+
table = { "" => nil, "v4" => nil, "v5" => "v1" }
|
69
65
|
out << table[api_version.to_s.downcase]
|
70
66
|
end.first
|
71
67
|
end
|
72
68
|
|
73
|
-
private
|
74
|
-
|
75
69
|
def configuration_keys
|
76
70
|
%w[thehive_url thehive_api_key]
|
77
71
|
end
|
78
72
|
|
73
|
+
private
|
74
|
+
|
79
75
|
def client
|
80
76
|
@client ||= Clients::TheHive.new(url, api_key: api_key, api_version: normalized_api_version, timeout: timeout)
|
81
77
|
end
|
@@ -14,9 +14,7 @@ module Mihari
|
|
14
14
|
# @return [Array<Mihari::Structs::GooglePublicDNS::Response>]
|
15
15
|
#
|
16
16
|
def call(name)
|
17
|
-
%w[A AAAA CNAME TXT NS].filter_map
|
18
|
-
query_by_type(name, resource_type)
|
19
|
-
end
|
17
|
+
%w[A AAAA CNAME TXT NS].filter_map { |resource_type| query_by_type(name, resource_type) }
|
20
18
|
end
|
21
19
|
|
22
20
|
#
|
@@ -31,10 +29,7 @@ module Mihari
|
|
31
29
|
url = "https://dns.google/resolve"
|
32
30
|
params = { name: name, type: resource_type }
|
33
31
|
res = http.get(url, params: params)
|
34
|
-
|
35
|
-
data = JSON.parse(res.body.to_s)
|
36
|
-
|
37
|
-
Structs::GooglePublicDNS::Response.from_dynamic! data
|
32
|
+
Structs::GooglePublicDNS::Response.from_dynamic! JSON.parse(res.body.to_s)
|
38
33
|
rescue HTTPError
|
39
34
|
nil
|
40
35
|
end
|
@@ -33,10 +33,9 @@ module Mihari
|
|
33
33
|
def call(ip)
|
34
34
|
url = "https://ipinfo.io/#{ip}/json"
|
35
35
|
res = http.get(url)
|
36
|
-
|
37
|
-
|
38
|
-
Structs::IPInfo::Response.from_dynamic! data
|
36
|
+
Structs::IPInfo::Response.from_dynamic! JSON.parse(res.body.to_s)
|
39
37
|
end
|
38
|
+
memo_wise :call
|
40
39
|
|
41
40
|
private
|
42
41
|
|
@@ -16,10 +16,9 @@ module Mihari
|
|
16
16
|
def call(ip)
|
17
17
|
url = "https://internetdb.shodan.io/#{ip}"
|
18
18
|
res = http.get(url)
|
19
|
-
|
20
|
-
|
21
|
-
Structs::Shodan::InternetDBResponse.from_dynamic! data
|
19
|
+
Structs::Shodan::InternetDBResponse.from_dynamic! JSON.parse(res.body.to_s)
|
22
20
|
end
|
21
|
+
memo_wise :call
|
23
22
|
|
24
23
|
private
|
25
24
|
|
@@ -8,16 +8,11 @@ module Mihari
|
|
8
8
|
# Whois enricher
|
9
9
|
#
|
10
10
|
class Whois < Base
|
11
|
-
# @return [Hash]
|
12
|
-
attr_accessor :memo
|
13
|
-
|
14
11
|
#
|
15
12
|
# @param [Hash, nil] options
|
16
13
|
#
|
17
14
|
def initialize(options: nil)
|
18
15
|
super(options: options)
|
19
|
-
|
20
|
-
@memo = {}
|
21
16
|
end
|
22
17
|
|
23
18
|
#
|
@@ -28,16 +23,22 @@ module Mihari
|
|
28
23
|
# @return [Mihari::Models::WhoisRecord, nil]
|
29
24
|
#
|
30
25
|
def call(domain)
|
31
|
-
|
26
|
+
_call PublicSuffix.domain(domain)
|
27
|
+
end
|
32
28
|
|
33
|
-
|
34
|
-
return memo[domain].dup if memo.key?(domain)
|
29
|
+
private
|
35
30
|
|
31
|
+
#
|
32
|
+
# @param [String] domain
|
33
|
+
#
|
34
|
+
# @return [Mihari::Models::WhoisRecord, nil]
|
35
|
+
#
|
36
|
+
def _call(domain)
|
36
37
|
record = whois.lookup(domain)
|
37
38
|
parser = record.parser
|
38
39
|
return nil if parser.available?
|
39
40
|
|
40
|
-
|
41
|
+
Models::WhoisRecord.new(
|
41
42
|
domain: domain,
|
42
43
|
created_on: get_created_on(parser),
|
43
44
|
updated_on: get_updated_on(parser),
|
@@ -45,18 +46,8 @@ module Mihari
|
|
45
46
|
registrar: get_registrar(parser),
|
46
47
|
contacts: get_contacts(parser)
|
47
48
|
)
|
48
|
-
|
49
|
-
# set memo
|
50
|
-
memo[domain] = whois_record
|
51
|
-
|
52
|
-
whois_record
|
53
49
|
end
|
54
|
-
|
55
|
-
def reset_cache
|
56
|
-
@memo = {}
|
57
|
-
end
|
58
|
-
|
59
|
-
private
|
50
|
+
memo_wise :_call
|
60
51
|
|
61
52
|
#
|
62
53
|
# @return [::Whois::Client]
|
@@ -11,6 +11,7 @@ module Mihari
|
|
11
11
|
expose :data, documentation: { type: String, required: true }
|
12
12
|
expose :data_type, documentation: { type: String, required: true }, as: :dataType
|
13
13
|
expose :source, documentation: { type: String, required: true }
|
14
|
+
expose :query, documentation: { type: String, required: false }
|
14
15
|
expose :tags, documentation: { type: String, is_array: true }
|
15
16
|
end
|
16
17
|
|
@@ -6,7 +6,7 @@ module Mihari
|
|
6
6
|
# False positive mixins
|
7
7
|
#
|
8
8
|
module FalsePositive
|
9
|
-
|
9
|
+
prepend MemoWise
|
10
10
|
|
11
11
|
#
|
12
12
|
# Normalize a falsepositive value
|
@@ -22,7 +22,7 @@ module Mihari
|
|
22
22
|
value_without_slashes = value[1..-2]
|
23
23
|
Regexp.compile value_without_slashes.to_s
|
24
24
|
end
|
25
|
-
|
25
|
+
memo_wise :normalize_falsepositive
|
26
26
|
|
27
27
|
#
|
28
28
|
# Check whether a value is valid format as a disallowed data value
|
data/lib/mihari/mixins/refang.rb
CHANGED
@@ -14,10 +14,7 @@ module Mihari
|
|
14
14
|
# @return [String]
|
15
15
|
#
|
16
16
|
def refang(indicator)
|
17
|
-
|
18
|
-
|
19
|
-
# for RSpec & Ruby 2.7
|
20
|
-
indicator
|
17
|
+
indicator.gsub("[.]", ".").gsub("(.)", ".")
|
21
18
|
end
|
22
19
|
end
|
23
20
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mihari
|
4
|
+
module Mixins
|
5
|
+
#
|
6
|
+
# Unwrap error mixins
|
7
|
+
#
|
8
|
+
module UnwrapError
|
9
|
+
def unwrap_error(err)
|
10
|
+
return err unless err.is_a?(Dry::Monads::UnwrapError)
|
11
|
+
|
12
|
+
# NOTE: UnwrapError's receiver can be either of:
|
13
|
+
# - Dry::Monads::Try::Error
|
14
|
+
# - Dry::Monads::Result::Failure
|
15
|
+
receiver = err.receiver
|
16
|
+
case receiver
|
17
|
+
when Dry::Monads::Try::Error
|
18
|
+
receiver.exception
|
19
|
+
when Dry::Monads::Failure
|
20
|
+
receiver.failure
|
21
|
+
else
|
22
|
+
err
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/mihari/models/alert.rb
CHANGED
@@ -30,7 +30,6 @@ module Mihari
|
|
30
30
|
offset = (page - 1) * limit
|
31
31
|
|
32
32
|
relation = build_relation(filter.without_pagination)
|
33
|
-
|
34
33
|
alert_ids = relation.limit(limit).offset(offset).order(id: :desc).pluck(:id).uniq
|
35
34
|
eager_load(:artifacts, :tags).where(id: [alert_ids]).order(id: :desc)
|
36
35
|
end
|
@@ -75,8 +74,7 @@ module Mihari
|
|
75
74
|
def build_relation(filter)
|
76
75
|
artifact_ids = get_artifact_ids_by_filter(filter)
|
77
76
|
|
78
|
-
relation =
|
79
|
-
relation = relation.includes(:artifacts, :tags)
|
77
|
+
relation = includes(:artifacts, :tags)
|
80
78
|
|
81
79
|
relation = relation.where(artifacts: { id: artifact_ids }) unless artifact_ids.empty?
|
82
80
|
relation = relation.where(tags: { name: filter.tag_name }) if filter.tag_name
|
@@ -78,7 +78,7 @@ module Mihari
|
|
78
78
|
end
|
79
79
|
|
80
80
|
#
|
81
|
-
# Enrich
|
81
|
+
# Enrich whois record
|
82
82
|
#
|
83
83
|
# @param [Mihari::Enrichers::Whois] enricher
|
84
84
|
#
|
@@ -89,7 +89,7 @@ module Mihari
|
|
89
89
|
end
|
90
90
|
|
91
91
|
#
|
92
|
-
# Enrich
|
92
|
+
# Enrich DNS records
|
93
93
|
#
|
94
94
|
# @param [Mihari::Enrichers::GooglePublicDNS] enricher
|
95
95
|
#
|
@@ -100,7 +100,7 @@ module Mihari
|
|
100
100
|
end
|
101
101
|
|
102
102
|
#
|
103
|
-
# Enrich
|
103
|
+
# Enrich reverse DNS names
|
104
104
|
#
|
105
105
|
# @param [Mihari::Enrichers::Shodan] enricher
|
106
106
|
#
|
@@ -111,7 +111,7 @@ module Mihari
|
|
111
111
|
end
|
112
112
|
|
113
113
|
#
|
114
|
-
# Enrich
|
114
|
+
# Enrich geolocation
|
115
115
|
#
|
116
116
|
# @param [Mihari::Enrichers::IPInfo] enricher
|
117
117
|
#
|
@@ -158,13 +158,13 @@ module Mihari
|
|
158
158
|
# Enrich all the enrichable relationships of the artifact
|
159
159
|
#
|
160
160
|
def enrich_all
|
161
|
-
enrich_autonomous_system
|
161
|
+
enrich_autonomous_system ipinfo
|
162
162
|
enrich_dns
|
163
|
-
enrich_geolocation
|
164
|
-
enrich_reverse_dns
|
163
|
+
enrich_geolocation ipinfo
|
164
|
+
enrich_reverse_dns shodan
|
165
165
|
enrich_whois
|
166
|
-
enrich_ports
|
167
|
-
enrich_cpes
|
166
|
+
enrich_ports shodan
|
167
|
+
enrich_cpes shodan
|
168
168
|
end
|
169
169
|
|
170
170
|
ENRICH_METHODS_BY_ENRICHER = {
|
@@ -192,13 +192,19 @@ module Mihari
|
|
192
192
|
#
|
193
193
|
def enrich_by_enricher(enricher)
|
194
194
|
methods = ENRICH_METHODS_BY_ENRICHER[enricher.class] || []
|
195
|
-
methods.each
|
196
|
-
send(method, enricher) if respond_to?(method)
|
197
|
-
end
|
195
|
+
methods.each { |method| send(method, enricher) if respond_to?(method) }
|
198
196
|
end
|
199
197
|
|
200
198
|
private
|
201
199
|
|
200
|
+
def ipinfo
|
201
|
+
@ipinfo ||= Enrichers::IPInfo.new
|
202
|
+
end
|
203
|
+
|
204
|
+
def shodan
|
205
|
+
@shodan ||= Enrichers::Shodan.new
|
206
|
+
end
|
207
|
+
|
202
208
|
def normalize_as_domain(url_or_domain)
|
203
209
|
return url_or_domain if data_type == "domain"
|
204
210
|
|
data/lib/mihari/models/rule.rb
CHANGED
@@ -66,8 +66,7 @@ module Mihari
|
|
66
66
|
# @return [Mihari::Models::Rule]
|
67
67
|
#
|
68
68
|
def build_relation(filter)
|
69
|
-
relation =
|
70
|
-
relation = relation.includes(alerts: :tags)
|
69
|
+
relation = includes(alerts: :tags)
|
71
70
|
|
72
71
|
relation = relation.where(alerts: { tags: { name: filter.tag_name } }) if filter.tag_name
|
73
72
|
|
data/lib/mihari/rule.rb
CHANGED
@@ -113,15 +113,15 @@ module Mihari
|
|
113
113
|
analyzers.flat_map do |analyzer|
|
114
114
|
# @type [Dry::Monads::Result::Success<Array<Mihari::Models::Artifact>>, Dry::Monads::Result::Failure]
|
115
115
|
result = analyzer.result
|
116
|
-
|
117
|
-
|
118
|
-
raise result.failure unless analyzer.ignore_error?
|
119
|
-
else
|
116
|
+
case result
|
117
|
+
when Success
|
120
118
|
artifacts = result.value!
|
121
119
|
artifacts.map do |artifact|
|
122
120
|
artifact.rule_id = id
|
123
121
|
artifact
|
124
122
|
end
|
123
|
+
else
|
124
|
+
raise result.failure unless analyzer.ignore_error?
|
125
125
|
end
|
126
126
|
end.compact
|
127
127
|
end
|
@@ -177,8 +177,14 @@ module Mihari
|
|
177
177
|
results = Parallel.map(emitters) { |emitter| emitter.result enriched_artifacts }
|
178
178
|
results.zip(emitters).map do |result_and_emitter|
|
179
179
|
result, emitter = result_and_emitter
|
180
|
-
|
181
|
-
|
180
|
+
|
181
|
+
case result
|
182
|
+
when Success
|
183
|
+
Mihari.logger.info "Emission by #{emitter.class} succeed"
|
184
|
+
else
|
185
|
+
Mihari.logger.info "Emission by #{emitter.class} failed: #{result.failure}"
|
186
|
+
end
|
187
|
+
|
182
188
|
result.value_or nil
|
183
189
|
end.compact
|
184
190
|
end
|
@@ -289,8 +295,7 @@ module Mihari
|
|
289
295
|
@analyzers ||= queries.map do |query_params|
|
290
296
|
analyzer_name = query_params[:analyzer]
|
291
297
|
klass = get_analyzer_class(analyzer_name)
|
292
|
-
klass.from_query(query_params)
|
293
|
-
end.map do |analyzer|
|
298
|
+
analyzer = klass.from_query(query_params)
|
294
299
|
analyzer.validate_configuration!
|
295
300
|
analyzer
|
296
301
|
end
|
@@ -320,8 +325,7 @@ module Mihari
|
|
320
325
|
%i[emitter options].each { |key| params.delete key }
|
321
326
|
|
322
327
|
klass = get_emitter_class(name)
|
323
|
-
klass.new(rule: self, options: options, **params)
|
324
|
-
end.map do |emitter|
|
328
|
+
emitter = klass.new(rule: self, options: options, **params)
|
325
329
|
emitter.validate_configuration!
|
326
330
|
emitter
|
327
331
|
end
|