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.
- checksums.yaml +4 -4
- data/lib/mihari/analyzers/rule.rb +0 -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/errors.rb +2 -0
- data/lib/mihari/models/alert.rb +6 -1
- 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/structs/filters.rb +71 -0
- data/lib/mihari/structs/ipinfo.rb +4 -4
- data/lib/mihari/structs/rule.rb +188 -144
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/endpoints/alerts.rb +1 -1
- data/lib/mihari/web/endpoints/rules.rb +13 -4
- 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 +1 -2
- data/mihari.gemspec +4 -4
- data/sig/lib/mihari/cli/base.rbs +0 -2
- 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/ipinfo.rbs +2 -2
- data/sig/lib/mihari/structs/rule.rbs +36 -43
- metadata +17 -23
- 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
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7eeecbacefc194f5d2e9356fe8f17884c358b45bb3b7ccbd83a47aa3567ae1f6
|
|
4
|
+
data.tar.gz: d99abc5ef4368bf1a23a48cf0f15a64a33d3539313a624d489887e86a8cc564c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e0347f1c03a949d7e3c85cb9a615247c41bea2a0ac8288f0e3c7a8f9bd5d93ad8f952c99f3b595bf19997fe10e32490898b58a858f63f402a6082344f68cdb87
|
|
7
|
+
data.tar.gz: a507cfb86593c602ba46ef59f279a17ed1a34e433c8e34898a872048306733d26fb1a6546f1ae96605d524ccd80c3c3a0487ab4b4ad66219c0e3a63215ce8c6e
|
data/lib/mihari/commands/init.rb
CHANGED
|
@@ -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 =
|
|
14
|
+
rule = Structs::Rule.from_path_or_id path_or_id
|
|
16
15
|
|
|
17
16
|
# validate
|
|
18
|
-
|
|
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
|
|
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 =
|
|
17
|
+
rule = Structs::Rule.from_path_or_id(path)
|
|
13
18
|
|
|
14
19
|
begin
|
|
15
|
-
|
|
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
data/lib/mihari/models/alert.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
22
|
+
new(country: NormalizeCountry(res.country_code, to: :short), country_code: res.country_code)
|
|
25
23
|
end
|
|
26
24
|
end
|
|
27
25
|
end
|
data/lib/mihari/models/port.rb
CHANGED
data/lib/mihari/models/rule.rb
CHANGED
|
@@ -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
|
|
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,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
|
-
|
|
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
|
-
#
|
|
143
|
-
#
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
data/lib/mihari/version.rb
CHANGED