mihari 4.6.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.
- checksums.yaml +4 -4
- data/lib/mihari/analyzers/clients/otx.rb +36 -0
- data/lib/mihari/analyzers/otx.rb +19 -11
- data/lib/mihari/analyzers/rule.rb +17 -1
- data/lib/mihari/commands/init.rb +25 -2
- data/lib/mihari/commands/search.rb +2 -7
- data/lib/mihari/commands/validator.rb +10 -5
- data/lib/mihari/constants.rb +2 -0
- data/lib/mihari/enrichers/google_public_dns.rb +36 -0
- data/lib/mihari/enrichers/whois.rb +126 -0
- data/lib/mihari/errors.rb +2 -0
- data/lib/mihari/http.rb +2 -2
- data/lib/mihari/models/alert.rb +6 -1
- data/lib/mihari/models/artifact.rb +30 -0
- data/lib/mihari/models/dns.rb +5 -21
- data/lib/mihari/models/geolocation.rb +2 -4
- data/lib/mihari/models/port.rb +1 -1
- data/lib/mihari/models/rule.rb +7 -2
- data/lib/mihari/models/whois.rb +1 -96
- data/lib/mihari/schemas/enricher.rb +9 -0
- data/lib/mihari/schemas/rule.rb +6 -0
- data/lib/mihari/structs/filters.rb +71 -0
- data/lib/mihari/structs/google_public_dns.rb +42 -0
- data/lib/mihari/structs/ipinfo.rb +4 -4
- data/lib/mihari/structs/rule.rb +187 -137
- data/lib/mihari/types.rb +7 -0
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/endpoints/alerts.rb +1 -1
- data/lib/mihari/web/endpoints/rules.rb +13 -5
- data/lib/mihari/web/public/index.html +1 -1
- data/lib/mihari/web/public/redoc-static.html +796 -763
- data/lib/mihari/web/public/static/css/chunk-vendors.5013d549.css +7 -0
- data/lib/mihari/web/public/static/js/app.3ac3bd7a.js +2 -0
- data/lib/mihari/web/public/static/js/app.3ac3bd7a.js.map +1 -0
- data/lib/mihari/web/public/static/js/{chunk-vendors.dde2116c.js → chunk-vendors.37b7208e.js} +6 -6
- data/lib/mihari/web/public/static/js/chunk-vendors.37b7208e.js.map +1 -0
- data/lib/mihari.rb +4 -2
- data/mihari.gemspec +8 -9
- data/sig/lib/mihari/cli/base.rbs +0 -2
- data/sig/lib/mihari/enrichers/google_public_dns.rbs +18 -0
- data/sig/lib/mihari/models/alert.rbs +3 -3
- data/sig/lib/mihari/models/rule.rbs +2 -2
- data/sig/lib/mihari/structs/filters.rbs +40 -0
- data/sig/lib/mihari/structs/google_public_dns.rbs +21 -0
- data/sig/lib/mihari/structs/ipinfo.rbs +2 -2
- data/sig/lib/mihari/structs/rule.rbs +36 -43
- metadata +32 -45
- data/lib/mihari/mixins/rule.rb +0 -84
- data/lib/mihari/structs/alert.rb +0 -44
- data/lib/mihari/web/public/static/css/chunk-vendors.06251949.css +0 -7
- data/lib/mihari/web/public/static/js/app-legacy.9d5c9c3d.js +0 -2
- data/lib/mihari/web/public/static/js/app-legacy.9d5c9c3d.js.map +0 -1
- data/lib/mihari/web/public/static/js/app.823b5af7.js +0 -2
- data/lib/mihari/web/public/static/js/app.823b5af7.js.map +0 -1
- data/lib/mihari/web/public/static/js/chunk-vendors-legacy.b110c129.js +0 -25
- data/lib/mihari/web/public/static/js/chunk-vendors-legacy.b110c129.js.map +0 -1
- data/lib/mihari/web/public/static/js/chunk-vendors.dde2116c.js.map +0 -1
- data/sig/lib/mihari/mixins/rule.rbs +0 -36
- data/sig/lib/mihari/structs/alert.rbs +0 -27
data/lib/mihari/models/whois.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "whois-parser"
|
4
|
-
|
5
3
|
module Mihari
|
6
4
|
class WhoisRecord < ActiveRecord::Base
|
7
5
|
belongs_to :artifact
|
@@ -17,100 +15,7 @@ module Mihari
|
|
17
15
|
# @return [WhoisRecord, nil]
|
18
16
|
#
|
19
17
|
def build_by_domain(domain)
|
20
|
-
|
21
|
-
|
22
|
-
# check memo
|
23
|
-
if @memo.key?(domain)
|
24
|
-
whois_record = @memo[domain]
|
25
|
-
# return clone of the record
|
26
|
-
return whois_record.dup
|
27
|
-
end
|
28
|
-
|
29
|
-
record = Whois.whois(domain)
|
30
|
-
parser = record.parser
|
31
|
-
|
32
|
-
return nil if parser.available?
|
33
|
-
|
34
|
-
whois_record = new(
|
35
|
-
domain: domain,
|
36
|
-
created_on: get_created_on(parser),
|
37
|
-
updated_on: get_updated_on(parser),
|
38
|
-
expires_on: get_expires_on(parser),
|
39
|
-
registrar: get_registrar(parser),
|
40
|
-
contacts: get_contacts(parser)
|
41
|
-
)
|
42
|
-
# set memo
|
43
|
-
@memo[domain] = whois_record
|
44
|
-
whois_record
|
45
|
-
rescue Whois::Error, Whois::ParserError, Timeout::Error
|
46
|
-
nil
|
47
|
-
end
|
48
|
-
|
49
|
-
private
|
50
|
-
|
51
|
-
#
|
52
|
-
# Get created_on
|
53
|
-
#
|
54
|
-
# @param [::Whois::Parser:] parser
|
55
|
-
#
|
56
|
-
# @return [Date, nil]
|
57
|
-
#
|
58
|
-
def get_created_on(parser)
|
59
|
-
parser.created_on
|
60
|
-
rescue ::Whois::AttributeNotImplemented
|
61
|
-
nil
|
62
|
-
end
|
63
|
-
|
64
|
-
#
|
65
|
-
# Get updated_on
|
66
|
-
#
|
67
|
-
# @param [::Whois::Parser:] parser
|
68
|
-
#
|
69
|
-
# @return [Date, nil]
|
70
|
-
#
|
71
|
-
def get_updated_on(parser)
|
72
|
-
parser.updated_on
|
73
|
-
rescue ::Whois::AttributeNotImplemented
|
74
|
-
nil
|
75
|
-
end
|
76
|
-
|
77
|
-
#
|
78
|
-
# Get expires_on
|
79
|
-
#
|
80
|
-
# @param [::Whois::Parser:] parser
|
81
|
-
#
|
82
|
-
# @return [Date, nil]
|
83
|
-
#
|
84
|
-
def get_expires_on(parser)
|
85
|
-
parser.expires_on
|
86
|
-
rescue ::Whois::AttributeNotImplemented
|
87
|
-
nil
|
88
|
-
end
|
89
|
-
|
90
|
-
#
|
91
|
-
# Get registrar
|
92
|
-
#
|
93
|
-
# @param [::Whois::Parser:] parser
|
94
|
-
#
|
95
|
-
# @return [Hash, nil]
|
96
|
-
#
|
97
|
-
def get_registrar(parser)
|
98
|
-
parser.registrar&.to_h
|
99
|
-
rescue ::Whois::AttributeNotImplemented
|
100
|
-
nil
|
101
|
-
end
|
102
|
-
|
103
|
-
#
|
104
|
-
# Get contacts
|
105
|
-
#
|
106
|
-
# @param [::Whois::Parser:] parser
|
107
|
-
#
|
108
|
-
# @return [Array[Hash], nil]
|
109
|
-
#
|
110
|
-
def get_contacts(parser)
|
111
|
-
parser.contacts.map(&:to_h)
|
112
|
-
rescue ::Whois::AttributeNotImplemented
|
113
|
-
nil
|
18
|
+
Enrichers::Whois.query domain
|
114
19
|
end
|
115
20
|
end
|
116
21
|
end
|
data/lib/mihari/schemas/rule.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require "mihari/schemas/analyzer"
|
4
4
|
require "mihari/schemas/emitter"
|
5
|
+
require "mihari/schemas/enricher"
|
5
6
|
|
6
7
|
module Mihari
|
7
8
|
module Schemas
|
@@ -20,6 +21,8 @@ module Mihari
|
|
20
21
|
|
21
22
|
optional(:emitters).value(:array).each { Emitter | MISP | TheHive | Slack | HTTP }
|
22
23
|
|
24
|
+
optional(:enrichers).value(:array).each(Enricher)
|
25
|
+
|
23
26
|
optional(:allowed_data_types).value(array[Types::DataTypes]).default(ALLOWED_DATA_TYPES)
|
24
27
|
optional(:disallowed_data_values).value(array[:string]).default([])
|
25
28
|
|
@@ -35,6 +38,9 @@ module Mihari
|
|
35
38
|
emitters = h[:emitters]
|
36
39
|
h[:emitters] = emitters || DEFAULT_EMITTERS
|
37
40
|
|
41
|
+
enrichers = h[:enrichers]
|
42
|
+
h[:enrichers] = enrichers || DEFAULT_ENRICHERS
|
43
|
+
|
38
44
|
h
|
39
45
|
end
|
40
46
|
end
|
@@ -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
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mihari
|
4
|
+
module Structs
|
5
|
+
module GooglePublicDNS
|
6
|
+
INT_TYPE_TO_TYPE = {
|
7
|
+
1 => "A",
|
8
|
+
2 => "NS",
|
9
|
+
5 => "CNAME",
|
10
|
+
16 => "TXT",
|
11
|
+
28 => "AAAA"
|
12
|
+
}
|
13
|
+
|
14
|
+
class Answer < Dry::Struct
|
15
|
+
attribute :name, Types::String
|
16
|
+
attribute :data, Types::String
|
17
|
+
attribute :resource_type, Types::String
|
18
|
+
|
19
|
+
def self.from_dynamic!(d)
|
20
|
+
d = Types::Hash[d]
|
21
|
+
resource_type = INT_TYPE_TO_TYPE[d.fetch("type")]
|
22
|
+
new(
|
23
|
+
name: d.fetch("name"),
|
24
|
+
data: d.fetch("data"),
|
25
|
+
resource_type: resource_type
|
26
|
+
)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Response < Dry::Struct
|
31
|
+
attribute :answers, Types.Array(Answer)
|
32
|
+
|
33
|
+
def self.from_dynamic!(d)
|
34
|
+
d = Types::Hash[d]
|
35
|
+
new(
|
36
|
+
answers: d.fetch("Answer", []).map { |x| Answer.from_dynamic!(x) }
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
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
|
26
|
+
loc: d["loc"],
|
27
27
|
hostname: d["hostname"],
|
28
|
-
country_code: d
|
28
|
+
country_code: d["country"],
|
29
29
|
asn: asn
|
30
30
|
)
|
31
31
|
end
|
data/lib/mihari/structs/rule.rb
CHANGED
@@ -1,186 +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
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
30
|
-
|
31
|
-
|
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
|
-
|
34
|
-
|
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
|
-
|
37
|
-
|
78
|
+
raise e
|
79
|
+
end
|
38
80
|
|
39
|
-
|
40
|
-
|
81
|
+
def [](key)
|
82
|
+
data[key.to_sym]
|
83
|
+
end
|
41
84
|
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
-
|
47
|
-
|
92
|
+
#
|
93
|
+
# @return [String]
|
94
|
+
#
|
95
|
+
def title
|
96
|
+
@title ||= data[:title]
|
97
|
+
end
|
48
98
|
|
49
|
-
|
50
|
-
|
99
|
+
#
|
100
|
+
# @return [String]
|
101
|
+
#
|
102
|
+
def description
|
103
|
+
@description ||= data[:description]
|
104
|
+
end
|
51
105
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
-
|
59
|
-
|
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
|
-
# @
|
153
|
+
# @param [Mihari::Rule] model
|
63
154
|
#
|
64
|
-
|
65
|
-
|
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
|
-
|
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
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
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
|
-
|
83
|
-
@errors = result.errors
|
176
|
+
Structs::Rule.new(data, yaml)
|
84
177
|
end
|
85
178
|
|
86
|
-
|
87
|
-
|
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
|
-
|
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
|
-
|
190
|
+
Structs::Rule.from_yaml yaml
|
92
191
|
end
|
93
192
|
|
94
|
-
|
95
|
-
data[key.to_sym]
|
96
|
-
end
|
193
|
+
private
|
97
194
|
|
98
195
|
#
|
99
|
-
#
|
196
|
+
# Load ERR templated YAML
|
100
197
|
#
|
101
|
-
|
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 [
|
200
|
+
# @return [Hash]
|
107
201
|
#
|
108
|
-
def
|
109
|
-
|
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
|
-
#
|
209
|
+
# Load YAML string from path
|
114
210
|
#
|
115
|
-
|
116
|
-
@description ||= data[:description]
|
117
|
-
end
|
118
|
-
|
211
|
+
# @param [String] path
|
119
212
|
#
|
120
|
-
# @return [
|
213
|
+
# @return [String, nil]
|
121
214
|
#
|
122
|
-
def
|
123
|
-
|
215
|
+
def load_yaml_from_file(path)
|
216
|
+
return nil unless Pathname(path).exist?
|
124
217
|
|
125
|
-
|
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
|
-
#
|
222
|
+
# Load YAML string from the database
|
143
223
|
#
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
id
|
154
|
-
)
|
155
|
-
analyzer.ignore_old_artifacts = self[:ignore_old_artifacts]
|
156
|
-
analyzer.ignore_threshold = self[:ignore_threshold]
|
157
|
-
|
158
|
-
analyzer
|
159
|
-
end
|
160
|
-
|
161
|
-
class << self
|
162
|
-
include Mixins::Rule
|
163
|
-
|
164
|
-
#
|
165
|
-
# @param [Mihari::Rule] model
|
166
|
-
#
|
167
|
-
# @return [Mihari::Structs::Rule::Rule]
|
168
|
-
#
|
169
|
-
def from_model(model)
|
170
|
-
data = model.data.deep_symbolize_keys
|
171
|
-
data[:id] = model.id unless data.key?(:id)
|
172
|
-
|
173
|
-
Structs::Rule::Rule.new(data, model.yaml)
|
174
|
-
end
|
175
|
-
|
176
|
-
#
|
177
|
-
# @param [String] yaml
|
178
|
-
#
|
179
|
-
# @return [Mihari::Structs::Rule::Rule]
|
180
|
-
#
|
181
|
-
def from_yaml(yaml)
|
182
|
-
data = load_erb_yaml(yaml)
|
183
|
-
Structs::Rule::Rule.new(data, yaml)
|
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"
|
184
234
|
end
|
185
235
|
end
|
186
236
|
end
|
data/lib/mihari/types.rb
CHANGED
data/lib/mihari/version.rb
CHANGED
@@ -43,7 +43,7 @@ module Mihari
|
|
43
43
|
# symbolize hash keys
|
44
44
|
filter = filter.to_h.symbolize_keys
|
45
45
|
|
46
|
-
search_filter_with_pagenation = Structs::Alert::SearchFilterWithPagination.new(**filter)
|
46
|
+
search_filter_with_pagenation = Structs::Filters::Alert::SearchFilterWithPagination.new(**filter)
|
47
47
|
alerts = Mihari::Alert.search(search_filter_with_pagenation)
|
48
48
|
total = Mihari::Alert.count(search_filter_with_pagenation.without_pagination)
|
49
49
|
|