mihari 4.7.1 → 4.7.2

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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mihari/analyzers/rule.rb +0 -1
  3. data/lib/mihari/commands/init.rb +25 -2
  4. data/lib/mihari/commands/search.rb +2 -7
  5. data/lib/mihari/commands/validator.rb +10 -5
  6. data/lib/mihari/errors.rb +2 -0
  7. data/lib/mihari/models/alert.rb +6 -1
  8. data/lib/mihari/models/geolocation.rb +2 -4
  9. data/lib/mihari/models/port.rb +1 -1
  10. data/lib/mihari/models/rule.rb +7 -2
  11. data/lib/mihari/structs/filters.rb +71 -0
  12. data/lib/mihari/structs/ipinfo.rb +4 -4
  13. data/lib/mihari/structs/rule.rb +188 -144
  14. data/lib/mihari/version.rb +1 -1
  15. data/lib/mihari/web/endpoints/alerts.rb +1 -1
  16. data/lib/mihari/web/endpoints/rules.rb +13 -4
  17. data/lib/mihari/web/public/index.html +1 -1
  18. data/lib/mihari/web/public/redoc-static.html +796 -763
  19. data/lib/mihari/web/public/static/css/chunk-vendors.5013d549.css +7 -0
  20. data/lib/mihari/web/public/static/js/app.3ac3bd7a.js +2 -0
  21. data/lib/mihari/web/public/static/js/app.3ac3bd7a.js.map +1 -0
  22. data/lib/mihari/web/public/static/js/{chunk-vendors.dde2116c.js → chunk-vendors.37b7208e.js} +6 -6
  23. data/lib/mihari/web/public/static/js/chunk-vendors.37b7208e.js.map +1 -0
  24. data/lib/mihari.rb +1 -2
  25. data/mihari.gemspec +4 -4
  26. data/sig/lib/mihari/cli/base.rbs +0 -2
  27. data/sig/lib/mihari/models/alert.rbs +3 -3
  28. data/sig/lib/mihari/models/rule.rbs +2 -2
  29. data/sig/lib/mihari/structs/filters.rbs +40 -0
  30. data/sig/lib/mihari/structs/ipinfo.rbs +2 -2
  31. data/sig/lib/mihari/structs/rule.rbs +36 -43
  32. metadata +17 -23
  33. data/lib/mihari/mixins/rule.rb +0 -84
  34. data/lib/mihari/structs/alert.rb +0 -44
  35. data/lib/mihari/web/public/static/css/chunk-vendors.06251949.css +0 -7
  36. data/lib/mihari/web/public/static/js/app-legacy.9d5c9c3d.js +0 -2
  37. data/lib/mihari/web/public/static/js/app-legacy.9d5c9c3d.js.map +0 -1
  38. data/lib/mihari/web/public/static/js/app.823b5af7.js +0 -2
  39. data/lib/mihari/web/public/static/js/app.823b5af7.js.map +0 -1
  40. data/lib/mihari/web/public/static/js/chunk-vendors-legacy.b110c129.js +0 -25
  41. data/lib/mihari/web/public/static/js/chunk-vendors-legacy.b110c129.js.map +0 -1
  42. data/lib/mihari/web/public/static/js/chunk-vendors.dde2116c.js.map +0 -1
  43. data/sig/lib/mihari/mixins/rule.rbs +0 -36
  44. data/sig/lib/mihari/structs/alert.rbs +0 -27
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 38f68ec091b2469095f321f104c1e238599c56afa7f55763920cfeef0df03ca0
4
- data.tar.gz: 625bbd99f92e4af768b76db15ab72cd53dba6a04150acc3ddfe7feb7d1a82bf7
3
+ metadata.gz: 7eeecbacefc194f5d2e9356fe8f17884c358b45bb3b7ccbd83a47aa3567ae1f6
4
+ data.tar.gz: d99abc5ef4368bf1a23a48cf0f15a64a33d3539313a624d489887e86a8cc564c
5
5
  SHA512:
