mihari 5.7.2 → 6.1.0
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/.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
|