Class: Mihari::Rule
- Defined in:
- lib/mihari/rule.rb
Instance Attribute Summary collapse
- #base_time ⇒ Time readonly
- #data ⇒ Hash readonly
- #errors ⇒ Array? readonly
Class Method Summary collapse
- .from_model(model) ⇒ Mihari::Rule
-
.from_yaml(yaml) ⇒ Mihari::Rule
Load rule from YAML string.
Instance Method Summary collapse
- #[](key) ⇒ Object
- #artifact_ttl ⇒ Integer?
-
#artifacts ⇒ Array<Mihari::Models::Artifact>
Returns a list of artifacts matched with queries/analyzers (with the rule ID).
-
#bulk_emit ⇒ Array<Mihari::Models::Alert>
Bulk emit.
-
#call ⇒ Mihari::Models::Alert?
Set artifacts & run emitters in parallel.
- #created_on ⇒ Date?
- #data_types ⇒ Array<String>
- #description ⇒ String
- #diff? ⇒ Boolean
-
#enriched_artifacts ⇒ Array<Mihari::Models::Artifact>
Enriched artifacts.
- #errors? ⇒ Boolean
- #exists? ⇒ Boolean
- #falsepositives ⇒ Array<String, RegExp>
- #id ⇒ String
-
#initialize(**data) ⇒ Rule
constructor
Initialize.
- #model ⇒ Mihari::Models::Rule
-
#normalized_artifacts ⇒ Array<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.
- #queries ⇒ Array<Hash>
- #taggings ⇒ Array<Mihari::Models::Tagging>
- #tags ⇒ Array<Mihari::Models::Tag>
- #title ⇒ String
-
#unique_artifacts ⇒ Array<Mihari::Models::Artifact>
Uniquify artifacts (assure rule level uniqueness).
- #update_or_create ⇒ Object
- #updated_on ⇒ Date?
- #yaml ⇒ String
Methods included from Concerns::FalsePositiveValidatable
Methods included from Concerns::FalsePositiveNormalizable
Methods inherited from Service
Constructor Details
#initialize(**data) ⇒ Rule
Initialize
22 23 24 25 26 27 28 29 30 |
# File 'lib/mihari/rule.rb', line 22 def initialize(**data) super() @data = data.deep_symbolize_keys @errors = nil @base_time = Time.now.utc validate! end |
Instance Attribute Details
#base_time ⇒ Time (readonly)
15 16 17 |
# File 'lib/mihari/rule.rb', line 15 def base_time @base_time end |
#data ⇒ Hash (readonly)
9 10 11 |
# File 'lib/mihari/rule.rb', line 9 def data @data end |
#errors ⇒ Array? (readonly)
12 13 14 |
# File 'lib/mihari/rule.rb', line 12 def errors @errors end |
Class Method Details
.from_model(model) ⇒ Mihari::Rule
271 272 273 |
# File 'lib/mihari/rule.rb', line 271 def from_model(model) new(**model.data) end |
.from_yaml(yaml) ⇒ Mihari::Rule
Load rule from YAML string
261 262 263 264 |
# File 'lib/mihari/rule.rb', line 261 def from_yaml(yaml) data = YAML.safe_load(ERB.new(yaml).result, permitted_classes: [Date, Symbol]) new(**data) end |
Instance Method Details
#[](key) ⇒ Object
41 42 43 |
# File 'lib/mihari/rule.rb', line 41 def [](key) data key.to_sym end |
#artifact_ttl ⇒ Integer?
127 128 129 |
# File 'lib/mihari/rule.rb', line 127 def artifact_ttl data[:artifact_ttl] end |
#artifacts ⇒ Array<Mihari::Models::Artifact>
Returns a list of artifacts matched with queries/analyzers (with the rule ID)
136 137 138 139 140 141 142 143 |
# File 'lib/mihari/rule.rb', line 136 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 |
#bulk_emit ⇒ Array<Mihari::Models::Alert>
Bulk emit
188 189 190 191 192 193 194 195 |
# File 'lib/mihari/rule.rb', line 188 def bulk_emit return [] if enriched_artifacts.empty? [].tap do |out| out << serial_emitters.map { |emitter| emitter.result(enriched_artifacts).value_or(nil) } out << Parallel.map(parallel_emitters) { |emitter| emitter.result(enriched_artifacts).value_or(nil) } end.flatten.compact end |
#call ⇒ Mihari::Models::Alert?
Set artifacts & run emitters in parallel
202 203 204 205 206 207 208 209 210 |
# File 'lib/mihari/rule.rb', line 202 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_on ⇒ Date?
90 91 92 |
# File 'lib/mihari/rule.rb', line 90 def created_on data[:created_on] end |
#data_types ⇒ Array<String>
83 84 85 |
# File 'lib/mihari/rule.rb', line 83 def data_types data[:data_types] end |
#description ⇒ String
62 63 64 |
# File 'lib/mihari/rule.rb', line 62 def description data[:description] end |
#diff? ⇒ Boolean
235 236 237 238 239 240 |
# File 'lib/mihari/rule.rb', line 235 def diff? model = Mihari::Models::Rule.find(id) model.data != diff_comparable_data rescue ActiveRecord::RecordNotFound false end |
#enriched_artifacts ⇒ Array<Mihari::Models::Artifact>
Enriched artifacts
174 175 176 177 178 179 180 181 |
# File 'lib/mihari/rule.rb', line 174 def enriched_artifacts @enriched_artifacts ||= Parallel.map(unique_artifacts) do |artifact| artifact.tap do |tapped| # NOTE: To apply changes correctly, enrichers should be applied to an artifact serially enrichers.each { |enricher| enricher.result(tapped) } end end end |
#errors? ⇒ Boolean
35 36 37 38 39 |
# File 'lib/mihari/rule.rb', line 35 def errors? return false if errors.nil? !errors.empty? end |
#exists? ⇒ Boolean
245 246 247 |
# File 'lib/mihari/rule.rb', line 245 def exists? Mihari::Models::Rule.exists? id end |
#falsepositives ⇒ Array<String, RegExp>
120 121 122 |
# File 'lib/mihari/rule.rb', line 120 def falsepositives @falsepositives ||= data[:falsepositives].map { |fp| normalize_falsepositive fp } end |
#id ⇒ String
48 49 50 |
# File 'lib/mihari/rule.rb', line 48 def id data[:id] end |
#model ⇒ Mihari::Models::Rule
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
# File 'lib/mihari/rule.rb', line 215 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 |
#normalized_artifacts ⇒ Array<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
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 |
#queries ⇒ Array<Hash>
76 77 78 |
# File 'lib/mihari/rule.rb', line 76 def queries data[:queries] end |
#taggings ⇒ Array<Mihari::Models::Tagging>
113 114 115 |
# File 'lib/mihari/rule.rb', line 113 def taggings .map { |tag| Models::Tagging.find_or_create_by(tag_id: tag.id, rule_id: id) } end |
#tags ⇒ Array<Mihari::Models::Tag>
104 105 106 107 108 |
# File 'lib/mihari/rule.rb', line 104 def data[:tags].uniq.filter_map do |name| Models::Tag.find_or_create_by(name:) end end |
#title ⇒ String
55 56 57 |
# File 'lib/mihari/rule.rb', line 55 def title data[:title] end |
#unique_artifacts ⇒ Array<Mihari::Models::Artifact>
Uniquify artifacts (assure rule level uniqueness)
165 166 167 |
# File 'lib/mihari/rule.rb', line 165 def unique_artifacts normalized_artifacts.select { |artifact| artifact.unique?(base_time:, artifact_ttl:) } end |
#update_or_create ⇒ Object
249 250 251 |
# File 'lib/mihari/rule.rb', line 249 def update_or_create model.save end |
#updated_on ⇒ Date?
97 98 99 |
# File 'lib/mihari/rule.rb', line 97 def updated_on data[:updated_on] end |
#yaml ⇒ String
69 70 71 |
# File 'lib/mihari/rule.rb', line 69 def yaml data.deep_stringify_keys.to_yaml end |