6
- metadata.gz: fe847e37658ede26d1910e8c73ee1726e2b32335571e03c97c891b2d799380397ed1d32de7e4ed31ae57c861b4a6aa74433454d4871e38e2137100ce342894a1
7
- data.tar.gz: 688c3adcf6ebb754d02b0fecde033602b47fe23ae11fffaff329ce53b93c268ab274319fdb630c819163856ce9708f2658bfe6fe7790e54ab1488e8d2af90245
6
+ metadata.gz: e0347f1c03a949d7e3c85cb9a615247c41bea2a0ac8288f0e3c7a8f9bd5d93ad8f952c99f3b595bf19997fe10e32490898b58a858f63f402a6082344f68cdb87
7
+ data.tar.gz: a507cfb86593c602ba46ef59f279a17ed1a34e433c8e34898a872048306733d26fb1a6546f1ae96605d524ccd80c3c3a0487ab4b4ad66219c0e3a63215ce8c6e
@@ -39,7 +39,6 @@ module Mihari
39
39
 
40
40
  class Rule < Base
41
41
  include Mixins::DisallowedDataValue
42
- include Mixins::Rule
43
42
 
44
43
  option :title
45
44
  option :description
@@ -3,8 +3,6 @@
3
3
  module Mihari
4
4
  module Commands
5
5
  module Initialization
6
- include Mixins::Rule
7
-
8
6
  def self.included(thor)
9
7
  thor.class_eval do
10
8
  desc "rule", "Create a rule file"
@@ -21,6 +19,31 @@ module Mihari
21
19
 
22
20
  Mihari.logger.info "The rule file is initialized as #{filename}."
23
21
  end
22
+
23
+ no_commands do
24
+ #
25
+ # Returns a template for rule
26
+ #
27
+ # @return [String] A template for rule
28
+ #
29
+ def rule_template
30
+ rule = Structs::Rule.from_path_or_id File.expand_path("../templates/rule.yml.erb", __dir__)
31
+ rule.yaml
32
+ end
33
+
34
+ #
35
+ # Create (blank) rule file
36
+ #
37
+ # @param [String] filename
38
+ # @param [Dry::Files] files
39
+ # @param [String] template
40
+ #
41
+ # @return [nil]
42
+ #
43
+ def initialize_rule_yaml(filename, files = Dry::Files.new, template: rule_template)
44
+ files.write(filename, template)
45
+ end
46
+ end
24
47
  end
25
48
  end
26
49
  end
@@ -4,7 +4,6 @@ module Mihari
4
4
  module Commands
5
5
  module Search
6
6
  include Mixins::Database
7
- include Mixins::Rule
8
7
  include Mixins::ErrorNotification
9
8
 
10
9
  def self.included(thor)
@@ -12,14 +11,10 @@ module Mihari
12
11
  desc "search [RULE]", "Search by a rule"
13
12
  method_option :yes, type: :boolean, aliases: "-y", desc: "yes to overwrite the rule in the database"
14
13
  def search_by_rule(path_or_id)
15
- rule = load_rule(path_or_id)
14
+ rule = Structs::Rule.from_path_or_id path_or_id
16
15
 
17
16
  # validate
18
- begin
19
- validate_rule! rule
20
- rescue RuleValidationError => e
21
- raise e
22
- end
17
+ rule.validate!
23
18
 
24
19
  # check update
25
20
  id = rule.id
@@ -3,16 +3,21 @@
3
3
  module Mihari
4
4
  module Commands
5
5
  module Validator
6
- include Mixins::Rule
7
-
8
6
  def self.included(thor)
9
7
  thor.class_eval do
10
- desc "rule [PATH]", "Validate format of a rule file"
8
+ desc "rule [PATH]", "Validate rule file format"
9
+ #
10
+ # Validate format of a rule
11
+ #
12
+ # @param [String] path
13
+ #
14
+ # @return [nil]
15
+ #
11
16
  def rule(path)
12
- rule = load_rule(path)
17
+ rule = Structs::Rule.from_path_or_id(path)
13
18
 
14
19
  begin
15
- validate_rule! rule
20
+ rule.validate!
16
21
  Mihari.logger.info "Valid format. The input is parsed as the following:\n#{rule.data.to_yaml}"
17
22
  rescue RuleValidationError
18
23
  nil
data/lib/mihari/errors.rb CHANGED
@@ -15,6 +15,8 @@ module Mihari
15
15
 
16
16
  class RuleValidationError < Error; end
17
17
 
18
+ class YAMLSyntaxError < Error; end
19
+
18
20
  class ConfigurationError < Error; end
19
21
 
20
22
  class HTTPError < Error; end
@@ -12,7 +12,7 @@ module Mihari
12
12
  #
13
13
  # Search alerts
14
14
  #
15
- # @param [Structs::Alert::SearchFilterWithPagination] filter
15
+ # @param [Structs::Filters::Alert::SearchFilterWithPagination] filter
16
16
  #
