mihari 7.2.0 → 7.3.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/Dockerfile +1 -1
- data/lib/mihari/actor.rb +7 -0
- data/lib/mihari/analyzers/base.rb +0 -7
- data/lib/mihari/enrichers/base.rb +54 -12
- data/lib/mihari/enrichers/google_public_dns.rb +28 -7
- data/lib/mihari/enrichers/mmdb.rb +25 -7
- data/lib/mihari/enrichers/shodan.rb +35 -4
- data/lib/mihari/enrichers/whois.rb +32 -24
- data/lib/mihari/models/alert.rb +12 -0
- data/lib/mihari/models/artifact.rb +105 -181
- data/lib/mihari/models/rule.rb +21 -0
- data/lib/mihari/rule.rb +27 -6
- data/lib/mihari/schemas/alert.rb +3 -3
- data/lib/mihari/schemas/analyzer.rb +27 -27
- data/lib/mihari/schemas/emitter.rb +9 -9
- data/lib/mihari/schemas/macros.rb +2 -2
- data/lib/mihari/schemas/options.rb +2 -5
- data/lib/mihari/schemas/rule.rb +12 -12
- data/lib/mihari/services/builders.rb +0 -153
- data/lib/mihari/services/enrichers.rb +1 -1
- data/lib/mihari/services/getters.rb +1 -1
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/public/assets/{index-GWurHG1o.js → index-JHS0L8KZ.js} +29 -29
- data/lib/mihari/web/public/index.html +1 -1
- data/mihari.gemspec +2 -2
- data/requirements.txt +1 -1
- metadata +7 -7
@@ -17,6 +17,90 @@ module Mihari
|
|
17
17
|
# Artifact model
|
18
18
|
#
|
19
19
|
class Artifact < ActiveRecord::Base
|
20
|
+
# @!attribute [r] id
|
21
|
+
# @return [Integer, nil]
|
22
|
+
|
23
|
+
# @!attribute [rw] data
|
24
|
+
# @return [String]
|
25
|
+
|
26
|
+
# @!attribute [rw] data_type
|
27
|
+
# @return [String]
|
28
|
+
|
29
|
+
# @!attribute [rw] source
|
30
|
+
# @return [String, nil]
|
31
|
+
|
32
|
+
# @!attribute [rw] query
|
33
|
+
# @return [String, nil]
|
34
|
+
|
35
|
+
# @!attribute [rw] metadata
|
36
|
+
# @return [Hash, nil]
|
37
|
+
|
38
|
+
# @!attribute [rw] created_at
|
39
|
+
# @return [DateTime]
|
40
|
+
|
41
|
+
# @!attribute [r] alert
|
42
|
+
# @return [Mihari::Models::Alert]
|
43
|
+
|
44
|
+
# @!attribute [r] rule
|
45
|
+
# @return [Mihari::Models::Rule]
|
46
|
+
|
47
|
+
# @!attribute [rw] autonomous_system
|
48
|
+
# @return [Mihari::Models::AutonomousSystem, nil]
|
49
|
+
|
50
|
+
# @!attribute [rw] geolocation
|
51
|
+
# @return [Mihari::Models::Geolocation, nil]
|
52
|
+
|
53
|
+
# @!attribute [rw] whois_record
|
54
|
+
# @return [Mihari::Models::WhoisRecord, nil]
|
55
|
+
|
56
|
+
# @!attribute [rw] cpes
|
57
|
+
# @return [Array<Mihari::Models::CPE>]
|
58
|
+
|
59
|
+
# @!attribute [rw] dns_records
|
60
|
+
# @return [Array<Mihari::Models::DnsRecord>]
|
61
|
+
|
62
|
+
# @!attribute [rw] ports
|
63
|
+
# @return [Array<Mihari::Models::Port>]
|
64
|
+
|
65
|
+
# @!attribute [rw] reverse_dns_names
|
66
|
+
# @return [Array<Mihari::Models::ReverseDnsName>]
|
67
|
+
|
68
|
+
# @!attribute [rw] vulnerabilities
|
69
|
+
# @return [Array<Mihari::Models::Vulnerability>]
|
70
|
+
|
71
|
+
# @!attribute [r] alert
|
72
|
+
# @return [Mihari::Models::Alert]
|
73
|
+
|
74
|
+
# @!attribute [r] rule
|
75
|
+
# @return [Mihari::Models::Rule]
|
76
|
+
|
77
|
+
# @!attribute [rw] autonomous_system
|
78
|
+
# @return [Mihari::Models::AutonomousSystem, nil]
|
79
|
+
|
80
|
+
# @!attribute [rw] geolocation
|
81
|
+
# @return [Mihari::Models::Geolocation, nil]
|
82
|
+
|
83
|
+
# @!attribute [rw] whois_record
|
84
|
+
# @return [Mihari::Models::WhoisRecord, nil]
|
85
|
+
|
86
|
+
# @!attribute [rw] cpes
|
87
|
+
# @return [Array<Mihari::Models::CPE>]
|
88
|
+
|
89
|
+
# @!attribute [rw] dns_records
|
90
|
+
# @return [Array<Mihari::Models::DnsRecord>]
|
91
|
+
|
92
|
+
# @!attribute [rw] ports
|
93
|
+
# @return [Array<Mihari::Models::Port>]
|
94
|
+
|
95
|
+
# @!attribute [rw] reverse_dns_names
|
96
|
+
# @return [Array<Mihari::Models::ReverseDnsName>]
|
97
|
+
|
98
|
+
# @!attribute [rw] vulnerabilities
|
99
|
+
# @return [Array<Mihari::Models::Vulnerability>]
|
100
|
+
|
101
|
+
# @!attribute [rw] tags
|
102
|
+
# @return [Array<Mihari::Models::Tag>]
|
103
|
+
|
20
104
|
belongs_to :alert
|
21
105
|
|
22
106
|
has_one :autonomous_system, dependent: :destroy
|
@@ -90,173 +174,24 @@ module Mihari
|
|
90
174
|
artifact.created_at < decayed_at
|
91
175
|
end
|
92
176
|
|
93
|
-
|
94
|
-
|
95
|
-
#
|
96
|
-
# @param [Mihari::Enrichers::Whois] enricher
|
97
|
-
#
|
98
|
-
def enrich_whois(enricher = Enrichers::Whois.new)
|
99
|
-
return unless can_enrich_whois?
|
100
|
-
|
101
|
-
self.whois_record = Services::WhoisRecordBuilder.call(domain, enricher: enricher)
|
102
|
-
end
|
103
|
-
|
104
|
-
#
|
105
|
-
# Enrich DNS records
|
106
|
-
#
|
107
|
-
# @param [Mihari::Enrichers::GooglePublicDNS] enricher
|
108
|
-
#
|
109
|
-
def enrich_dns(enricher = Enrichers::GooglePublicDNS.new)
|
110
|
-
return unless can_enrich_dns?
|
111
|
-
|
112
|
-
self.dns_records = Services::DnsRecordBuilder.call(domain, enricher: enricher)
|
113
|
-
end
|
114
|
-
|
115
|
-
#
|
116
|
-
# Enrich reverse DNS names
|
117
|
-
#
|
118
|
-
# @param [Mihari::Enrichers::Shodan] enricher
|
119
|
-
#
|
120
|
-
def enrich_reverse_dns(enricher = Enrichers::Shodan.new)
|
121
|
-
return unless can_enrich_reverse_dns?
|
122
|
-
|
123
|
-
self.reverse_dns_names = Services::ReverseDnsNameBuilder.call(data, enricher: enricher)
|
124
|
-
end
|
125
|
-
|
126
|
-
#
|
127
|
-
# Enrich geolocation
|
128
|
-
#
|
129
|
-
# @param [Mihari::Enrichers::IPInfo] enricher
|
130
|
-
#
|
131
|
-
def enrich_geolocation(enricher = Enrichers::MMDB.new)
|
132
|
-
return unless can_enrich_geolocation?
|
133
|
-
|
134
|
-
self.geolocation = Services::GeolocationBuilder.call(data, enricher: enricher)
|
135
|
-
end
|
136
|
-
|
137
|
-
#
|
138
|
-
# Enrich AS
|
139
|
-
#
|
140
|
-
# @param [Mihari::Enrichers::IPInfo] enricher
|
141
|
-
#
|
142
|
-
def enrich_autonomous_system(enricher = Enrichers::MMDB.new)
|
143
|
-
return unless can_enrich_autonomous_system?
|
144
|
-
|
145
|
-
self.autonomous_system = Services::AutonomousSystemBuilder.call(data, enricher: enricher)
|
146
|
-
end
|
147
|
-
|
148
|
-
#
|
149
|
-
# Enrich ports
|
150
|
-
#
|
151
|
-
# @param [Mihari::Enrichers::Shodan] enricher
|
152
|
-
#
|
153
|
-
def enrich_ports(enricher = Enrichers::Shodan.new)
|
154
|
-
return unless can_enrich_ports?
|
155
|
-
|
156
|
-
self.ports = Services::PortBuilder.call(data, enricher: enricher)
|
157
|
-
end
|
158
|
-
|
159
|
-
#
|
160
|
-
# Enrich CPEs
|
161
|
-
#
|
162
|
-
# @param [Mihari::Enrichers::Shodan] enricher
|
163
|
-
#
|
164
|
-
def enrich_cpes(enricher = Enrichers::Shodan.new)
|
165
|
-
return unless can_enrich_cpes?
|
166
|
-
|
167
|
-
self.cpes = Services::CPEBuilder.call(data, enricher: enricher)
|
168
|
-
end
|
169
|
-
|
170
|
-
#
|
171
|
-
# Enrich vulnerabilities
|
172
|
-
#
|
173
|
-
# @param [Mihari::Enrichers::Shodan] enricher
|
174
|
-
#
|
175
|
-
def enrich_vulnerabilities(enricher = Enrichers::Shodan.new)
|
176
|
-
return unless can_enrich_vulnerabilities?
|
177
|
-
|
178
|
-
self.vulnerabilities = Services::VulnerabilityBuilder.call(data, enricher: enricher)
|
177
|
+
def enrichable?
|
178
|
+
!callable_enrichers.empty?
|
179
179
|
end
|
180
180
|
|
181
|
-
|
182
|
-
|
183
|
-
#
|
184
|
-
def enrich_all
|
185
|
-
enrich_autonomous_system mmdb
|
186
|
-
enrich_dns
|
187
|
-
enrich_geolocation mmdb
|
188
|
-
enrich_reverse_dns shodan
|
189
|
-
enrich_whois
|
190
|
-
enrich_ports shodan
|
191
|
-
enrich_cpes shodan
|
192
|
-
enrich_vulnerabilities shodan
|
181
|
+
def enrich
|
182
|
+
callable_enrichers.each { |enricher| enricher.result self }
|
193
183
|
end
|
194
184
|
|
195
|
-
ENRICH_METHODS_BY_ENRICHER = {
|
196
|
-
Enrichers::Whois => %i[
|
197
|
-
enrich_whois
|
198
|
-
],
|
199
|
-
Enrichers::MMDB => %i[
|
200
|
-
enrich_autonomous_system
|
201
|
-
enrich_geolocation
|
202
|
-
],
|
203
|
-
Enrichers::Shodan => %i[
|
204
|
-
enrich_ports
|
205
|
-
enrich_cpes
|
206
|
-
enrich_reverse_dns
|
207
|
-
enrich_vulnerabilities
|
208
|
-
],
|
209
|
-
Enrichers::GooglePublicDNS => %i[
|
210
|
-
enrich_dns
|
211
|
-
]
|
212
|
-
}.freeze
|
213
|
-
|
214
185
|
#
|
215
|
-
#
|
216
|
-
#
|
217
|
-
# @param [Mihari::Enrichers::Base] enricher
|
186
|
+
# @return [String, nil]
|
218
187
|
#
|
219
|
-
def
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
end
|
227
|
-
|
228
|
-
def can_enrich_dns?
|
229
|
-
%w[domain url].include?(data_type) && dns_records.empty?
|
230
|
-
end
|
231
|
-
|
232
|
-
def can_enrich_reverse_dns?
|
233
|
-
data_type == "ip" && reverse_dns_names.empty?
|
234
|
-
end
|
235
|
-
|
236
|
-
def can_enrich_geolocation?
|
237
|
-
data_type == "ip" && geolocation.nil?
|
238
|
-
end
|
239
|
-
|
240
|
-
def can_enrich_autonomous_system?
|
241
|
-
data_type == "ip" && autonomous_system.nil?
|
242
|
-
end
|
243
|
-
|
244
|
-
def can_enrich_ports?
|
245
|
-
data_type == "ip" && ports.empty?
|
246
|
-
end
|
247
|
-
|
248
|
-
def can_enrich_cpes?
|
249
|
-
data_type == "ip" && cpes.empty?
|
250
|
-
end
|
251
|
-
|
252
|
-
def can_enrich_vulnerabilities?
|
253
|
-
data_type == "ip" && vulnerabilities.empty?
|
254
|
-
end
|
255
|
-
|
256
|
-
def enrichable?
|
257
|
-
enrich_methods = methods.map(&:to_s).select { |method| method.start_with?("can_enrich_") }
|
258
|
-
enrich_methods.map(&:to_sym).any? do |method|
|
259
|
-
send(method) if respond_to?(method)
|
188
|
+
def domain
|
189
|
+
case data_type
|
190
|
+
when "domain"
|
191
|
+
data
|
192
|
+
when "url"
|
193
|
+
host = Addressable::URI.parse(data).host
|
194
|
+
(DataType.type(host) == "ip") ? nil : host
|
260
195
|
end
|
261
196
|
end
|
262
197
|
|
@@ -272,6 +207,15 @@ module Mihari
|
|
272
207
|
|
273
208
|
private
|
274
209
|
|
210
|
+
#
|
211
|
+
# @return [Array<Mihari::Enrichers::Base>]
|
212
|
+
#
|
213
|
+
def callable_enrichers
|
214
|
+
@callable_enrichers ||= Mihari.enrichers.map(&:new).select do |enricher|
|
215
|
+
enricher.callable?(self)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
275
219
|
def set_data_type
|
276
220
|
self.data_type = DataType.type(data)
|
277
221
|
end
|
@@ -279,26 +223,6 @@ module Mihari
|
|
279
223
|
def set_rule_id
|
280
224
|
@set_rule_id ||= nil
|
281
225
|
end
|
282
|
-
|
283
|
-
def mmdb
|
284
|
-
@mmdb ||= Enrichers::MMDB.new
|
285
|
-
end
|
286
|
-
|
287
|
-
def shodan
|
288
|
-
@shodan ||= Enrichers::Shodan.new
|
289
|
-
end
|
290
|
-
|
291
|
-
#
|
292
|
-
# @return [String, nil]
|
293
|
-
#
|
294
|
-
def domain
|
295
|
-
case data_type
|
296
|
-
when "domain"
|
297
|
-
data
|
298
|
-
when "url"
|
299
|
-
Addressable::URI.parse(data).host
|
300
|
-
end
|
301
|
-
end
|
302
226
|
end
|
303
227
|
end
|
304
228
|
end
|
data/lib/mihari/models/rule.rb
CHANGED
@@ -6,6 +6,27 @@ module Mihari
|
|
6
6
|
# Rule model
|
7
7
|
#
|
8
8
|
class Rule < ActiveRecord::Base
|
9
|
+
# @!attribute [rw] id
|
10
|
+
# @return [String]
|
11
|
+
|
12
|
+
# @!attribute [rw] title
|
13
|
+
# @return [String]
|
14
|
+
|
15
|
+
# @!attribute [rw] description
|
16
|
+
# @return [String]
|
17
|
+
|
18
|
+
# @!attribute [rw] data
|
19
|
+
# @return [Hash]
|
20
|
+
|
21
|
+
# @!attribute [rw] created_at
|
22
|
+
# @return [DateTime]
|
23
|
+
|
24
|
+
# @!attribute [rw] updated_at
|
25
|
+
# @return [DateTime]
|
26
|
+
|
27
|
+
# @!attribute [r] alerts
|
28
|
+
# @return [Array<Mihari::Models::Alert>]
|
29
|
+
|
9
30
|
has_many :alerts, dependent: :destroy
|
10
31
|
has_many :taggings, dependent: :destroy
|
11
32
|
has_many :tags, through: :taggings
|
data/lib/mihari/rule.rb
CHANGED
@@ -174,8 +174,10 @@ module Mihari
|
|
174
174
|
# @return [Array<Mihari::Models::Artifact>]
|
175
175
|
#
|
176
176
|
def enriched_artifacts
|
177
|
-
@enriched_artifacts ||=
|
178
|
-
|
177
|
+
@enriched_artifacts ||= unique_artifacts.map do |artifact|
|
178
|
+
serial_enrichers.each { |enricher| enricher.result(artifact) }
|
179
|
+
Parallel.each(parallel_enrichers) { |enricher| enricher.result(artifact) }
|
180
|
+
|
179
181
|
artifact
|
180
182
|
end
|
181
183
|
end
|
@@ -188,7 +190,10 @@ module Mihari
|
|
188
190
|
def bulk_emit
|
189
191
|
return [] if enriched_artifacts.empty?
|
190
192
|
|
191
|
-
|
193
|
+
[].tap do |out|
|
194
|
+
out << serial_emitters.map { |emitter| emitter.result(enriched_artifacts).value_or(nil) }
|
195
|
+
out << Parallel.map(parallel_emitters) { |emitter| emitter.result(enriched_artifacts).value_or(nil) }
|
196
|
+
end.flatten.compact
|
192
197
|
end
|
193
198
|
|
194
199
|
#
|
@@ -289,11 +294,11 @@ module Mihari
|
|
289
294
|
#
|
290
295
|
# @return [Boolean]
|
291
296
|
#
|
292
|
-
def falsepositive?(
|
293
|
-
return true if falsepositives.include?(
|
297
|
+
def falsepositive?(artifact)
|
298
|
+
return true if falsepositives.include?(artifact)
|
294
299
|
|
295
300
|
regexps = falsepositives.select { |fp| fp.is_a?(Regexp) }
|
296
|
-
regexps.any? { |fp| fp.match?(
|
301
|
+
regexps.any? { |fp| fp.match?(artifact) }
|
297
302
|
end
|
298
303
|
|
299
304
|
#
|
@@ -365,6 +370,14 @@ module Mihari
|
|
365
370
|
end
|
366
371
|
end
|
367
372
|
|
373
|
+
def parallel_emitters
|
374
|
+
emitters.select(&:parallel?)
|
375
|
+
end
|
376
|
+
|
377
|
+
def serial_emitters
|
378
|
+
emitters.reject(&:parallel?)
|
379
|
+
end
|
380
|
+
|
368
381
|
#
|
369
382
|
# Get enricher class
|
370
383
|
#
|
@@ -391,6 +404,14 @@ module Mihari
|
|
391
404
|
end
|
392
405
|
end
|
393
406
|
|
407
|
+
def parallel_enrichers
|
408
|
+
enrichers.select(&:parallel?)
|
409
|
+
end
|
410
|
+
|
411
|
+
def serial_enrichers
|
412
|
+
enrichers.reject(&:parallel?)
|
413
|
+
end
|
414
|
+
|
394
415
|
#
|
395
416
|
# Validate the data format
|
396
417
|
#
|
data/lib/mihari/schemas/alert.rb
CHANGED
@@ -3,9 +3,9 @@
|
|
3
3
|
module Mihari
|
4
4
|
module Schemas
|
5
5
|
Alert = Dry::Schema.Params do
|
6
|
-
required(:rule_id).
|
7
|
-
required(:artifacts).
|
8
|
-
optional(:source).
|
6
|
+
required(:rule_id).filled(:string)
|
7
|
+
required(:artifacts).array { filled(:string) }
|
8
|
+
optional(:source).filled(:string)
|
9
9
|
end
|
10
10
|
|
11
11
|
#
|
@@ -20,8 +20,8 @@ module Mihari
|
|
20
20
|
key = keys.first
|
21
21
|
const_set(key.upcase, Dry::Schema.Params do
|
22
22
|
required(:analyzer).value(Types::String.enum(*keys))
|
23
|
-
required(:query).
|
24
|
-
optional(:api_key).
|
23
|
+
required(:query).filled(:string)
|
24
|
+
optional(:api_key).filled(:string)
|
25
25
|
optional(:options).hash(AnalyzerPaginationOptions)
|
26
26
|
end)
|
27
27
|
end
|
@@ -36,60 +36,60 @@ module Mihari
|
|
36
36
|
key = keys.first
|
37
37
|
const_set(key.upcase, Dry::Schema.Params do
|
38
38
|
required(:analyzer).value(Types::String.enum(*keys))
|
39
|
-
required(:query).
|
40
|
-
optional(:api_key).
|
39
|
+
required(:query).filled(:string)
|
40
|
+
optional(:api_key).filled(:string)
|
41
41
|
optional(:options).hash(AnalyzerOptions)
|
42
42
|
end)
|
43
43
|
end
|
44
44
|
|
45
45
|
DNSTwister = Dry::Schema.Params do
|
46
46
|
required(:analyzer).value(Types::String.enum(*Mihari::Analyzers::DNSTwister.keys))
|
47
|
-
required(:query).
|
47
|
+
required(:query).filled(:string)
|
48
48
|
optional(:options).hash(AnalyzerOptions)
|
49
49
|
end
|
50
50
|
|
51
51
|
Censys = Dry::Schema.Params do
|
52
52
|
required(:analyzer).value(Types::String.enum(*Mihari::Analyzers::Censys.keys))
|
53
|
-
required(:query).
|
54
|
-
optional(:id).
|
55
|
-
optional(:secret).
|
53
|
+
required(:query).filled(:string)
|
54
|
+
optional(:id).filled(:string)
|
55
|
+
optional(:secret).filled(:string)
|
56
56
|
optional(:options).hash(AnalyzerPaginationOptions)
|
57
57
|
end
|
58
58
|
|
59
59
|
CIRCL = Dry::Schema.Params do
|
60
60
|
required(:analyzer).value(Types::String.enum(*Mihari::Analyzers::CIRCL.keys))
|
61
|
-
required(:query).
|
62
|
-
optional(:username).
|
63
|
-
optional(:password).
|
61
|
+
required(:query).filled(:string)
|
62
|
+
optional(:username).filled(:string)
|
63
|
+
optional(:password).filled(:string)
|
64
64
|
optional(:options).hash(AnalyzerOptions)
|
65
65
|
end
|
66
66
|
|
67
67
|
Fofa = Dry::Schema.Params do
|
68
68
|
required(:analyzer).value(Types::String.enum(*Mihari::Analyzers::Fofa.keys))
|
69
|
-
required(:query).
|
70
|
-
optional(:api_key).
|
71
|
-
optional(:email).
|
69
|
+
required(:query).filled(:string)
|
70
|
+
optional(:api_key).filled(:string)
|
71
|
+
optional(:email).filled(:string)
|
72
72
|
optional(:options).hash(AnalyzerPaginationOptions)
|
73
73
|
end
|
74
74
|
|
75
75
|
PassiveTotal = Dry::Schema.Params do
|
76
76
|
required(:analyzer).value(Types::String.enum(*Mihari::Analyzers::PassiveTotal.keys))
|
77
|
-
required(:query).
|
78
|
-
optional(:username).
|
79
|
-
optional(:api_key).
|
77
|
+
required(:query).filled(:string)
|
78
|
+
optional(:username).filled(:string)
|
79
|
+
optional(:api_key).filled(:string)
|
80
80
|
optional(:options).hash(AnalyzerOptions)
|
81
81
|
end
|
82
82
|
|
83
83
|
ZoomEye = Dry::Schema.Params do
|
84
84
|
required(:analyzer).value(Types::String.enum(*Mihari::Analyzers::ZoomEye.keys))
|
85
|
-
required(:query).
|
85
|
+
required(:query).filled(:string)
|
86
86
|
required(:type).value(Types::String.enum("host", "web"))
|
87
87
|
optional(:options).hash(AnalyzerPaginationOptions)
|
88
88
|
end
|
89
89
|
|
90
90
|
Crtsh = Dry::Schema.Params do
|
91
91
|
required(:analyzer).value(Types::String.enum(*Mihari::Analyzers::Crtsh.keys))
|
92
|
-
required(:query).
|
92
|
+
required(:query).filled(:string)
|
93
93
|
optional(:exclude_expired).value(:bool).default(true)
|
94
94
|
optional(:match).value(Types::String.enum("=", "ILIKE", "LIKE", "single", "any", "FTS")).default(nil)
|
95
95
|
optional(:options).hash(AnalyzerOptions)
|
@@ -97,22 +97,22 @@ module Mihari
|
|
97
97
|
|
98
98
|
HunterHow = Dry::Schema.Params do
|
99
99
|
required(:analyzer).value(Types::String.enum(*Mihari::Analyzers::HunterHow.keys))
|
100
|
-
required(:query).
|
100
|
+
required(:query).filled(:string)
|
101
101
|
required(:start_time).value(:date)
|
102
102
|
required(:end_time).value(:date)
|
103
|
-
optional(:api_key).
|
103
|
+
optional(:api_key).filled(:string)
|
104
104
|
optional(:options).hash(AnalyzerPaginationOptions)
|
105
105
|
end
|
106
106
|
|
107
107
|
Feed = Dry::Schema.Params do
|
108
108
|
required(:analyzer).value(Types::String.enum(*Mihari::Analyzers::Feed.keys))
|
109
|
-
required(:query).
|
110
|
-
required(:selector).
|
109
|
+
required(:query).filled(:string)
|
110
|
+
required(:selector).filled(:string)
|
111
111
|
optional(:method).value(Types::HTTPRequestMethods).default("GET")
|
112
|
-
optional(:headers).
|
113
|
-
optional(:params).
|
114
|
-
optional(:form).
|
115
|
-
optional(:json).
|
112
|
+
optional(:headers).filled(:hash)
|
113
|
+
optional(:params).filled(:hash)
|
114
|
+
optional(:form).filled(:hash)
|
115
|
+
optional(:json).filled(:hash)
|
116
116
|
optional(:options).hash(AnalyzerOptions)
|
117
117
|
end
|
118
118
|
end
|
@@ -15,31 +15,31 @@ module Mihari
|
|
15
15
|
|
16
16
|
MISP = Dry::Schema.Params do
|
17
17
|
required(:emitter).value(Types::String.enum(*Mihari::Emitters::MISP.keys))
|
18
|
-
optional(:url).
|
19
|
-
optional(:api_key).
|
18
|
+
optional(:url).filled(:string)
|
19
|
+
optional(:api_key).filled(:string)
|
20
20
|
optional(:options).hash(Options)
|
21
21
|
end
|
22
22
|
|
23
23
|
TheHive = Dry::Schema.Params do
|
24
24
|
required(:emitter).value(Types::String.enum(*Mihari::Emitters::TheHive.keys))
|
25
|
-
optional(:url).
|
26
|
-
optional(:api_key).
|
25
|
+
optional(:url).filled(:string)
|
26
|
+
optional(:api_key).filled(:string)
|
27
27
|
optional(:options).hash(Options)
|
28
28
|
end
|
29
29
|
|
30
30
|
Slack = Dry::Schema.Params do
|
31
31
|
required(:emitter).value(Types::String.enum(*Mihari::Emitters::Slack.keys))
|
32
|
-
optional(:webhook_url).
|
33
|
-
optional(:channel).
|
32
|
+
optional(:webhook_url).filled(:string)
|
33
|
+
optional(:channel).filled(:string)
|
34
34
|
optional(:options).hash(Options)
|
35
35
|
end
|
36
36
|
|
37
37
|
Webhook = Dry::Schema.Params do
|
38
38
|
required(:emitter).value(Types::String.enum(*Mihari::Emitters::Webhook.keys))
|
39
|
-
required(:url).
|
39
|
+
required(:url).filled(:string)
|
40
40
|
optional(:method).value(Types::HTTPRequestMethods).default("POST")
|
41
|
-
optional(:headers).
|
42
|
-
optional(:template).
|
41
|
+
optional(:headers).filled(:hash)
|
42
|
+
optional(:template).filled(:string)
|
43
43
|
optional(:options).hash(Options)
|
44
44
|
end
|
45
45
|
end
|
@@ -8,9 +8,9 @@ module Dry
|
|
8
8
|
# (see https://github.com/dry-rb/dry-schema/issues/70)
|
9
9
|
#
|
10
10
|
class DSL
|
11
|
-
def default(
|
11
|
+
def default(artifact)
|
12
12
|
schema_dsl.before(:rule_applier) do |result|
|
13
|
-
result.update(name =>
|
13
|
+
result.update(name => artifact) if result.output && !result[name]
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
@@ -7,17 +7,14 @@ module Mihari
|
|
7
7
|
optional(:retry_interval).value(:integer).default(Mihari.config.retry_interval)
|
8
8
|
optional(:retry_exponential_backoff).value(:bool).default(Mihari.config.retry_exponential_backoff)
|
9
9
|
optional(:timeout).value(:integer)
|
10
|
+
optional(:parallel).value(:bool).default(Mihari.config.parallel)
|
10
11
|
end
|
11
12
|
|
12
13
|
IgnoreErrorOptions = Dry::Schema.Params do
|
13
14
|
optional(:ignore_error).value(:bool).default(Mihari.config.ignore_error)
|
14
15
|
end
|
15
16
|
|
16
|
-
|
17
|
-
optional(:parallel).value(:bool).default(Mihari.config.parallel)
|
18
|
-
end
|
19
|
-
|
20
|
-
AnalyzerOptions = Options | IgnoreErrorOptions | ParallelOptions
|
17
|
+
AnalyzerOptions = Options | IgnoreErrorOptions
|
21
18
|
|
22
19
|
PaginationOptions = Dry::Schema.Params do
|
23
20
|
optional(:pagination_interval).value(:integer).default(Mihari.config.pagination_interval)
|
data/lib/mihari/schemas/rule.rb
CHANGED
@@ -7,27 +7,27 @@ require "mihari/schemas/enricher"
|
|
7
7
|
module Mihari
|
8
8
|
module Schemas
|
9
9
|
Rule = Dry::Schema.Params do
|
10
|
-
required(:id).
|
11
|
-
required(:title).
|
12
|
-
required(:description).
|
10
|
+
required(:id).filled(:string)
|
11
|
+
required(:title).filled(:string)
|
12
|
+
required(:description).filled(:string)
|
13
13
|
|
14
|
-
optional(:
|
14
|
+
optional(:author).filled(:string)
|
15
|
+
optional(:status).filled(:string)
|
15
16
|
|
16
|
-
optional(:
|
17
|
-
optional(:references).
|
18
|
-
optional(:related).
|
19
|
-
optional(:status).value(:string)
|
17
|
+
optional(:tags).array { filled(:string) }.default([])
|
18
|
+
optional(:references).array { filled(:string) }
|
19
|
+
optional(:related).array { filled(:string) }
|
20
20
|
|
21
21
|
optional(:created_on).value(:date)
|
22
22
|
optional(:updated_on).value(:date)
|
23
23
|
|
24
24
|
required(:queries).value(:array).each { Analyzer } # rubocop:disable Lint/Void
|
25
|
-
|
26
25
|
optional(:emitters).value(:array).each { Emitter }.default(DEFAULT_EMITTERS) # rubocop:disable Lint/Void
|
27
26
|
optional(:enrichers).value(:array).each { Enricher }.default(DEFAULT_ENRICHERS) # rubocop:disable Lint/Void
|
28
27
|
|
29
|
-
optional(:data_types).
|
30
|
-
|
28
|
+
optional(:data_types).filled(array[Types::DataTypes]).default(Mihari::Types::DataTypes.values)
|
29
|
+
|
30
|
+
optional(:falsepositives).array { filled(:string) }.default([])
|
31
31
|
|
32
32
|
optional(:artifact_ttl).value(:integer)
|
33
33
|
end
|
@@ -42,7 +42,7 @@ module Mihari
|
|
42
42
|
|
43
43
|
rule(:falsepositives) do
|
44
44
|
value.each do |v|
|
45
|
-
key.failure("#{v} is not a valid format
|
45
|
+
key.failure("#{v} is not a valid format") unless valid_falsepositive?(v)
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|