mihari 4.7.1 → 4.7.2

Sign up to get free protection for your applications and to get access to all the features.
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