17
17
  # @return [Array<Alert>]
18
18
  #
@@ -58,6 +58,11 @@ module Mihari
58
58
 
59
59
  private
60
60
 
61
+ #
62
+ # @param [Structs::Filters::Alert::SearchFilter] filter
63
+ #
64
+ # @return [Mihari::Alert]
65
+ #
61
66
  def build_relation(filter)
62
67
  artifact_ids = []
63
68
  artifact = Artifact.includes(:autonomous_system, :dns_records, :reverse_dns_names)
@@ -17,11 +17,9 @@ module Mihari
17
17
  def build_by_ip(ip)
18
18
  res = Enrichers::IPInfo.query(ip)
19
19
 
20
- unless res.nil?
21
- return new(country: NormalizeCountry(res.country_code, to: :short), country_code: res.country_code)
22
- end
20
+ return nil if res&.country_code.nil?
23
21
 
24
- nil
22
+ new(country: NormalizeCountry(res.country_code, to: :short), country_code: res.country_code)
25
23
  end
26
24
  end
27
25
  end
@@ -14,7 +14,7 @@ module Mihari
14
14
  #
15
15
  def build_by_ip(ip)
16
16
  res = Enrichers::Shodan.query(ip)
17
- return if res.nil?
17
+ return [] if res.nil?
18
18
 
19
19
  res.ports.map { |port| new(port: port) }
20
20
  end
@@ -28,7 +28,7 @@ module Mihari
28
28
  #
29
29
  # Search rules
30
30
  #
31
- # @param [Structs::Rule::SearchFilterWithPagination] filter
31
+ # @param [Structs::Filters::Rule::SearchFilterWithPagination] filter
32
32
  #
33
33
  # @return [Array<Rule>]
34
34
  #
@@ -51,7 +51,7 @@ module Mihari
51
51
  #
52
52
  # Count alerts
53
53
  #
54
- # @param [Structs::Rule::SearchFilterWithPagination] filter
54
+ # @param [Structs::Filters::Rule::SearchFilterWithPagination] filter
55
55
  #
56
56
  # @return [Integer]
57
57
  #
@@ -62,6 +62,11 @@ module Mihari
62
62
 
63
63
  private
64
64
 
65
+ #
66
+ # @param [Structs::Filters::Rule::SearchFilter] filter
67
+ #
68
+ # @return [Mihari::Rule]
69
+ #
65
70
  def build_relation(filter)
66
71
  relation = self
67
72
  relation = relation.includes(alerts: :tags)
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mihari
4
+ module Structs
5
+ module Filters
6
+ module Alert
7
+ class SearchFilter < Dry::Struct
8
+ attribute? :artifact_data, Types::String.optional
9
+ attribute? :description, Types::String.optional
10
+ attribute? :source, Types::String.optional
11
+ attribute? :tag_name, Types::String.optional
12
+ attribute? :title, Types::String.optional
13
+ attribute? :from_at, Types::DateTime.optional
14
+ attribute? :to_at, Types::DateTime.optional
15
+ attribute? :asn, Types::Int.optional
16
+ attribute? :dns_record, Types::String.optional
17
+ attribute? :reverse_dns_name, Types::String.optional
18
+
19
+ def valid_artifact_filters?
20
+ !(artifact_data || asn || dns_record || reverse_dns_name).nil?
21
+ end
22
+ end
23
+
24
+ class SearchFilterWithPagination < SearchFilter
25
+ attribute? :page, Types::Int.default(1)
26
+ attribute? :limit, Types::Int.default(10)
27
+
28
+ def without_pagination
29
+ SearchFilter.new(
30
+ artifact_data: artifact_data,
31
+ description: description,
32
+ from_at: from_at,
33
+ source: source,
34
+ tag_name: tag_name,
35
+ title: title,
36
+ to_at: to_at,
37
+ asn: asn,
38
+ dns_record: dns_record,
39
+ reverse_dns_name: reverse_dns_name
40
+ )
41
+ end
42
+ end
43
+ end
44
+
45
+ module Rule
46
+ class SearchFilter < Dry::Struct
47
+ attribute? :description, Types::String.optional
48
+ attribute? :tag_name, Types::String.optional
49
+ attribute? :title, Types::String.optional
50
+ attribute? :from_at, Types::DateTime.optional
51
+ attribute? :to_at, Types::DateTime.optional
52
+ end
53
+
54
+ class SearchFilterWithPagination < SearchFilter
55
+ attribute? :page, Types::Int.default(1)
56
+ attribute? :limit, Types::Int.default(10)
57
+
58
+ def without_pagination
59
+ SearchFilter.new(
60
+ description: description,
61
+ from_at: from_at,
62
+ tag_name: tag_name,
63
+ title: title,
64
+ to_at: to_at
65
+ )
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -6,8 +6,8 @@ module Mihari
6
6
  class Response < Dry::Struct
