Class: Mihari::Rule

Inherits:
Service show all
Includes:
Concerns::FalsePositiveValidatable
Defined in:
lib/mihari/rule.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Concerns::FalsePositiveValidatable

#normalize_falsepositive, #valid_falsepositive?

Methods inherited from Service

call, #result, result

Constructor Details

#initialize(**data) ⇒ Rule

Initialize

Parameters:

  • data (Hash)


21
22
23
24
25
26
27
28
29
# File 'lib/mihari/rule.rb', line 21

def initialize(**data)
  super()

  @data = data.deep_symbolize_keys
  @errors = nil
  @base_time = Time.now.utc

  validate!
end

Instance Attribute Details

#base_timeTime (readonly)

Returns:

  • (Time)


14
15
16
# File 'lib/mihari/rule.rb', line 14

def base_time
  @base_time
end

#dataHash (readonly)

Returns:

  • (Hash)


8
9
10
# File 'lib/mihari/rule.rb', line 8

def data
  @data
end

#errorsArray? (readonly)

Returns:

  • (Array, nil)


11
12
13
# File 'lib/mihari/rule.rb', line 11

def errors
  @errors
end

Class Method Details

.from_model(model) ⇒ Mihari::Rule

Parameters:

Returns:



270
271
272
# File 'lib/mihari/rule.rb', line 270

def from_model(model)
  new(**model.data)
end

.from_yaml(yaml) ⇒ Mihari::Rule

Load rule from YAML string

Parameters:

  • yaml (String)

Returns:



260
261
262
263
# File 'lib/mihari/rule.rb', line 260

def from_yaml(yaml)
  data = YAML.safe_load(ERB.new(yaml).result, permitted_classes: [Date, Symbol])
  new(**data)
end

Instance Method Details

#[](key) ⇒ Object



40
41
42
# File 'lib/mihari/rule.rb', line 40

def [](key)
  data key.to_sym
end

#artifact_ttlInteger?

Returns:

  • (Integer, nil)


126
127
128
# File 'lib/mihari/rule.rb', line 126

def artifact_ttl
  data[:artifact_ttl]
end

#artifactsArray<Mihari::Models::Artifact>

Returns a list of artifacts matched with queries/analyzers (with the rule ID)

Returns:



135
136
137
138
139
140
141
142
143
# File 'lib/mihari/rule.rb', line 135

def artifacts
  analyzer_results.flat_map do |result|
    artifacts = result.value!
    artifacts.map do |artifact|
      artifact.rule_id = id
      artifact
    end
  end
end

#bulk_emitArray<Mihari::Models::Alert>

Bulk emit

Returns:



188
189
190
191
192
193
194
# File 'lib/mihari/rule.rb', line 188

def bulk_emit
  return [] if enriched_artifacts.empty?

  Parallel.map(emitters) do |emitter|
    emitter.result(enriched_artifacts).value_or nil
  end.compact
end

#callMihari::Models::Alert?

Set artifacts & run emitters in parallel

Returns:



201
202
203
204
205
206
207
208
209
# File 'lib/mihari/rule.rb', line 201

def call
  # Validate analyzers & emitters before using them
  analyzers
  emitters

  alert_or_something = bulk_emit
  # Return Mihari::Models::Alert created by the database emitter
  alert_or_something.find { |res| res.is_a?(Mihari::Models::Alert) }
end

#created_onDate?

Returns:

  • (Date, nil)


89
90
91
# File 'lib/mihari/rule.rb', line 89

def created_on
  data[:created_on]
end

#data_typesArray<String>

Returns:

  • (Array<String>)


82
83
84
# File 'lib/mihari/rule.rb', line 82

def data_types
  data[:data_types]
end

#descriptionString

Returns:

  • (String)


61
62
63
# File 'lib/mihari/rule.rb', line 61

def description
  data[:description]
end

#diff?Boolean

Returns:

  • (Boolean)


234
235
236
237
238
239
# File 'lib/mihari/rule.rb', line 234

def diff?
  model = Mihari::Models::Rule.find(id)
  model.data != diff_comparable_data
rescue ActiveRecord::RecordNotFound
  false
end

#enriched_artifactsArray<Mihari::Models::Artifact>

Enriched artifacts

Returns:



176
177
178
179
180
181
# File 'lib/mihari/rule.rb', line 176

def enriched_artifacts
  @enriched_artifacts ||= Parallel.map(unique_artifacts) do |artifact|
    enrichers.each { |enricher| artifact.enrich_by_enricher enricher }
    artifact
  end
end

#errors?Boolean

Returns:

  • (Boolean)


34
35
36
37
38
# File 'lib/mihari/rule.rb', line 34

def errors?
  return false if @errors.nil?

  !@errors.empty?
end

#exists?Boolean

Returns:

  • (Boolean)


244
245
246
# File 'lib/mihari/rule.rb', line 244

def exists?
  Mihari::Models::Rule.exists? id
end

#falsepositivesArray<String, RegExp>

Returns:

  • (Array<String, RegExp>)


119
120
121
# File 'lib/mihari/rule.rb', line 119

def falsepositives
  @falsepositives ||= data[:falsepositives].map { |fp| normalize_falsepositive fp }
end

#idString

Returns:

  • (String)


47
48
49
# File 'lib/mihari/rule.rb', line 47

def id
  data[:id]
end

#modelMihari::Models::Rule



214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/mihari/rule.rb', line 214

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: id,
    title: title,
    description: description,
    data: data,
    taggings: taggings
  )
end

#normalized_artifactsArray<Mihari::Models::Artifact>

Normalize artifacts

  • Reject invalid artifacts (for just in case)

  • Select artifacts with allowed data types

  • Reject artifacts with false positive values

  • Set rule ID

Returns:



154
155
156
157
158
# File 'lib/mihari/rule.rb', line 154

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

#queriesArray<Hash>

Returns:

  • (Array<Hash>)


75
76
77
# File 'lib/mihari/rule.rb', line 75

def queries
  data[:queries]
end

#taggingsArray<Mihari::Models::Tagging>

Returns:



112
113
114
# File 'lib/mihari/rule.rb', line 112

def taggings
  tags.map { |tag| Models::Tagging.find_or_create_by(tag_id: tag.id, rule_id: id) }
end

#tagsArray<Mihari::Models::Tag>

Returns:



103
104
105
106
107
# File 'lib/mihari/rule.rb', line 103

def tags
  data[:tags].uniq.filter_map do |name|
    Models::Tag.find_or_create_by(name: name)
  end
end

#titleString

Returns:

  • (String)


54
55
56
# File 'lib/mihari/rule.rb', line 54

def title
  data[:title]
end

#unique_artifactsArray<Mihari::Models::Artifact>

Uniquify artifacts (assure rule level uniqueness)

Returns:



165
166
167
168
169
# File 'lib/mihari/rule.rb', line 165

def unique_artifacts
  normalized_artifacts.select do |artifact|
    artifact.unique?(base_time: base_time, artifact_ttl: artifact_ttl)
  end
end

#update_or_createObject



248
249
250
# File 'lib/mihari/rule.rb', line 248

def update_or_create
  model.save
end

#updated_onDate?

Returns:

  • (Date, nil)


96
97
98
# File 'lib/mihari/rule.rb', line 96

def updated_on
  data[:updated_on]
end

#yamlString

Returns:

  • (String)


68
69
70
# File 'lib/mihari/rule.rb', line 68

def yaml
  data.deep_stringify_keys.to_yaml
end