Class: Mihari::Rule
- Defined in:
- lib/mihari/rule.rb
Instance Attribute Summary collapse
- #base_time ⇒ Time readonly
- #data ⇒ Hash readonly
- #errors ⇒ Array? readonly
- #path_or_id ⇒ String? readonly
Class Method Summary collapse
-
.from_file(path) ⇒ Mihari::Rule
Load rule from YAML file.
- .from_model(model) ⇒ Mihari::Rule
-
.from_yaml(yaml, path: nil) ⇒ 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(path_or_id: nil, **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(path_or_id: nil, **data) ⇒ Rule
Initialize
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 (readonly)
18 19 20 |
# File 'lib/mihari/rule.rb', line 18 def base_time @base_time end |
#data ⇒ Hash (readonly)
12 13 14 |
# File 'lib/mihari/rule.rb', line 12 def data @data end |
#errors ⇒ Array? (readonly)
15 16 17 |
# File 'lib/mihari/rule.rb', line 15 def errors @errors end |
#path_or_id ⇒ String? (readonly)
9 10 11 |
# File 'lib/mihari/rule.rb', line 9 def path_or_id @path_or_id end |
Class Method Details
.from_file(path) ⇒ Mihari::Rule
Load rule from YAML file
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 |
.from_model(model) ⇒ Mihari::Rule
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 |
#artifacts ⇒ Array<Mihari::Models::Artifact>
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 |
#bulk_emit ⇒ Array<Mihari::Models::Alert>
Bulk emit
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.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
204 205 206 207 208 209 210 211 212 |
# File 'lib/mihari/rule.rb', line 204 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?
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 |
#enriched_artifacts ⇒ Array<Mihari::Models::Artifact>
Enriched artifacts
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 |
#model ⇒ Mihari::Models::Rule
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 |
#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
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 |
#taggings ⇒ Array<Mihari::Models::Tagging>
118 119 120 |
# File 'lib/mihari/rule.rb', line 118 def taggings .map { |tag| Models::Tagging.find_or_create_by(tag_id: tag.id, rule_id: id) } end |
#tags ⇒ Array<Mihari::Models::Tag>
109 110 111 112 113 |
# File 'lib/mihari/rule.rb', line 109 def 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 |
#unique_artifacts ⇒ Array<Mihari::Models::Artifact>
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 |