7
7
  attribute :ip, Types::String
8
8
  attribute :hostname, Types::String.optional
9
- attribute :loc, Types::String
10
- attribute :country_code, Types::String
9
+ attribute :loc, Types::String.optional
10
+ attribute :country_code, Types::String.optional
11
11
  attribute :asn, Types::Integer.optional
12
12
 
13
13
  class << self
@@ -23,9 +23,9 @@ module Mihari
23
23
 
24
24
  new(
25
25
  ip: d.fetch("ip"),
26
- loc: d.fetch("loc"),
26
+ loc: d["loc"],
27
27
  hostname: d["hostname"],
28
- country_code: d.fetch("country"),
28
+ country_code: d["country"],
29
29
  asn: asn
30
30
  )
31
31
  end
@@ -1,192 +1,236 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "date"
4
+ require "erb"
5
+ require "pathname"
6
+ require "yaml"
7
+
3
8
  module Mihari
4
9
  module Structs
5
- module Rule
6
- class SearchFilter < Dry::Struct
7
- attribute? :description, Types::String.optional
8
- attribute? :tag_name, Types::String.optional
9
- attribute? :title, Types::String.optional
10
- attribute? :from_at, Types::DateTime.optional
11
- attribute? :to_at, Types::DateTime.optional
10
+ class Rule
11
+ # @return [Hash]
12
+ attr_reader :data
13
+
14
+ # @return [String]
15
+ attr_reader :yaml
16
+
17
+ # @return [Array, nil]
18
+ attr_reader :errors
19
+
20
+ # @return [String]
21
+ attr_writer :id
22
+
23
+ def initialize(data, yaml)
24
+ @data = data.deep_symbolize_keys
25
+ @yaml = yaml
26
+
27
+ @errors = nil
28
+ @no_method_error = nil
29
+
30
+ validate
12
31
  end
13
32
 
14
- class SearchFilterWithPagination < SearchFilter
15
- attribute? :page, Types::Int.default(1)
16
- attribute? :limit, Types::Int.default(10)
17
-
18
- def without_pagination
19
- SearchFilter.new(
20
- description: description,
21
- from_at: from_at,
22
- tag_name: tag_name,
23
- title: title,
24
- to_at: to_at
25
- )
33
+ #
34
+ # @return [Boolean]
35
+ #
36
+ def errors?
37
+ return false if @errors.nil?
38
+
39
+ !@errors.empty?
40
+ end
41
+
42
+ #
43
+ # @return [Array<String>]
44
+ #
45
+ def error_messages
46
+ return [] if @errors.nil?
47
+
48
+ @errors.messages.filter_map do |message|
49
+ path = message.path.map(&:to_s).join
50
+ "#{path} #{message.text}"
51
+ rescue NoMethodError
52
+ nil
26
53
  end
27
54
  end
28
55
 
29
- class Rule
30
- # @return [Hash]
31
- attr_reader :data
56
+ def validate
57
+ begin
58
+ contract = Schemas::RuleContract.new
59
+ result = contract.call(data)
60
+ rescue NoMethodError => e
61
+ @no_method_error = e
62
+ return
63
+ end
64
+
65
+ @data = result.to_h
66
+ @errors = result.errors
67
+ end
32
68
 
33
- # @return [String]
34
- attr_reader :yaml
69
+ def validate!
70
+ raise RuleValidationError, "Data should be a hash" unless data.is_a?(Hash)
71
+ raise RuleValidationError, error_messages.join("\n") if errors?
72
+ raise RuleValidationError, "Something wrong with queries, emitters or enrichers." unless @no_method_error.nil?
73
+ rescue RuleValidationError => e
74
+ message = "Failed to parse the input as a rule"
75
+ message += ": #{e.message}" unless e.message.empty?
76
+ Mihari.logger.error message
35
77
 
36
- # @return [Array, nil]
37
- attr_reader :errors
78
+ raise e
79
+ end
38
80
 
