mihari 6.1.0 → 6.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/mihari/actor.rb +3 -5
- data/lib/mihari/analyzers/base.rb +7 -3
- data/lib/mihari/analyzers/circl.rb +1 -1
- data/lib/mihari/analyzers/dnstwister.rb +1 -1
- data/lib/mihari/analyzers/otx.rb +1 -1
- data/lib/mihari/analyzers/passivetotal.rb +1 -1
- data/lib/mihari/analyzers/pulsedive.rb +1 -1
- data/lib/mihari/analyzers/securitytrails.rb +1 -1
- data/lib/mihari/analyzers/virustotal.rb +1 -1
- data/lib/mihari/clients/google_public_dns.rb +31 -0
- data/lib/mihari/config.rb +5 -1
- data/lib/mihari/{type_checker.rb → data_type.rb} +32 -37
- data/lib/mihari/database.rb +1 -3
- data/lib/mihari/enrichers/google_public_dns.rb +4 -21
- data/lib/mihari/entities/artifact.rb +8 -0
- data/lib/mihari/models/alert.rb +4 -27
- data/lib/mihari/models/artifact.rb +65 -3
- data/lib/mihari/models/dns.rb +3 -8
- data/lib/mihari/models/rule.rb +2 -5
- data/lib/mihari/rule.rb +20 -7
- data/lib/mihari/schemas/options.rb +5 -1
- data/lib/mihari/structs/filters.rb +53 -9
- data/lib/mihari/structs/google_public_dns.rb +4 -8
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/endpoints/alerts.rb +2 -10
- data/lib/mihari/web/endpoints/artifacts.rb +64 -0
- data/lib/mihari/web/endpoints/exports.rb +0 -0
- data/lib/mihari/web/endpoints/rules.rb +1 -8
- data/lib/mihari/web/public/assets/index-81613_nX.js +1763 -0
- data/lib/mihari/web/public/assets/index-Wv6xUrTI.css +1 -0
- data/lib/mihari/web/public/index.html +2 -3
- data/lib/mihari/web/public/redoc-static.html +20 -16
- data/lib/mihari.rb +2 -1
- data/mihari.gemspec +8 -7
- data/requirements.txt +1 -1
- metadata +30 -56
- data/lib/mihari/web/public/assets/index-216d49d1.js +0 -1750
- data/lib/mihari/web/public/assets/index-4c8509ee.css +0 -1
- /data/lib/mihari/web/public/assets/{mode-yaml-24faa242.js → mode-yaml-BC4MIiYj.js} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f027c5c24759f4e09d5dad277df4cad58a1705478eba1f3b9b6b354a896a29a5
|
4
|
+
data.tar.gz: a908c432c313bab4f4af70b0b32fb1f2e65c9f40926dc3099c4869330080005c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8110df87f854b6e06ea8477c94c6563b2450dae4585738f256da9c94203d14c6fb3ce11e775a470dae04d4d3934549ae71068e6b3f3d3eca48ea3689d87a4cd4
|
7
|
+
data.tar.gz: 375855d7dba59d13ff894472f5f6d5ccc82f36de73aeb12c4144c05863c64283f2ff451dd4531643ba3ddbb218181937d57da1a42bb28970f78170e3fae5dfbf
|
data/lib/mihari/actor.rb
CHANGED
@@ -65,11 +65,9 @@ module Mihari
|
|
65
65
|
|
66
66
|
def result(...)
|
67
67
|
Try[StandardError] do
|
68
|
-
retry_on_error(
|
69
|
-
|
70
|
-
|
71
|
-
exponential_backoff: retry_exponential_backoff
|
72
|
-
) { call(...) }
|
68
|
+
retry_on_error(times: retry_times, interval: retry_interval, exponential_backoff: retry_exponential_backoff) do
|
69
|
+
call(...)
|
70
|
+
end
|
73
71
|
end.to_result
|
74
72
|
end
|
75
73
|
|
@@ -37,10 +37,14 @@ module Mihari
|
|
37
37
|
# @return [Boolean]
|
38
38
|
#
|
39
39
|
def ignore_error?
|
40
|
-
|
41
|
-
|
40
|
+
options[:ignore_error] || Mihari.config.ignore_error
|
41
|
+
end
|
42
42
|
|
43
|
-
|
43
|
+
#
|
44
|
+
# @return [Boolean]
|
45
|
+
#
|
46
|
+
def parallel?
|
47
|
+
options[:parallel] || Mihari.config.parallel
|
44
48
|
end
|
45
49
|
|
46
50
|
# @return [Array<String>, Array<Mihari::Models::Artifact>]
|
@@ -26,7 +26,7 @@ module Mihari
|
|
26
26
|
def initialize(query, options: nil, username: nil, password: nil)
|
27
27
|
super(refang(query), options: options)
|
28
28
|
|
29
|
-
@type =
|
29
|
+
@type = DataType.type(query)
|
30
30
|
|
31
31
|
@username = username || Mihari.config.circl_passive_username
|
32
32
|
@password = password || Mihari.config.circl_passive_password
|
data/lib/mihari/analyzers/otx.rb
CHANGED
@@ -26,7 +26,7 @@ module Mihari
|
|
26
26
|
def initialize(query, options: nil, api_key: nil, username: nil)
|
27
27
|
super(refang(query), options: options)
|
28
28
|
|
29
|
-
@type =
|
29
|
+
@type = DataType.type(query)
|
30
30
|
|
31
31
|
@username = username || Mihari.config.passivetotal_username
|
32
32
|
@api_key = api_key || Mihari.config.passivetotal_api_key
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mihari
|
4
|
+
module Clients
|
5
|
+
#
|
6
|
+
# Google Public DNS enricher
|
7
|
+
#
|
8
|
+
class GooglePublicDNS < Base
|
9
|
+
#
|
10
|
+
# @param [String] base_url
|
11
|
+
# @param [Hash] headers
|
12
|
+
# @param [Integer, nil] timeout
|
13
|
+
#
|
14
|
+
def initialize(base_url = "https://dns.google", headers: {}, timeout: nil)
|
15
|
+
super(base_url, headers: headers, timeout: timeout)
|
16
|
+
end
|
17
|
+
|
18
|
+
#
|
19
|
+
# Query Google Public DNS by resource type
|
20
|
+
#
|
21
|
+
# @param [String] name
|
22
|
+
#
|
23
|
+
# @return [Mihari::Structs::GooglePublicDNS::Response, nil]
|
24
|
+
#
|
25
|
+
def query_all(name)
|
26
|
+
Structs::GooglePublicDNS::Response.from_dynamic! get_json("/resolve",
|
27
|
+
params: { name: name, type: "ALL" })
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/mihari/config.rb
CHANGED
@@ -42,6 +42,7 @@ module Mihari
|
|
42
42
|
ignore_error: false,
|
43
43
|
pagination_interval: 0,
|
44
44
|
pagination_limit: 100,
|
45
|
+
parallel: false,
|
45
46
|
retry_exponential_backoff: true,
|
46
47
|
retry_interval: 5,
|
47
48
|
retry_times: 3,
|
@@ -62,7 +63,7 @@ module Mihari
|
|
62
63
|
# @return [String, nil]
|
63
64
|
|
64
65
|
# @!attribute [r] database_url
|
65
|
-
# @return [
|
66
|
+
# @return [URI, nil]
|
66
67
|
|
67
68
|
# @!attribute [r] fofa_api_key
|
68
69
|
# @return [String, nil]
|
@@ -151,6 +152,9 @@ module Mihari
|
|
151
152
|
# @!attribute [r] pagination_limit
|
152
153
|
# @return [Integer]
|
153
154
|
|
155
|
+
# @!attribute [r] parallel
|
156
|
+
# @return [Boolean]
|
157
|
+
|
154
158
|
# @!attribute [r] ignore_error
|
155
159
|
# @return [Boolean]
|
156
160
|
|
@@ -2,9 +2,11 @@
|
|
2
2
|
|
3
3
|
module Mihari
|
4
4
|
#
|
5
|
-
# Artifact
|
5
|
+
# (Artifact) Data Type
|
6
6
|
#
|
7
|
-
class
|
7
|
+
class DataType
|
8
|
+
include Dry::Monads[:try]
|
9
|
+
|
8
10
|
# @return [String]
|
9
11
|
attr_reader :data
|
10
12
|
|
@@ -24,26 +26,25 @@ module Mihari
|
|
24
26
|
|
25
27
|
# @return [Boolean]
|
26
28
|
def ip?
|
27
|
-
IPAddr
|
28
|
-
|
29
|
-
|
30
|
-
false
|
29
|
+
Try[IPAddr::InvalidAddressError] do
|
30
|
+
IPAddr.new(data).to_s == data
|
31
|
+
end.to_result.value_or(false)
|
31
32
|
end
|
32
33
|
|
33
34
|
# @return [Boolean]
|
34
35
|
def domain?
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
false
|
36
|
+
Try[Addressable::URI::InvalidURIError] do
|
37
|
+
uri = Addressable::URI.parse("http://#{data}")
|
38
|
+
uri.host == data && PublicSuffix.valid?(uri.host)
|
39
|
+
end.to_result.value_or(false)
|
39
40
|
end
|
40
41
|
|
41
42
|
# @return [Boolean]
|
42
43
|
def url?
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
false
|
44
|
+
Try[Addressable::URI::InvalidURIError] do
|
45
|
+
uri = Addressable::URI.parse(data)
|
46
|
+
uri.scheme && uri.host && uri.path && PublicSuffix.valid?(uri.host)
|
47
|
+
end.to_result.value_or(false)
|
47
48
|
end
|
48
49
|
|
49
50
|
# @return [Boolean]
|
@@ -53,38 +54,20 @@ module Mihari
|
|
53
54
|
|
54
55
|
# @return [String, nil]
|
55
56
|
def type
|
56
|
-
|
57
|
-
return
|
58
|
-
return "domain" if domain?
|
59
|
-
return "url" if url?
|
57
|
+
found = %i[hash? ip? domain? url? mail?].find { |method| send(method) if respond_to?(method) }
|
58
|
+
return nil if found.nil?
|
60
59
|
|
61
|
-
|
60
|
+
found[...-1].to_s
|
62
61
|
end
|
63
62
|
|
64
63
|
# @return [String, nil]
|
65
64
|
def detailed_type
|
66
|
-
|
67
|
-
return
|
68
|
-
return "sha256" if sha256?
|
69
|
-
return "sha512" if sha512?
|
65
|
+
found = %i[md5? sha1? sha256? sha512?].find { |method| send(method) if respond_to?(method) }
|
66
|
+
return found[...-1].to_s unless found.nil?
|
70
67
|
|
71
68
|
type
|
72
69
|
end
|
73
70
|
|
74
|
-
class << self
|
75
|
-
# @return [String, nil]
|
76
|
-
def type(data)
|
77
|
-
new(data).type
|
78
|
-
end
|
79
|
-
|
80
|
-
# @return [String, nil]
|
81
|
-
def detailed_type(data)
|
82
|
-
new(data).detailed_type
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
private
|
87
|
-
|
88
71
|
# @return [Boolean]
|
89
72
|
def md5?
|
90
73
|
data.match?(/^[A-Fa-f0-9]{32}$/)
|
@@ -104,5 +87,17 @@ module Mihari
|
|
104
87
|
def sha512?
|
105
88
|
data.match?(/^[A-Fa-f0-9]{128}$/)
|
106
89
|
end
|
90
|
+
|
91
|
+
class << self
|
92
|
+
# @return [String, nil]
|
93
|
+
def type(data)
|
94
|
+
new(data).type
|
95
|
+
end
|
96
|
+
|
97
|
+
# @return [String, nil]
|
98
|
+
def detailed_type(data)
|
99
|
+
new(data).detailed_type
|
100
|
+
end
|
101
|
+
end
|
107
102
|
end
|
108
103
|
end
|
data/lib/mihari/database.rb
CHANGED
@@ -154,7 +154,7 @@ module Mihari
|
|
154
154
|
|
155
155
|
case adapter
|
156
156
|
when "postgresql", "mysql2"
|
157
|
-
ActiveRecord::Base.establish_connection
|
157
|
+
ActiveRecord::Base.establish_connection Mihari.config.database_url.to_s
|
158
158
|
else
|
159
159
|
ActiveRecord::Base.establish_connection(
|
160
160
|
adapter: adapter,
|
@@ -162,8 +162,6 @@ module Mihari
|
|
162
162
|
)
|
163
163
|
end
|
164
164
|
ActiveRecord::Base.logger = Logger.new($stdout) if development_env?
|
165
|
-
rescue StandardError => e
|
166
|
-
Mihari.logger.error e
|
167
165
|
end
|
168
166
|
|
169
167
|
#
|
@@ -11,27 +11,10 @@ module Mihari
|
|
11
11
|
#
|
12
12
|
# @param [String] name
|
13
13
|
#
|
14
|
-
# @return [
|
14
|
+
# @return [Mihari::Structs::GooglePublicDNS::Response]
|
15
15
|
#
|
16
16
|
def call(name)
|
17
|
-
|
18
|
-
end
|
19
|
-
|
20
|
-
#
|
21
|
-
# Query Google Public DNS by resource type
|
22
|
-
#
|
23
|
-
# @param [String] name
|
24
|
-
# @param [String] resource_type
|
25
|
-
#
|
26
|
-
# @return [Mihari::Structs::GooglePublicDNS::Response, nil]
|
27
|
-
#
|
28
|
-
def query_by_type(name, resource_type)
|
29
|
-
url = "https://dns.google/resolve"
|
30
|
-
params = { name: name, type: resource_type }
|
31
|
-
res = http.get(url, params: params)
|
32
|
-
Structs::GooglePublicDNS::Response.from_dynamic! JSON.parse(res.body.to_s)
|
33
|
-
rescue HTTPError
|
34
|
-
nil
|
17
|
+
client.query_all name
|
35
18
|
end
|
36
19
|
|
37
20
|
class << self
|
@@ -45,8 +28,8 @@ module Mihari
|
|
45
28
|
|
46
29
|
private
|
47
30
|
|
48
|
-
def
|
49
|
-
|
31
|
+
def client
|
32
|
+
Clients::GooglePublicDNS.new
|
50
33
|
end
|
51
34
|
end
|
52
35
|
end
|
@@ -42,5 +42,13 @@ module Mihari
|
|
42
42
|
status.ports.empty? ? nil : status.ports
|
43
43
|
end
|
44
44
|
end
|
45
|
+
|
46
|
+
class ArtifactsWithPagination < Grape::Entity
|
47
|
+
expose :artifacts, using: Entities::Artifact,
|
48
|
+
documentation: { type: Entities::Artifact, is_array: true, required: true }
|
49
|
+
expose :total, documentation: { type: Integer, required: true }
|
50
|
+
expose :current_page, documentation: { type: Integer, required: true }, as: :currentPage
|
51
|
+
expose :page_size, documentation: { type: Integer, required: true }, as: :pageSize
|
52
|
+
end
|
45
53
|
end
|
46
54
|
end
|
data/lib/mihari/models/alert.rb
CHANGED
@@ -30,8 +30,7 @@ module Mihari
|
|
30
30
|
offset = (page - 1) * limit
|
31
31
|
|
32
32
|
relation = build_relation(filter.without_pagination)
|
33
|
-
|
34
|
-
eager_load(:artifacts, :tags).where(id: [alert_ids]).order(id: :desc)
|
33
|
+
relation.limit(limit).offset(offset).order(id: :desc)
|
35
34
|
end
|
36
35
|
|
37
36
|
#
|
@@ -48,39 +47,17 @@ module Mihari
|
|
48
47
|
|
49
48
|
private
|
50
49
|
|
51
|
-
#
|
52
|
-
# @param [Mihari::Structs::Filters::Alert::SearchFilter] filter
|
53
|
-
#
|
54
|
-
# @return [Array<Integer>]
|
55
|
-
#
|
56
|
-
def get_artifact_ids_by_filter(filter)
|
57
|
-
artifact_ids = []
|
58
|
-
|
59
|
-
if filter.artifact_data
|
60
|
-
artifact = Artifact.where(data: filter.artifact_data)
|
61
|
-
artifact_ids = artifact.pluck(:id)
|
62
|
-
# set invalid ID if nothing is matched with the filters
|
63
|
-
artifact_ids = [-1] if artifact_ids.empty?
|
64
|
-
end
|
65
|
-
|
66
|
-
artifact_ids
|
67
|
-
end
|
68
|
-
|
69
50
|
#
|
70
51
|
# @param [Mihari::Structs::Filters::Alert::SearchFilter] filter
|
71
52
|
#
|
72
53
|
# @return [Mihari::Models::Alert]
|
73
54
|
#
|
74
55
|
def build_relation(filter)
|
75
|
-
|
76
|
-
|
77
|
-
relation = includes(:artifacts, :tags)
|
78
|
-
|
79
|
-
relation = relation.where(artifacts: { id: artifact_ids }) unless artifact_ids.empty?
|
80
|
-
relation = relation.where(tags: { name: filter.tag_name }) if filter.tag_name
|
56
|
+
relation = eager_load(:artifacts, :tags)
|
81
57
|
|
58
|
+
relation = relation.where(artifacts: { data: filter.artifact }) if filter.artifact
|
59
|
+
relation = relation.where(tags: { name: filter.tag }) if filter.tag
|
82
60
|
relation = relation.where(rule_id: filter.rule_id) if filter.rule_id
|
83
|
-
|
84
61
|
relation = relation.where("alerts.created_at >= ?", filter.from_at) if filter.from_at
|
85
62
|
relation = relation.where("alerts.created_at <= ?", filter.to_at) if filter.to_at
|
86
63
|
|
@@ -46,7 +46,7 @@ module Mihari
|
|
46
46
|
|
47
47
|
super(*args, **kwargs)
|
48
48
|
|
49
|
-
self.data_type =
|
49
|
+
self.data_type = DataType.type(data)
|
50
50
|
|
51
51
|
@tags = []
|
52
52
|
@rule_id = ""
|
@@ -77,6 +77,18 @@ module Mihari
|
|
77
77
|
artifact.created_at < decayed_at
|
78
78
|
end
|
79
79
|
|
80
|
+
#
|
81
|
+
# Count artifacts
|
82
|
+
#
|
83
|
+
# @param [Mihari::Structs::Filters::Artifact::SearchFilter] filter
|
84
|
+
#
|
85
|
+
# @return [Integer]
|
86
|
+
#
|
87
|
+
def count(filter)
|
88
|
+
relation = build_relation(filter)
|
89
|
+
relation.distinct("artifact.id").count
|
90
|
+
end
|
91
|
+
|
80
92
|
#
|
81
93
|
# Enrich whois record
|
82
94
|
#
|
@@ -105,7 +117,7 @@ module Mihari
|
|
105
117
|
# @param [Mihari::Enrichers::Shodan] enricher
|
106
118
|
#
|
107
119
|
def enrich_reverse_dns(enricher = Enrichers::Shodan.new)
|
108
|
-
return unless
|
120
|
+
return unless can_enrich_reverse_dns?
|
109
121
|
|
110
122
|
self.reverse_dns_names = ReverseDnsName.build_by_ip(data, enricher: enricher)
|
111
123
|
end
|
@@ -195,6 +207,56 @@ module Mihari
|
|
195
207
|
methods.each { |method| send(method, enricher) if respond_to?(method) }
|
196
208
|
end
|
197
209
|
|
210
|
+
class << self
|
211
|
+
#
|
212
|
+
# Search artifacts
|
213
|
+
#
|
214
|
+
# @param [Mihari::Structs::Filters::Artifact::SearchFilterWithPagination] filter
|
215
|
+
#
|
216
|
+
# @return [Array<Artifact>]
|
217
|
+
#
|
218
|
+
def search(filter)
|
219
|
+
limit = filter.limit.to_i
|
220
|
+
raise ArgumentError, "limit should be bigger than zero" unless limit.positive?
|
221
|
+
|
222
|
+
page = filter.page.to_i
|
223
|
+
raise ArgumentError, "page should be bigger than zero" unless page.positive?
|
224
|
+
|
225
|
+
offset = (page - 1) * limit
|
226
|
+
|
227
|
+
relation = build_relation(filter.without_pagination)
|
228
|
+
relation.limit(limit).offset(offset).order(id: :desc)
|
229
|
+
end
|
230
|
+
|
231
|
+
#
|
232
|
+
# Count artifacts
|
233
|
+
#
|
234
|
+
# @param [Mihari::Structs::Filters::Artifact::SearchFilter] filter
|
235
|
+
#
|
236
|
+
# @return [Integer]
|
237
|
+
#
|
238
|
+
def count(filter)
|
239
|
+
relation = build_relation(filter)
|
240
|
+
relation.distinct("artifacts.id").count
|
241
|
+
end
|
242
|
+
|
243
|
+
#
|
244
|
+
# @param [Mihari::Structs::Filters::Artifact::SearchFilter] filter
|
245
|
+
#
|
246
|
+
# @return [Mihari::Models::Artifact]
|
247
|
+
#
|
248
|
+
def build_relation(filter)
|
249
|
+
relation = eager_load(alert: :tags)
|
250
|
+
|
251
|
+
relation = relation.where(alert: { rule_id: filter.rule_id }) if filter.rule_id
|
252
|
+
relation = relation.where(alert: { tags: { name: filter.tag } }) if filter.tag
|
253
|
+
relation = relation.where("artifacts.created_at >= ?", filter.from_at) if filter.from_at
|
254
|
+
relation = relation.where("artifacts.created_at <= ?", filter.to_at) if filter.to_at
|
255
|
+
|
256
|
+
relation
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
198
260
|
private
|
199
261
|
|
200
262
|
def ipinfo
|
@@ -219,7 +281,7 @@ module Mihari
|
|
219
281
|
%w[domain url].include?(data_type) && dns_records.empty?
|
220
282
|
end
|
221
283
|
|
222
|
-
def
|
284
|
+
def can_enrich_reverse_dns?
|
223
285
|
data_type == "ip" && reverse_dns_names.empty?
|
224
286
|
end
|
225
287
|
|
data/lib/mihari/models/dns.rb
CHANGED
@@ -20,16 +20,11 @@ module Mihari
|
|
20
20
|
# @return [Array<Mihari::Models::DnsRecord>]
|
21
21
|
#
|
22
22
|
def build_by_domain(domain, enricher: Enrichers::GooglePublicDNS.new)
|
23
|
-
|
23
|
+
enricher.result(domain).bind do |res|
|
24
24
|
Success(
|
25
|
-
|
26
|
-
res.answers.map do |answer|
|
27
|
-
new(resource: answer.resource_type, value: answer.data)
|
28
|
-
end
|
29
|
-
end.flatten
|
25
|
+
res.answers.map { |answer| new(resource: answer.resource_type, value: answer.data) }
|
30
26
|
)
|
31
|
-
end
|
32
|
-
result.value_or []
|
27
|
+
end.value_or([])
|
33
28
|
end
|
34
29
|
end
|
35
30
|
end
|
data/lib/mihari/models/rule.rb
CHANGED
@@ -40,10 +40,7 @@ module Mihari
|
|
40
40
|
offset = (page - 1) * limit
|
41
41
|
|
42
42
|
relation = build_relation(filter.without_pagination)
|
43
|
-
|
44
|
-
# TODO: improve queires
|
45
|
-
rule_ids = relation.limit(limit).offset(offset).order(created_at: :desc).pluck(:id).uniq
|
46
|
-
where(id: [rule_ids]).order(created_at: :desc)
|
43
|
+
relation.limit(limit).offset(offset).order(created_at: :desc)
|
47
44
|
end
|
48
45
|
|
49
46
|
#
|
@@ -68,7 +65,7 @@ module Mihari
|
|
68
65
|
def build_relation(filter)
|
69
66
|
relation = includes(alerts: :tags)
|
70
67
|
|
71
|
-
relation = relation.where(alerts: { tags: { name: filter.
|
68
|
+
relation = relation.where(alerts: { tags: { name: filter.tag } }) if filter.tag
|
72
69
|
|
73
70
|
relation = relation.where("rules.title LIKE ?", "%#{filter.title}%") if filter.title
|
74
71
|
relation = relation.where("rules.description LIKE ?", "%#{filter.description}%") if filter.description
|
data/lib/mihari/rule.rb
CHANGED
@@ -110,9 +110,7 @@ module Mihari
|
|
110
110
|
# @return [Array<Mihari::Models::Artifact>]
|
111
111
|
#
|
112
112
|
def artifacts
|
113
|
-
|
114
|
-
# @type [Dry::Monads::Result::Success<Array<Mihari::Models::Artifact>>, Dry::Monads::Result::Failure]
|
115
|
-
result = analyzer.result
|
113
|
+
analyzer_results.flat_map do |result|
|
116
114
|
case result
|
117
115
|
when Success
|
118
116
|
artifacts = result.value!
|
@@ -123,7 +121,7 @@ module Mihari
|
|
123
121
|
else
|
124
122
|
raise result.failure unless analyzer.ignore_error?
|
125
123
|
end
|
126
|
-
end
|
124
|
+
end
|
127
125
|
end
|
128
126
|
|
129
127
|
#
|
@@ -292,15 +290,30 @@ module Mihari
|
|
292
290
|
# @return [Array<Mihari::Analyzers::Base>]
|
293
291
|
#
|
294
292
|
def analyzers
|
295
|
-
@analyzers ||= queries.map do |
|
296
|
-
analyzer_name =
|
293
|
+
@analyzers ||= queries.map do |params|
|
294
|
+
analyzer_name = params[:analyzer]
|
297
295
|
klass = get_analyzer_class(analyzer_name)
|
298
|
-
analyzer = klass.from_query(
|
296
|
+
analyzer = klass.from_query(params)
|
299
297
|
analyzer.validate_configuration!
|
300
298
|
analyzer
|
301
299
|
end
|
302
300
|
end
|
303
301
|
|
302
|
+
def parallel_analyzers
|
303
|
+
analyzers.select(&:parallel?)
|
304
|
+
end
|
305
|
+
|
306
|
+
def serial_analyzers
|
307
|
+
analyzers.reject(&:parallel?)
|
308
|
+
end
|
309
|
+
|
310
|
+
# @return [Array<Dry::Monads::Result::Success<Array<Mihari::Models::Artifact>>, Dry::Monads::Result::Failure>]
|
311
|
+
def analyzer_results
|
312
|
+
parallel_results = Parallel.map(parallel_analyzers) { |analyzer| analyzer.result }
|
313
|
+
serial_results = serial_analyzers.map(&:result)
|
314
|
+
parallel_results + serial_results
|
315
|
+
end
|
316
|
+
|
304
317
|
#
|
305
318
|
# Get emitter class
|
306
319
|
#
|
@@ -15,7 +15,11 @@ module Mihari
|
|
15
15
|
optional(:ignore_error).value(:bool).default(Mihari.config.ignore_error)
|
16
16
|
end
|
17
17
|
|
18
|
-
|
18
|
+
ParallelOptions = Dry::Schema.Params do
|
19
|
+
optional(:parallel).value(:bool).default(Mihari.config.parallel)
|
20
|
+
end
|
21
|
+
|
22
|
+
AnalyzerOptions = Options | IgnoreErrorOptions | ParallelOptions
|
19
23
|
|
20
24
|
PaginationOptions = Dry::Schema.Params do
|
21
25
|
optional(:pagination_interval).value(:integer).default(Mihari.config.pagination_interval)
|