mihari 5.6.1 → 5.6.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/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
|