39
- # @return [String]
40
- attr_writer :id
81
+ def [](key)
82
+ data[key.to_sym]
83
+ end
41
84
 
42
- def initialize(data, yaml)
43
- @data = data.deep_symbolize_keys
44
- @yaml = yaml
85
+ #
86
+ # @return [String]
87
+ #
88
+ def id
89
+ @id ||= data[:id] || UUIDTools::UUID.md5_create(UUIDTools::UUID_URL_NAMESPACE, data.to_yaml).to_s
90
+ end
45
91
 
46
- @errors = nil
47
- @no_method_error = nil
92
+ #
93
+ # @return [String]
94
+ #
95
+ def title
96
+ @title ||= data[:title]
97
+ end
48
98
 
49
- validate
50
- end
99
+ #
100
+ # @return [String]
101
+ #
102
+ def description
103
+ @description ||= data[:description]
104
+ end
51
105
 
52
- #
53
- # @return [Boolean]
54
- #
55
- def errors?
56
- return false if @errors.nil?
106
+ #
107
+ # @return [Mihari::Rule]
108
+ #
109
+ def to_model
110
+ rule = Mihari::Rule.find(id)
111
+
112
+ rule.title = title
113
+ rule.description = description
114
+ rule.data = data
115
+ rule.yaml = yaml
116
+
117
+ rule
118
+ rescue ActiveRecord::RecordNotFound
119
+ Mihari::Rule.new(
120
+ id: id,
121
+ title: title,
122
+ description: description,
123
+ data: data,
124
+ yaml: yaml
125
+ )
126
+ end
57
127
 
58
- !@errors.empty?
59
- end
128
+ #
129
+ # @return [Mihari::Analyzers::Rule]
130
+ #
131
+ def to_analyzer
132
+ analyzer = Mihari::Analyzers::Rule.new(
133
+ title: self[:title],
134
+ description: self[:description],
135
+ tags: self[:tags],
136
+ queries: self[:queries],
137
+ allowed_data_types: self[:allowed_data_types],
138
+ disallowed_data_values: self[:disallowed_data_values],
139
+ emitters: self[:emitters],
140
+ enrichers: self[:enrichers],
141
+ id: id
142
+ )
143
+ analyzer.ignore_old_artifacts = self[:ignore_old_artifacts]
144
+ analyzer.ignore_threshold = self[:ignore_threshold]
145
+
146
+ analyzer
147
+ end
148
+
149
+ class << self
150
+ include Mixins::Database
60
151
 
61
152
  #
62
- # @return [Array<String>]
153
+ # @param [Mihari::Rule] model
63
154
  #
64
- def error_messages
65
- return [] if @errors.nil?
155
+ # @return [Mihari::Structs::Rule]
156
+ #
157
+ def from_model(model)
158
+ data = model.data.deep_symbolize_keys
159
+ # set ID if YAML data do not have ID
160
+ data[:id] = model.id unless data.key?(:id)
66
161
 
67
- @errors.messages.map do |message|
68
- path = message.path.map(&:to_s).join
69
- "#{path} #{message.text}"
70
- end
162
+ Structs::Rule.new(data, model.yaml)
71
163
  end
72
164
 
73
- def validate
74
- begin
75
- contract = Schemas::RuleContract.new
76
- result = contract.call(data)
77
- rescue NoMethodError => e
78
- @no_method_error = e
79
- return
80
- end
165
+ #
166
+ # @param [String] yaml
167
+ #
168
+ # @return [Mihari::Structs::Rule]
169
+ # @param [String, nil] id
170
+ #
171
+ def from_yaml(yaml, id: nil)
172
+ data = load_erb_yaml(yaml)
173
+ # set ID if id is given & YAML data do not have ID
174
+ data[:id] = id if !id.nil? && !data.key?(:id)
81
175
 
82
- @data = result.to_h
83
- @errors = result.errors
176
+ Structs::Rule.new(data, yaml)
84
177
  end
85
178
 
86
- def validate!
87
- raise RuleValidationError, "Data should be a hash" unless data.is_a?(Hash)
179
+ #
180
+ # @param [String] path_or_id Path to YAML file or YAML string or ID of a rule in the database
181
+ #
182
+ # @return [Mihari::Structs::Rule]
183
+ #
184
+ def from_path_or_id(path_or_id)
185
+ yaml = nil
88
186
 
