mihari 5.6.1 → 5.6.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/frontend/package-lock.json +173 -176
- data/frontend/package.json +9 -9
- data/lib/mihari/{base.rb → actor.rb} +16 -2
- data/lib/mihari/analyzers/base.rb +5 -10
- data/lib/mihari/analyzers/censys.rb +1 -1
- data/lib/mihari/analyzers/hunterhow.rb +1 -1
- data/lib/mihari/analyzers/passivetotal.rb +1 -1
- data/lib/mihari/analyzers/pulsedive.rb +1 -1
- data/lib/mihari/analyzers/securitytrails.rb +1 -1
- data/lib/mihari/analyzers/urlscan.rb +1 -1
- data/lib/mihari/analyzers/virustotal.rb +5 -5
- data/lib/mihari/analyzers/zoomeye.rb +3 -3
- data/lib/mihari/clients/crtsh.rb +2 -2
- data/lib/mihari/clients/passivetotal.rb +4 -4
- data/lib/mihari/clients/securitytrails.rb +3 -3
- data/lib/mihari/commands/rule.rb +2 -11
- data/lib/mihari/commands/search.rb +1 -1
- data/lib/mihari/emitters/base.rb +13 -24
- data/lib/mihari/emitters/database.rb +7 -9
- data/lib/mihari/emitters/misp.rb +14 -38
- data/lib/mihari/emitters/slack.rb +14 -11
- data/lib/mihari/emitters/the_hive.rb +16 -44
- data/lib/mihari/emitters/webhook.rb +31 -21
- data/lib/mihari/enrichers/base.rb +1 -6
- data/lib/mihari/enrichers/whois.rb +1 -1
- data/lib/mihari/models/alert.rb +75 -73
- data/lib/mihari/models/artifact.rb +182 -180
- data/lib/mihari/models/autonomous_system.rb +22 -20
- data/lib/mihari/models/cpe.rb +21 -19
- data/lib/mihari/models/dns.rb +24 -22
- data/lib/mihari/models/geolocation.rb +22 -20
- data/lib/mihari/models/port.rb +21 -19
- data/lib/mihari/models/reverse_dns.rb +21 -19
- data/lib/mihari/models/rule.rb +67 -65
- data/lib/mihari/models/tag.rb +5 -3
- data/lib/mihari/models/tagging.rb +5 -3
- data/lib/mihari/models/whois.rb +18 -16
- data/lib/mihari/rule.rb +352 -0
- data/lib/mihari/schemas/analyzer.rb +94 -87
- data/lib/mihari/schemas/emitter.rb +9 -5
- data/lib/mihari/schemas/enricher.rb +8 -4
- data/lib/mihari/schemas/mixins.rb +15 -0
- data/lib/mihari/schemas/rule.rb +3 -10
- data/lib/mihari/services/alert_builder.rb +1 -1
- data/lib/mihari/services/alert_proxy.rb +10 -6
- data/lib/mihari/services/alert_runner.rb +4 -4
- data/lib/mihari/services/rule_builder.rb +3 -3
- data/lib/mihari/services/rule_runner.rb +5 -5
- data/lib/mihari/structs/binaryedge.rb +1 -1
- data/lib/mihari/structs/censys.rb +6 -6
- data/lib/mihari/structs/config.rb +1 -1
- data/lib/mihari/structs/greynoise.rb +5 -5
- data/lib/mihari/structs/hunterhow.rb +3 -3
- data/lib/mihari/structs/onyphe.rb +5 -5
- data/lib/mihari/structs/shodan.rb +6 -6
- data/lib/mihari/structs/urlscan.rb +3 -3
- data/lib/mihari/structs/virustotal_intelligence.rb +3 -3
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/endpoints/alerts.rb +4 -4
- data/lib/mihari/web/endpoints/artifacts.rb +6 -6
- data/lib/mihari/web/endpoints/rules.rb +10 -17
- data/lib/mihari/web/endpoints/tags.rb +2 -2
- data/lib/mihari/web/public/assets/{index-9cc489e6.js → index-28d4c79d.js} +48 -48
- data/lib/mihari/web/public/index.html +1 -1
- data/lib/mihari.rb +6 -8
- data/mihari.gemspec +1 -2
- data/requirements.txt +1 -1
- metadata +8 -22
- data/lib/mihari/analyzers/rule.rb +0 -232
- data/lib/mihari/services/rule_proxy.rb +0 -182
@@ -6,7 +6,7 @@
|
|
6
6
|
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
7
7
|
<link rel="icon" href="/favicon.ico" />
|
8
8
|
<title>Mihari</title>
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
9
|
+
<script type="module" crossorigin src="/assets/index-28d4c79d.js"></script>
|
10
10
|
<link rel="stylesheet" href="/assets/index-56fc2187.css">
|
11
11
|
</head>
|
12
12
|
<body>
|
data/lib/mihari.rb
CHANGED
@@ -35,7 +35,6 @@ require "addressable/uri"
|
|
35
35
|
require "awrence"
|
36
36
|
require "email_address"
|
37
37
|
require "memist"
|
38
|
-
require "net/ping"
|
39
38
|
require "parallel"
|
40
39
|
require "plissken"
|
41
40
|
require "public_suffix"
|
@@ -74,7 +73,7 @@ module Mihari
|
|
74
73
|
#
|
75
74
|
def emitter_to_class
|
76
75
|
@emitter_to_class ||= emitters.flat_map do |klass|
|
77
|
-
klass.class_keys.map { |key| [key
|
76
|
+
klass.class_keys.map { |key| [key, klass] }
|
78
77
|
end.to_h
|
79
78
|
end
|
80
79
|
|
@@ -88,7 +87,7 @@ module Mihari
|
|
88
87
|
#
|
89
88
|
def analyzer_to_class
|
90
89
|
@analyzer_to_class ||= analyzers.flat_map do |klass|
|
91
|
-
klass.class_keys.map { |key| [key
|
90
|
+
klass.class_keys.map { |key| [key, klass] }
|
92
91
|
end.to_h
|
93
92
|
end
|
94
93
|
|
@@ -102,7 +101,7 @@ module Mihari
|
|
102
101
|
#
|
103
102
|
def enricher_to_class
|
104
103
|
@enricher_to_class ||= enrichers.flat_map do |klass|
|
105
|
-
klass.class_keys.map { |key| [key
|
104
|
+
klass.class_keys.map { |key| [key, klass] }
|
106
105
|
end.to_h
|
107
106
|
end
|
108
107
|
|
@@ -131,10 +130,11 @@ module Mihari
|
|
131
130
|
end
|
132
131
|
|
133
132
|
# Core classes
|
134
|
-
require "mihari/
|
133
|
+
require "mihari/actor"
|
135
134
|
require "mihari/database"
|
136
135
|
require "mihari/http"
|
137
136
|
require "mihari/type_checker"
|
137
|
+
require "mihari/rule"
|
138
138
|
|
139
139
|
# Enrichers
|
140
140
|
require "mihari/enrichers/base"
|
@@ -210,8 +210,6 @@ require "mihari/analyzers/virustotal_intelligence"
|
|
210
210
|
require "mihari/analyzers/virustotal"
|
211
211
|
require "mihari/analyzers/zoomeye"
|
212
212
|
|
213
|
-
require "mihari/analyzers/rule"
|
214
|
-
|
215
213
|
# Types
|
216
214
|
require "mihari/types"
|
217
215
|
|
@@ -234,6 +232,7 @@ require "mihari/structs/virustotal_intelligence"
|
|
234
232
|
|
235
233
|
# Schemas
|
236
234
|
require "mihari/schemas/macros"
|
235
|
+
require "mihari/schemas/mixins"
|
237
236
|
|
238
237
|
require "mihari/schemas/options"
|
239
238
|
|
@@ -243,7 +242,6 @@ require "mihari/schemas/rule"
|
|
243
242
|
|
244
243
|
# Services
|
245
244
|
require "mihari/services/rule_builder"
|
246
|
-
require "mihari/services/rule_proxy"
|
247
245
|
require "mihari/services/rule_runner"
|
248
246
|
|
249
247
|
require "mihari/services/alert_builder"
|
data/mihari.gemspec
CHANGED
@@ -48,7 +48,7 @@ Gem::Specification.new do |spec|
|
|
48
48
|
spec.add_development_dependency "rb-fsevent", "~> 0.11"
|
49
49
|
spec.add_development_dependency "rerun", "~> 0.14"
|
50
50
|
spec.add_development_dependency "rspec", "~> 3.12"
|
51
|
-
spec.add_development_dependency "rubocop-rspec", "~> 2.
|
51
|
+
spec.add_development_dependency "rubocop-rspec", "~> 2.25"
|
52
52
|
spec.add_development_dependency "simplecov-lcov", "~> 0.8"
|
53
53
|
spec.add_development_dependency "standard", "~> 1.31"
|
54
54
|
spec.add_development_dependency "test-prof", "~> 1.2"
|
@@ -80,7 +80,6 @@ Gem::Specification.new do |spec|
|
|
80
80
|
spec.add_dependency "jr-cli", "0.6.0"
|
81
81
|
spec.add_dependency "launchy", "2.5.2"
|
82
82
|
spec.add_dependency "memist", "2.0.2"
|
83
|
-
spec.add_dependency "net-ping", "2.0.8"
|
84
83
|
spec.add_dependency "normalize_country", "0.3.2"
|
85
84
|
spec.add_dependency "parallel", "1.23.0"
|
86
85
|
spec.add_dependency "plissken", "2.0.1"
|
data/requirements.txt
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
mkdocs==1.5.3
|
2
|
-
mkdocs-material==9.4.
|
2
|
+
mkdocs-material==9.4.7
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mihari
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.6.
|
4
|
+
version: 5.6.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Manabu Niseki
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-10-
|
11
|
+
date: 2023-10-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -184,14 +184,14 @@ dependencies:
|
|
184
184
|
requirements:
|
185
185
|
- - "~>"
|
186
186
|
- !ruby/object:Gem::Version
|
187
|
-
version: '2.
|
187
|
+
version: '2.25'
|
188
188
|
type: :development
|
189
189
|
prerelease: false
|
190
190
|
version_requirements: !ruby/object:Gem::Requirement
|
191
191
|
requirements:
|
192
192
|
- - "~>"
|
193
193
|
- !ruby/object:Gem::Version
|
194
|
-
version: '2.
|
194
|
+
version: '2.25'
|
195
195
|
- !ruby/object:Gem::Dependency
|
196
196
|
name: simplecov-lcov
|
197
197
|
requirement: !ruby/object:Gem::Requirement
|
@@ -570,20 +570,6 @@ dependencies:
|
|
570
570
|
- - '='
|
571
571
|
- !ruby/object:Gem::Version
|
572
572
|
version: 2.0.2
|
573
|
-
- !ruby/object:Gem::Dependency
|
574
|
-
name: net-ping
|
575
|
-
requirement: !ruby/object:Gem::Requirement
|
576
|
-
requirements:
|
577
|
-
- - '='
|
578
|
-
- !ruby/object:Gem::Version
|
579
|
-
version: 2.0.8
|
580
|
-
type: :runtime
|
581
|
-
prerelease: false
|
582
|
-
version_requirements: !ruby/object:Gem::Requirement
|
583
|
-
requirements:
|
584
|
-
- - '='
|
585
|
-
- !ruby/object:Gem::Version
|
586
|
-
version: 2.0.8
|
587
573
|
- !ruby/object:Gem::Dependency
|
588
574
|
name: normalize_country
|
589
575
|
requirement: !ruby/object:Gem::Requirement
|
@@ -970,6 +956,7 @@ files:
|
|
970
956
|
- frontend/vitest.config.ts
|
971
957
|
- lefthook.yml
|
972
958
|
- lib/mihari.rb
|
959
|
+
- lib/mihari/actor.rb
|
973
960
|
- lib/mihari/analyzers/base.rb
|
974
961
|
- lib/mihari/analyzers/binaryedge.rb
|
975
962
|
- lib/mihari/analyzers/censys.rb
|
@@ -983,14 +970,12 @@ files:
|
|
983
970
|
- lib/mihari/analyzers/otx.rb
|
984
971
|
- lib/mihari/analyzers/passivetotal.rb
|
985
972
|
- lib/mihari/analyzers/pulsedive.rb
|
986
|
-
- lib/mihari/analyzers/rule.rb
|
987
973
|
- lib/mihari/analyzers/securitytrails.rb
|
988
974
|
- lib/mihari/analyzers/shodan.rb
|
989
975
|
- lib/mihari/analyzers/urlscan.rb
|
990
976
|
- lib/mihari/analyzers/virustotal.rb
|
991
977
|
- lib/mihari/analyzers/virustotal_intelligence.rb
|
992
978
|
- lib/mihari/analyzers/zoomeye.rb
|
993
|
-
- lib/mihari/base.rb
|
994
979
|
- lib/mihari/cli/alert.rb
|
995
980
|
- lib/mihari/cli/base.rb
|
996
981
|
- lib/mihari/cli/database.rb
|
@@ -1071,18 +1056,19 @@ files:
|
|
1071
1056
|
- lib/mihari/models/tag.rb
|
1072
1057
|
- lib/mihari/models/tagging.rb
|
1073
1058
|
- lib/mihari/models/whois.rb
|
1059
|
+
- lib/mihari/rule.rb
|
1074
1060
|
- lib/mihari/schemas/alert.rb
|
1075
1061
|
- lib/mihari/schemas/analyzer.rb
|
1076
1062
|
- lib/mihari/schemas/emitter.rb
|
1077
1063
|
- lib/mihari/schemas/enricher.rb
|
1078
1064
|
- lib/mihari/schemas/macros.rb
|
1065
|
+
- lib/mihari/schemas/mixins.rb
|
1079
1066
|
- lib/mihari/schemas/options.rb
|
1080
1067
|
- lib/mihari/schemas/rule.rb
|
1081
1068
|
- lib/mihari/services/alert_builder.rb
|
1082
1069
|
- lib/mihari/services/alert_proxy.rb
|
1083
1070
|
- lib/mihari/services/alert_runner.rb
|
1084
1071
|
- lib/mihari/services/rule_builder.rb
|
1085
|
-
- lib/mihari/services/rule_proxy.rb
|
1086
1072
|
- lib/mihari/services/rule_runner.rb
|
1087
1073
|
- lib/mihari/structs/binaryedge.rb
|
1088
1074
|
- lib/mihari/structs/censys.rb
|
@@ -1110,8 +1096,8 @@ files:
|
|
1110
1096
|
- lib/mihari/web/endpoints/tags.rb
|
1111
1097
|
- lib/mihari/web/middleware/connection_adapter.rb
|
1112
1098
|
- lib/mihari/web/middleware/error_notification_adapter.rb
|
1099
|
+
- lib/mihari/web/public/assets/index-28d4c79d.js
|
1113
1100
|
- lib/mihari/web/public/assets/index-56fc2187.css
|
1114
|
-
- lib/mihari/web/public/assets/index-9cc489e6.js
|
1115
1101
|
- lib/mihari/web/public/assets/mode-yaml-a21faa53.js
|
1116
1102
|
- lib/mihari/web/public/favicon.ico
|
1117
1103
|
- lib/mihari/web/public/index.html
|
@@ -1,232 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Mihari
|
4
|
-
module Analyzers
|
5
|
-
class Rule
|
6
|
-
include Mixins::FalsePositive
|
7
|
-
|
8
|
-
# @return [Mihari::Services::RuleProxy]
|
9
|
-
attr_reader :rule
|
10
|
-
|
11
|
-
# @return [Time]
|
12
|
-
attr_reader :base_time
|
13
|
-
|
14
|
-
#
|
15
|
-
# @param [Mihari::Services::RuleProxy] rule
|
16
|
-
#
|
17
|
-
def initialize(rule)
|
18
|
-
@rule = rule
|
19
|
-
@base_time = Time.now.utc
|
20
|
-
|
21
|
-
validate_analyzer_configurations
|
22
|
-
end
|
23
|
-
|
24
|
-
#
|
25
|
-
# Returns a list of artifacts matched with queries/analyzers (with the rule ID)
|
26
|
-
#
|
27
|
-
# @return [Array<Mihari::Artifact>]
|
28
|
-
#
|
29
|
-
def artifacts
|
30
|
-
analyzers.flat_map do |analyzer|
|
31
|
-
result = analyzer.result
|
32
|
-
|
33
|
-
raise result.failure if result.failure? && !analyzer.ignore_error?
|
34
|
-
|
35
|
-
artifacts = result.value!
|
36
|
-
artifacts.map do |artifact|
|
37
|
-
artifact.rule_id = rule.id
|
38
|
-
artifact
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
#
|
44
|
-
# Normalize artifacts
|
45
|
-
# - Reject invalid artifacts (for just in case)
|
46
|
-
# - Select artifacts with allowed data types
|
47
|
-
# - Reject artifacts with false positive values
|
48
|
-
# - Set rule ID
|
49
|
-
#
|
50
|
-
# @return [Array<Mihari::Artifact>]
|
51
|
-
#
|
52
|
-
def normalized_artifacts
|
53
|
-
valid_artifacts = artifacts.uniq(&:data).select(&:valid?)
|
54
|
-
date_type_allowed_artifacts = valid_artifacts.select { |artifact| rule.data_types.include? artifact.data_type }
|
55
|
-
date_type_allowed_artifacts.reject { |artifact| falsepositive? artifact.data }
|
56
|
-
end
|
57
|
-
|
58
|
-
#
|
59
|
-
# Uniquify artifacts (assure rule level uniqueness)
|
60
|
-
#
|
61
|
-
# @return [Array<Mihari::Artifact>]
|
62
|
-
#
|
63
|
-
def unique_artifacts
|
64
|
-
normalized_artifacts.select do |artifact|
|
65
|
-
artifact.unique?(base_time: base_time, artifact_lifetime: rule.artifact_lifetime)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
#
|
70
|
-
# Enriched artifacts
|
71
|
-
#
|
72
|
-
# @return [Array<Mihari::Artifact>]
|
73
|
-
#
|
74
|
-
def enriched_artifacts
|
75
|
-
@enriched_artifacts ||= Parallel.map(unique_artifacts) do |artifact|
|
76
|
-
enrichers.each { |enricher| artifact.enrich_by_enricher enricher }
|
77
|
-
artifact
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
#
|
82
|
-
# Bulk emit
|
83
|
-
#
|
84
|
-
# @return [Array<Mihari::Alert>]
|
85
|
-
#
|
86
|
-
def bulk_emit
|
87
|
-
return [] if enriched_artifacts.empty?
|
88
|
-
|
89
|
-
# NOTE: separate parallel execution and logging
|
90
|
-
# because the logger does not work along with Parallel
|
91
|
-
results = Parallel.map(valid_emitters) do |emitter|
|
92
|
-
emitter.result
|
93
|
-
end
|
94
|
-
|
95
|
-
results.zip(valid_emitters).map do |result_and_emitter|
|
96
|
-
result, emitter = result_and_emitter
|
97
|
-
|
98
|
-
Mihari.logger.info "Emission by #{emitter.class} is failed: #{result.failure}" if result.failure?
|
99
|
-
Mihari.logger.info "Emission by #{emitter.class} is succeeded" if result.success?
|
100
|
-
|
101
|
-
result.value_or nil
|
102
|
-
end.compact
|
103
|
-
end
|
104
|
-
|
105
|
-
#
|
106
|
-
# Set artifacts & run emitters in parallel
|
107
|
-
#
|
108
|
-
# @return [Mihari::Alert, nil]
|
109
|
-
#
|
110
|
-
def run
|
111
|
-
alert_or_something = bulk_emit
|
112
|
-
# returns Mihari::Alert created by the database emitter
|
113
|
-
alert_or_something.find { |res| res.is_a?(Mihari::Alert) }
|
114
|
-
end
|
115
|
-
|
116
|
-
private
|
117
|
-
|
118
|
-
#
|
119
|
-
# Check whether a value is a falsepositive value or not
|
120
|
-
#
|
121
|
-
# @return [Boolean]
|
122
|
-
#
|
123
|
-
def falsepositive?(value)
|
124
|
-
return true if rule.falsepositives.include?(value)
|
125
|
-
|
126
|
-
regexps = rule.falsepositives.select { |fp| fp.is_a?(Regexp) }
|
127
|
-
regexps.any? { |fp| fp.match?(value) }
|
128
|
-
end
|
129
|
-
|
130
|
-
#
|
131
|
-
# Get analyzer class
|
132
|
-
#
|
133
|
-
# @param [String] key
|
134
|
-
#
|
135
|
-
# @return [Class<Mihari::Analyzers::Base>] analyzer class
|
136
|
-
#
|
137
|
-
def get_analyzer_class(key)
|
138
|
-
raise ArgumentError, "#{key} is not supported" unless Mihari.analyzer_to_class.key?(key)
|
139
|
-
|
140
|
-
Mihari.analyzer_to_class[key]
|
141
|
-
end
|
142
|
-
|
143
|
-
#
|
144
|
-
# @return [Array<Mihari::Analyzers::Base>]
|
145
|
-
#
|
146
|
-
def analyzers
|
147
|
-
rule.queries.map do |query_params|
|
148
|
-
analyzer_name = query_params[:analyzer]
|
149
|
-
klass = get_analyzer_class(analyzer_name)
|
150
|
-
klass.from_query(query_params)
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
#
|
155
|
-
# Get emitter class
|
156
|
-
#
|
157
|
-
# @param [String] key
|
158
|
-
#
|
159
|
-
# @return [Class<Mihari::Emitters::Base>] emitter class
|
160
|
-
#
|
161
|
-
def get_emitter_class(key)
|
162
|
-
raise ArgumentError, "#{key} is not supported" unless Mihari.emitter_to_class.key?(key)
|
163
|
-
|
164
|
-
Mihari.emitter_to_class[key]
|
165
|
-
end
|
166
|
-
|
167
|
-
#
|
168
|
-
# @return [Array<Mihari::Emitters::Base>]
|
169
|
-
#
|
170
|
-
def emitters
|
171
|
-
rule.emitters.map(&:deep_dup).map do |params|
|
172
|
-
name = params[:emitter]
|
173
|
-
options = params[:options]
|
174
|
-
|
175
|
-
%i[emitter options].each { |key| params.delete key }
|
176
|
-
|
177
|
-
klass = get_emitter_class(name)
|
178
|
-
klass.new(artifacts: enriched_artifacts, rule: rule, options: options, **params)
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
#
|
183
|
-
# @return [Array<Mihari::Emitters::Base>]
|
184
|
-
#
|
185
|
-
def valid_emitters
|
186
|
-
@valid_emitters ||= emitters.select(&:valid?)
|
187
|
-
end
|
188
|
-
|
189
|
-
#
|
190
|
-
# Get enricher class
|
191
|
-
#
|
192
|
-
# @param [String] key
|
193
|
-
#
|
194
|
-
# @return [Class<Mihari::Enrichers::Base>] enricher class
|
195
|
-
#
|
196
|
-
def get_enricher_class(key)
|
197
|
-
raise ArgumentError, "#{key} is not supported" unless Mihari.enricher_to_class.key?(key)
|
198
|
-
|
199
|
-
Mihari.enricher_to_class[key]
|
200
|
-
end
|
201
|
-
|
202
|
-
#
|
203
|
-
# @return [Array<Mihari::Enrichers::Base>] enrichers
|
204
|
-
#
|
205
|
-
def enrichers
|
206
|
-
@enrichers ||= rule.enrichers.map(&:deep_dup).map do |params|
|
207
|
-
name = params[:enricher]
|
208
|
-
options = params[:options]
|
209
|
-
|
210
|
-
%i[enricher options].each { |key| params.delete key }
|
211
|
-
|
212
|
-
klass = get_enricher_class(name)
|
213
|
-
klass.new(options: options, **params)
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
#
|
218
|
-
# Validate configuration of analyzers
|
219
|
-
#
|
220
|
-
def validate_analyzer_configurations
|
221
|
-
analyzers.map do |analyzer|
|
222
|
-
next if analyzer.configured?
|
223
|
-
|
224
|
-
joined = analyzer.configuration_keys.join(", ")
|
225
|
-
be = (analyzer.configuration_keys.length > 1) ? "are" : "is"
|
226
|
-
message = "#{analyzer.class.class_key} is not configured correctly. #{joined} #{be} missing."
|
227
|
-
raise ConfigurationError, message
|
228
|
-
end
|
229
|
-
end
|
230
|
-
end
|
231
|
-
end
|
232
|
-
end
|
@@ -1,182 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "json"
|
4
|
-
|
5
|
-
module Mihari
|
6
|
-
module Services
|
7
|
-
#
|
8
|
-
# proxy (or converter) class for rule
|
9
|
-
# proxying rule schema data into analyzer & model
|
10
|
-
#
|
11
|
-
class RuleProxy
|
12
|
-
include Mixins::FalsePositive
|
13
|
-
|
14
|
-
# @return [Hash]
|
15
|
-
attr_reader :data
|
16
|
-
|
17
|
-
# @return [Array, nil]
|
18
|
-
attr_reader :errors
|
19
|
-
|
20
|
-
#
|
21
|
-
# Initialize
|
22
|
-
#
|
23
|
-
# @param [Hash] data
|
24
|
-
#
|
25
|
-
def initialize(data)
|
26
|
-
@data = data.deep_symbolize_keys
|
27
|
-
@errors = nil
|
28
|
-
|
29
|
-
validate!
|
30
|
-
end
|
31
|
-
|
32
|
-
#
|
33
|
-
# @return [Boolean]
|
34
|
-
#
|
35
|
-
def errors?
|
36
|
-
return false if @errors.nil?
|
37
|
-
|
38
|
-
!@errors.empty?
|
39
|
-
end
|
40
|
-
|
41
|
-
def validate!
|
42
|
-
contract = Schemas::RuleContract.new
|
43
|
-
result = contract.call(data)
|
44
|
-
|
45
|
-
@data = result.to_h
|
46
|
-
@errors = result.errors
|
47
|
-
|
48
|
-
raise ValidationError.new("Validation failed", errors) if errors?
|
49
|
-
end
|
50
|
-
|
51
|
-
def [](key)
|
52
|
-
data key.to_sym
|
53
|
-
end
|
54
|
-
|
55
|
-
#
|
56
|
-
# @return [String]
|
57
|
-
#
|
58
|
-
def id
|
59
|
-
@id ||= data[:id]
|
60
|
-
end
|
61
|
-
|
62
|
-
#
|
63
|
-
# @return [String]
|
64
|
-
#
|
65
|
-
def title
|
66
|
-
@title ||= data[:title]
|
67
|
-
end
|
68
|
-
|
69
|
-
#
|
70
|
-
# @return [String]
|
71
|
-
#
|
72
|
-
def description
|
73
|
-
@description ||= data[:description]
|
74
|
-
end
|
75
|
-
|
76
|
-
#
|
77
|
-
# @return [String]
|
78
|
-
#
|
79
|
-
def yaml
|
80
|
-
@yaml ||= data.deep_stringify_keys.to_yaml
|
81
|
-
end
|
82
|
-
|
83
|
-
#
|
84
|
-
# @return [Array<Hash>]
|
85
|
-
#
|
86
|
-
def queries
|
87
|
-
@queries ||= data[:queries]
|
88
|
-
end
|
89
|
-
|
90
|
-
#
|
91
|
-
# @return [Array<String>]
|
92
|
-
#
|
93
|
-
def data_types
|
94
|
-
@data_types ||= data[:data_types]
|
95
|
-
end
|
96
|
-
|
97
|
-
#
|
98
|
-
# @return [Array<String>]
|
99
|
-
#
|
100
|
-
def tags
|
101
|
-
@tags ||= data[:tags]
|
102
|
-
end
|
103
|
-
|
104
|
-
#
|
105
|
-
# @return [Array<String, RegExp>]
|
106
|
-
#
|
107
|
-
def falsepositives
|
108
|
-
@falsepositives ||= data[:falsepositives].map { |fp| normalize_falsepositive fp }
|
109
|
-
end
|
110
|
-
|
111
|
-
#
|
112
|
-
# @return [Array<Hash>]
|
113
|
-
#
|
114
|
-
def emitters
|
115
|
-
@emitters ||= data[:emitters]
|
116
|
-
end
|
117
|
-
|
118
|
-
#
|
119
|
-
# @return [Array<Hash>]
|
120
|
-
#
|
121
|
-
def enrichers
|
122
|
-
@enrichers ||= data[:enrichers]
|
123
|
-
end
|
124
|
-
|
125
|
-
#
|
126
|
-
# @return [Integer, nil]
|
127
|
-
#
|
128
|
-
def artifact_lifetime
|
129
|
-
@artifact_lifetime ||= data[:artifact_lifetime] || data[:artifact_ttl]
|
130
|
-
end
|
131
|
-
|
132
|
-
#
|
133
|
-
# @return [Mihari::Rule]
|
134
|
-
#
|
135
|
-
def model
|
136
|
-
rule = Mihari::Rule.find(id)
|
137
|
-
|
138
|
-
rule.title = title
|
139
|
-
rule.description = description
|
140
|
-
rule.data = data
|
141
|
-
|
142
|
-
rule
|
143
|
-
rescue ActiveRecord::RecordNotFound
|
144
|
-
Mihari::Rule.new(
|
145
|
-
id: id,
|
146
|
-
title: title,
|
147
|
-
description: description,
|
148
|
-
data: data
|
149
|
-
)
|
150
|
-
end
|
151
|
-
|
152
|
-
#
|
153
|
-
# @return [Mihari::Analyzers::Rule]
|
154
|
-
#
|
155
|
-
def analyzer
|
156
|
-
Mihari::Analyzers::Rule.new self
|
157
|
-
end
|
158
|
-
|
159
|
-
class << self
|
160
|
-
#
|
161
|
-
# Load rule from YAML string
|
162
|
-
#
|
163
|
-
# @param [String] yaml
|
164
|
-
#
|
165
|
-
# @return [Mihari::Services::Rule]
|
166
|
-
#
|
167
|
-
def from_yaml(yaml)
|
168
|
-
new YAML.safe_load(ERB.new(yaml).result, permitted_classes: [Date, Symbol])
|
169
|
-
end
|
170
|
-
|
171
|
-
#
|
172
|
-
# @param [Mihari::Rule] model
|
173
|
-
#
|
174
|
-
# @return [Mihari::Services::Rule]
|
175
|
-
#
|
176
|
-
def from_model(model)
|
177
|
-
new model.data
|
178
|
-
end
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
182
|
-
end
|