Class: Mihari::Rule
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
#valid_falsepositive?
#normalize_falsepositive
Methods inherited from Service
call, #get_result, get_result
Constructor Details
#initialize(path_or_id: nil, **data) ⇒ Rule
26
27
28
29
30
31
32
33
34
35
|
# File 'lib/mihari/rule.rb', line 26
def initialize(path_or_id: nil, **data)
super()
@path_or_id = path_or_id
@data = data.deep_symbolize_keys
@errors = nil
@base_time = Time.now.utc
validate!
end
|
Instance Attribute Details
#base_time ⇒ Time
18
19
20
|
# File 'lib/mihari/rule.rb', line 18
def base_time
@base_time
end
|
#data ⇒ Hash
12
13
14
|
# File 'lib/mihari/rule.rb', line 12
def data
@data
end
|
#errors ⇒ Array?
15
16
17
|
# File 'lib/mihari/rule.rb', line 15
def errors
@errors
end
|
#path_or_id ⇒ String?
9
10
11
|
# File 'lib/mihari/rule.rb', line 9
def path_or_id
@path_or_id
end
|
Class Method Details
263
264
265
266
|
# File 'lib/mihari/rule.rb', line 263
def from_file(path)
yaml = File.read(path)
from_yaml(yaml, path: path)
end
|
286
287
288
|
# File 'lib/mihari/rule.rb', line 286
def from_model(model)
new(path_or_id: model.id, **model.data)
end
|
.from_yaml(yaml, path: nil) ⇒ Mihari::Rule
Load rule from YAML string
276
277
278
279
|
# File 'lib/mihari/rule.rb', line 276
def from_yaml(yaml, path: nil)
data = YAML.safe_load(ERB.new(yaml).result, permitted_classes: [Date, Symbol])
new(path_or_id: path, **data)
end
|
Instance Method Details
#[](key) ⇒ Object
46
47
48
|
# File 'lib/mihari/rule.rb', line 46
def [](key)
data key.to_sym
end
|
#artifact_ttl ⇒ Integer?
132
133
134
|
# File 'lib/mihari/rule.rb', line 132
def artifact_ttl
data[:artifact_ttl]
end
|
Returns a list of artifacts matched with queries/analyzers (with the rule ID)
141
142
143
144
145
146
147
148
|
# File 'lib/mihari/rule.rb', line 141
def artifacts
analyzer_results.flat_map do |result|
artifacts = result.value!
artifacts.map do |artifact|
artifact.tap { |tapped| tapped.rule_id = id }
end
end
end
|
190
191
192
193
194
195
196
197
|
# File 'lib/mihari/rule.rb', line 190
def bulk_emit
return [] if enriched_artifacts.empty?
[].tap do |out|
out << serial_emitters.map { |emitter| emitter.get_result(enriched_artifacts).value_or(nil) }
out << Parallel.map(parallel_emitters) { |emitter| emitter.get_result(enriched_artifacts).value_or(nil) }
end.flatten.compact
end
|
Set artifacts & run emitters in parallel
204
205
206
207
208
209
210
211
212
|
# File 'lib/mihari/rule.rb', line 204
def call
analyzers
emitters
alert_or_something = bulk_emit
alert_or_something.find { |res| res.is_a?(Mihari::Models::Alert) }
end
|
#created_on ⇒ Date?
95
96
97
|
# File 'lib/mihari/rule.rb', line 95
def created_on
data[:created_on]
end
|
#data_types ⇒ Array<String>
88
89
90
|
# File 'lib/mihari/rule.rb', line 88
def data_types
data[:data_types]
end
|
#description ⇒ String
67
68
69
|
# File 'lib/mihari/rule.rb', line 67
def description
data[:description]
end
|
#diff? ⇒ Boolean
237
238
239
240
241
242
|
# File 'lib/mihari/rule.rb', line 237
def diff?
model = Mihari::Models::Rule.find(id)
model.data != diff_comparable_data
rescue ActiveRecord::RecordNotFound
false
end
|
179
180
181
182
183
|
# File 'lib/mihari/rule.rb', line 179
def enriched_artifacts
@enriched_artifacts ||= Parallel.map(unique_artifacts) do |artifact|
artifact.enrich_by_enrichers enrichers
end
end
|
#errors? ⇒ Boolean
40
41
42
43
44
|
# File 'lib/mihari/rule.rb', line 40
def errors?
return false if errors.nil?
!errors.empty?
end
|
#exists? ⇒ Boolean
247
248
249
|
# File 'lib/mihari/rule.rb', line 247
def exists?
Mihari::Models::Rule.exists? id
end
|
#falsepositives ⇒ Array<String, RegExp>
125
126
127
|
# File 'lib/mihari/rule.rb', line 125
def falsepositives
@falsepositives ||= data[:falsepositives].map { |fp| normalize_falsepositive fp }
end
|
#id ⇒ String
53
54
55
|
# File 'lib/mihari/rule.rb', line 53
def id
data[:id]
end
|
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
|
# File 'lib/mihari/rule.rb', line 217
def model
Mihari::Models::Rule.find(id).tap do |rule|
rule.title = title
rule.description = description
rule.data = data
rule.taggings = taggings
end
rescue ActiveRecord::RecordNotFound
Mihari::Models::Rule.new(
id:,
title:,
description:,
data:,
taggings:
)
end
|
Normalize artifacts
-
Reject invalid artifacts (for just in case)
-
Select artifacts with allowed data types
-
Reject artifacts with false positive values
-
Set rule ID
159
160
161
162
163
|
# File 'lib/mihari/rule.rb', line 159
def normalized_artifacts
valid_artifacts = artifacts.uniq(&:data).select(&:valid?)
date_type_allowed_artifacts = valid_artifacts.select { |artifact| data_types.include? artifact.data_type }
date_type_allowed_artifacts.reject { |artifact| falsepositive? artifact.data }
end
|
#queries ⇒ Array<Hash>
81
82
83
|
# File 'lib/mihari/rule.rb', line 81
def queries
data[:queries]
end
|
118
119
120
|
# File 'lib/mihari/rule.rb', line 118
def taggings
tags.map { |tag| Models::Tagging.find_or_create_by(tag_id: tag.id, rule_id: id) }
end
|
109
110
111
112
113
|
# File 'lib/mihari/rule.rb', line 109
def tags
data[:tags].uniq.filter_map do |name|
Models::Tag.find_or_create_by(name:)
end
end
|
#title ⇒ String
60
61
62
|
# File 'lib/mihari/rule.rb', line 60
def title
data[:title]
end
|
Uniquify artifacts (assure rule level uniqueness)
170
171
172
|
# File 'lib/mihari/rule.rb', line 170
def unique_artifacts
normalized_artifacts.select { |artifact| artifact.unique?(base_time:, artifact_ttl:) }
end
|
#update_or_create ⇒ Object
251
252
253
|
# File 'lib/mihari/rule.rb', line 251
def update_or_create
model.save
end
|
#updated_on ⇒ Date?
102
103
104
|
# File 'lib/mihari/rule.rb', line 102
def updated_on
data[:updated_on]
end
|
#yaml ⇒ String
74
75
76
|
# File 'lib/mihari/rule.rb', line 74
def yaml
data.deep_stringify_keys.to_yaml
end
|