89
- raise RuleValidationError, error_messages.join("\n") if errors?
187
+ yaml = load_yaml_from_file(path_or_id) if File.exist?(path_or_id)
188
+ yaml = load_yaml_from_db(path_or_id) if yaml.nil?
90
189
 
91
- raise RuleValidationError, "Something wrong with queries, emitters or enrichers." unless @no_method_error.nil?
190
+ Structs::Rule.from_yaml yaml
92
191
  end
93
192
 
94
- def [](key)
95
- data[key.to_sym]
96
- end
193
+ private
97
194
 
98
195
  #
99
- # @return [String]
196
+ # Load ERR templated YAML
100
197
  #
101
- def id
102
- @id ||= data[:id] || UUIDTools::UUID.md5_create(UUIDTools::UUID_URL_NAMESPACE, data.to_yaml).to_s
103
- end
104
-
198
+ # @param [String] yaml
105
199
  #
106
- # @return [String]
200
+ # @return [Hash]
107
201
  #
108
- def title
109
- @title ||= data[:title]
202
+ def load_erb_yaml(yaml)
203
+ YAML.safe_load(ERB.new(yaml).result, permitted_classes: [Date, Symbol], symbolize_names: true)
204
+ rescue Psych::SyntaxError => e
205
+ raise YAMLSyntaxError, e.message
110
206
  end
111
207
 
112
208
  #
113
- # @return [String]
209
+ # Load YAML string from path
114
210
  #
115
- def description
116
- @description ||= data[:description]
117
- end
118
-
211
+ # @param [String] path
119
212
  #
120
- # @return [Mihari::Rule]
213
+ # @return [String, nil]
121
214
  #
122
- def to_model
123
- rule = Mihari::Rule.find(id)
215
+ def load_yaml_from_file(path)
216
+ return nil unless Pathname(path).exist?
124
217
 
125
- rule.title = title
126
- rule.description = description
127
- rule.data = data
128
- rule.yaml = yaml
129
-
130
- rule
131
- rescue ActiveRecord::RecordNotFound
132
- Mihari::Rule.new(
133
- id: id,
134
- title: title,
135
- description: description,
136
- data: data,
137
- yaml: yaml
138
- )
218
+ File.read path
139
219
  end
140
220
 
141
221
  #
142
- # @return [Mihari::Analyzers::Rule]
143
- #
144
- def to_analyzer
145
- analyzer = Mihari::Analyzers::Rule.new(
146
- title: self[:title],
147
- description: self[:description],
148
- tags: self[:tags],
149
- queries: self[:queries],
150
- allowed_data_types: self[:allowed_data_types],
151
- disallowed_data_values: self[:disallowed_data_values],
152
- emitters: self[:emitters],
153
- enrichers: self[:enrichers],
154
- id: id
155
- )
156
- analyzer.ignore_old_artifacts = self[:ignore_old_artifacts]
157
- analyzer.ignore_threshold = self[:ignore_threshold]
158
-
159
- analyzer
160
- end
161
-
162
- class << self
163
- include Mixins::Rule
164
-
165
- #
166
- # @param [Mihari::Rule] model
167
- #
168
- # @return [Mihari::Structs::Rule::Rule]
169
- #
170
- def from_model(model)
171
- data = model.data.deep_symbolize_keys
172
- # set ID if YAML data do not have ID
173
- data[:id] = model.id unless data.key?(:id)
174
-
175
- Structs::Rule::Rule.new(data, model.yaml)
176
- end
177
-
178
- #
179
- # @param [String] yaml
180
- #
181
- # @return [Mihari::Structs::Rule::Rule]
182
- # @param [String, nil] id
183
- #
184
- def from_yaml(yaml, id: nil)
185
- data = load_erb_yaml(yaml)
186
- # set ID if id is given & YAML data do not have ID
187
- data[:id] = id if !id.nil? && !data.key?(:id)
188
-
189
- Structs::Rule::Rule.new(data, yaml)
222
+ # Load YAML string from the database
223
+ #
224
+ # @param [String] id <description>
225
+ #
226
+ # @return [Hash]
227
+ #
228
+ def load_yaml_from_db(id)
229
+ with_db_connection do
230
+ rule = Mihari::Rule.find(id)
231
+ rule.yaml || rule.symbolized_data.to_yaml
232
+ rescue ActiveRecord::RecordNotFound
233
+ raise ArgumentError, "ID:#{id} is not found in the database"
190
234
  end
191
235
  end
192
236
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- VERSION = "4.7.1"
4
+ VERSION = "4.7.2"
5
5
  end