mihari 5.5.0 → 5.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/docs/analyzers/passivetotal.md +4 -0
- data/docs/analyzers/securitytrails.md +4 -0
- data/docs/analyzers/virustotal.md +4 -0
- data/docs/analyzers/virustotal_intelligence.md +4 -0
- data/docs/emitters/hive.md +1 -1
- data/docs/emitters/slack.md +0 -5
- data/docs/rule.md +1 -4
- data/docs/usage.md +5 -2
- data/frontend/src/components/ErrorMessage.vue +0 -1
- data/frontend/src/components/alert/Alerts.vue +0 -1
- data/frontend/src/components/alert/AlertsWithPagination.vue +0 -1
- data/frontend/src/components/alert/AlertsWrapper.vue +0 -6
- data/frontend/src/components/alert/Form.vue +1 -3
- data/frontend/src/components/artifact/Artifact.vue +0 -17
- data/frontend/src/components/artifact/ArtifactWrapper.vue +0 -2
- data/frontend/src/components/artifact/WhoisRecord.vue +0 -3
- data/frontend/src/components/config/ConfigsWrapper.vue +0 -2
- data/frontend/src/components/rule/EditRule.vue +0 -3
- data/frontend/src/components/rule/EditRuleWrapper.vue +0 -2
- data/frontend/src/components/rule/Form.vue +1 -3
- data/frontend/src/components/rule/NewRule.vue +0 -3
- data/frontend/src/components/rule/Rule.vue +1 -7
- data/frontend/src/components/rule/RuleWrapper.vue +0 -2
- data/frontend/src/components/rule/RulesWrapper.vue +0 -6
- data/frontend/src/swagger.yaml +254 -254
- data/lib/mihari/analyzers/base.rb +4 -41
- data/lib/mihari/analyzers/passivetotal.rb +9 -0
- data/lib/mihari/analyzers/pulsedive.rb +1 -1
- data/lib/mihari/analyzers/rule.rb +24 -59
- data/lib/mihari/analyzers/securitytrails.rb +9 -0
- data/lib/mihari/analyzers/virustotal.rb +11 -2
- data/lib/mihari/analyzers/virustotal_intelligence.rb +16 -0
- data/lib/mihari/analyzers/zoomeye.rb +2 -2
- data/lib/mihari/base.rb +69 -0
- data/lib/mihari/cli/main.rb +36 -0
- data/lib/mihari/commands/alert.rb +6 -33
- data/lib/mihari/commands/rule.rb +7 -12
- data/lib/mihari/commands/search.rb +10 -38
- data/lib/mihari/constants.rb +3 -3
- data/lib/mihari/emitters/base.rb +3 -33
- data/lib/mihari/emitters/database.rb +1 -1
- data/lib/mihari/enrichers/base.rb +2 -33
- data/lib/mihari/enrichers/google_public_dns.rb +9 -0
- data/lib/mihari/schemas/analyzer.rb +24 -24
- data/lib/mihari/schemas/emitter.rb +6 -13
- data/lib/mihari/schemas/enricher.rb +4 -11
- data/lib/mihari/schemas/options.rb +27 -0
- data/lib/mihari/schemas/rule.rb +2 -2
- data/lib/mihari/services/alert_runner.rb +1 -1
- data/lib/mihari/services/rule_runner.rb +1 -11
- data/lib/mihari/types.rb +1 -14
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/public/assets/{index-33165282.css → index-56fc2187.css} +1 -1
- data/lib/mihari/web/public/assets/{index-b5d817a3.js → index-9cc489e6.js} +2 -2
- data/lib/mihari/web/public/index.html +2 -2
- data/lib/mihari.rb +67 -37
- data/mihari.gemspec +1 -0
- metadata +20 -4
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Mihari
|
4
4
|
module Analyzers
|
5
|
-
class Base
|
5
|
+
class Base < Mihari::Base
|
6
6
|
include Dry::Monads[:result, :try]
|
7
7
|
|
8
8
|
include Mixins::Configurable
|
@@ -11,37 +11,14 @@ module Mihari
|
|
11
11
|
# @return [String]
|
12
12
|
attr_reader :query
|
13
13
|
|
14
|
-
# @return [Hash]
|
15
|
-
attr_reader :options
|
16
|
-
|
17
14
|
#
|
18
15
|
# @param [String] query
|
19
16
|
# @param [Hash, nil] options
|
20
17
|
#
|
21
18
|
def initialize(query, options: nil)
|
22
|
-
|
23
|
-
@options = options || {}
|
24
|
-
end
|
25
|
-
|
26
|
-
#
|
27
|
-
# @return [Integer]
|
28
|
-
#
|
29
|
-
def retry_interval
|
30
|
-
options[:retry_interval] || Mihari.config.retry_interval
|
31
|
-
end
|
32
|
-
|
33
|
-
#
|
34
|
-
# @return [Boolean]
|
35
|
-
#
|
36
|
-
def retry_exponential_backoff
|
37
|
-
options[:retry_exponential_backoff] || Mihari.config.retry_exponential_backoff
|
38
|
-
end
|
19
|
+
super(options: options)
|
39
20
|
|
40
|
-
|
41
|
-
# @return [Integer]
|
42
|
-
#
|
43
|
-
def retry_times
|
44
|
-
options[:retry_times] || Mihari.config.retry_times
|
21
|
+
@query = query
|
45
22
|
end
|
46
23
|
|
47
24
|
#
|
@@ -68,13 +45,6 @@ module Mihari
|
|
68
45
|
Mihari.config.ignore_error
|
69
46
|
end
|
70
47
|
|
71
|
-
#
|
72
|
-
# @return [Integer, nil]
|
73
|
-
#
|
74
|
-
def timeout
|
75
|
-
options[:timeout]
|
76
|
-
end
|
77
|
-
|
78
48
|
# @return [Array<String>, Array<Mihari::Artifact>]
|
79
49
|
def artifacts
|
80
50
|
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
@@ -93,7 +63,7 @@ module Mihari
|
|
93
63
|
# No need to set data_type manually
|
94
64
|
# It is set automatically in #initialize
|
95
65
|
artifact = artifact.is_a?(Artifact) ? artifact : Artifact.new(data: artifact)
|
96
|
-
artifact.source =
|
66
|
+
artifact.source = self.class.class_key
|
97
67
|
artifact
|
98
68
|
end.select(&:valid?).uniq(&:data)
|
99
69
|
end
|
@@ -106,13 +76,6 @@ module Mihari
|
|
106
76
|
Try[StandardError] { normalized_artifacts }.to_result
|
107
77
|
end
|
108
78
|
|
109
|
-
# @return [String]
|
110
|
-
def class_name
|
111
|
-
self.class.to_s.split("::").last
|
112
|
-
end
|
113
|
-
|
114
|
-
alias_method :source, :class_name
|
115
|
-
|
116
79
|
class << self
|
117
80
|
#
|
118
81
|
# Initialize an analyzer by query params
|
@@ -2,46 +2,6 @@
|
|
2
2
|
|
3
3
|
module Mihari
|
4
4
|
module Analyzers
|
5
|
-
ANALYZER_TO_CLASS = {
|
6
|
-
"binaryedge" => BinaryEdge,
|
7
|
-
"censys" => Censys,
|
8
|
-
"circl" => CIRCL,
|
9
|
-
"crtsh" => Crtsh,
|
10
|
-
"dnstwister" => DNSTwister,
|
11
|
-
"feed" => Feed,
|
12
|
-
"greynoise" => GreyNoise,
|
13
|
-
"hunterhow" => HunterHow,
|
14
|
-
"onyphe" => Onyphe,
|
15
|
-
"otx" => OTX,
|
16
|
-
"passivetotal" => PassiveTotal,
|
17
|
-
"pt" => PassiveTotal,
|
18
|
-
"pulsedive" => Pulsedive,
|
19
|
-
"securitytrails" => SecurityTrails,
|
20
|
-
"shodan" => Shodan,
|
21
|
-
"st" => SecurityTrails,
|
22
|
-
"urlscan" => Urlscan,
|
23
|
-
"virustotal_intelligence" => VirusTotalIntelligence,
|
24
|
-
"virustotal" => VirusTotal,
|
25
|
-
"vt_intel" => VirusTotalIntelligence,
|
26
|
-
"vt" => VirusTotal,
|
27
|
-
"zoomeye" => ZoomEye
|
28
|
-
}.freeze
|
29
|
-
|
30
|
-
EMITTER_TO_CLASS = {
|
31
|
-
"database" => Emitters::Database,
|
32
|
-
"misp" => Emitters::MISP,
|
33
|
-
"slack" => Emitters::Slack,
|
34
|
-
"the_hive" => Emitters::TheHive,
|
35
|
-
"webhook" => Emitters::Webhook
|
36
|
-
}.freeze
|
37
|
-
|
38
|
-
ENRICHER_TO_CLASS = {
|
39
|
-
"whois" => Enrichers::Whois,
|
40
|
-
"ipinfo" => Enrichers::IPInfo,
|
41
|
-
"shodan" => Enrichers::Shodan,
|
42
|
-
"google_public_dns" => Enrichers::GooglePublicDNS
|
43
|
-
}.freeze
|
44
|
-
|
45
5
|
class Rule
|
46
6
|
include Mixins::FalsePositive
|
47
7
|
|
@@ -126,8 +86,14 @@ module Mihari
|
|
126
86
|
def bulk_emit
|
127
87
|
return [] if enriched_artifacts.empty?
|
128
88
|
|
129
|
-
|
130
|
-
|
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
|
131
97
|
|
132
98
|
Mihari.logger.info "Emission by #{emitter.class} is failed: #{result.failure}" if result.failure?
|
133
99
|
Mihari.logger.info "Emission by #{emitter.class} is succeeded" if result.success?
|
@@ -164,15 +130,14 @@ module Mihari
|
|
164
130
|
#
|
165
131
|
# Get analyzer class
|
166
132
|
#
|
167
|
-
# @param [String]
|
133
|
+
# @param [String] key
|
168
134
|
#
|
169
135
|
# @return [Class<Mihari::Analyzers::Base>] analyzer class
|
170
136
|
#
|
171
|
-
def get_analyzer_class(
|
172
|
-
|
173
|
-
return analyzer if analyzer
|
137
|
+
def get_analyzer_class(key)
|
138
|
+
raise ArgumentError, "#{key} is not supported" unless Mihari.analyzer_to_class.key?(key)
|
174
139
|
|
175
|
-
|
140
|
+
Mihari.analyzer_to_class[key]
|
176
141
|
end
|
177
142
|
|
178
143
|
#
|
@@ -189,15 +154,14 @@ module Mihari
|
|
189
154
|
#
|
190
155
|
# Get emitter class
|
191
156
|
#
|
192
|
-
# @param [String]
|
157
|
+
# @param [String] key
|
193
158
|
#
|
194
159
|
# @return [Class<Mihari::Emitters::Base>] emitter class
|
195
160
|
#
|
196
|
-
def get_emitter_class(
|
197
|
-
|
198
|
-
return emitter if emitter
|
161
|
+
def get_emitter_class(key)
|
162
|
+
raise ArgumentError, "#{key} is not supported" unless Mihari.emitter_to_class.key?(key)
|
199
163
|
|
200
|
-
|
164
|
+
Mihari.emitter_to_class[key]
|
201
165
|
end
|
202
166
|
|
203
167
|
#
|
@@ -219,21 +183,20 @@ module Mihari
|
|
219
183
|
# @return [Array<Mihari::Emitters::Base>]
|
220
184
|
#
|
221
185
|
def valid_emitters
|
222
|
-
emitters.select(&:valid?)
|
186
|
+
@valid_emitters ||= emitters.select(&:valid?)
|
223
187
|
end
|
224
188
|
|
225
189
|
#
|
226
190
|
# Get enricher class
|
227
191
|
#
|
228
|
-
# @param [String]
|
192
|
+
# @param [String] key
|
229
193
|
#
|
230
194
|
# @return [Class<Mihari::Enrichers::Base>] enricher class
|
231
195
|
#
|
232
|
-
def get_enricher_class(
|
233
|
-
|
234
|
-
return enricher if enricher
|
196
|
+
def get_enricher_class(key)
|
197
|
+
raise ArgumentError, "#{key} is not supported" unless Mihari.enricher_to_class.key?(key)
|
235
198
|
|
236
|
-
|
199
|
+
Mihari.enricher_to_class[key]
|
237
200
|
end
|
238
201
|
|
239
202
|
#
|
@@ -258,7 +221,9 @@ module Mihari
|
|
258
221
|
analyzers.map do |analyzer|
|
259
222
|
next if analyzer.configured?
|
260
223
|
|
261
|
-
|
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."
|
262
227
|
raise ConfigurationError, message
|
263
228
|
end
|
264
229
|
end
|
@@ -39,6 +39,15 @@ module Mihari
|
|
39
39
|
%w[virustotal_api_key]
|
40
40
|
end
|
41
41
|
|
42
|
+
class << self
|
43
|
+
#
|
44
|
+
# @return [Array<String>, nil]
|
45
|
+
#
|
46
|
+
def key_aliases
|
47
|
+
["vt"]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
42
51
|
private
|
43
52
|
|
44
53
|
def client
|
@@ -65,7 +74,7 @@ module Mihari
|
|
65
74
|
data = res["data"] || []
|
66
75
|
data.filter_map do |item|
|
67
76
|
data = item.dig("attributes", "ip_address")
|
68
|
-
data.nil? ? nil : Artifact.new(data: data,
|
77
|
+
data.nil? ? nil : Artifact.new(data: data, metadata: item)
|
69
78
|
end
|
70
79
|
end
|
71
80
|
|
@@ -80,7 +89,7 @@ module Mihari
|
|
80
89
|
data = res["data"] || []
|
81
90
|
data.filter_map do |item|
|
82
91
|
data = item.dig("attributes", "host_name")
|
83
|
-
Artifact.new(data: data,
|
92
|
+
Artifact.new(data: data, metadata: item)
|
84
93
|
end.uniq
|
85
94
|
end
|
86
95
|
end
|
@@ -25,6 +25,22 @@ module Mihari
|
|
25
25
|
%w[virustotal_api_key]
|
26
26
|
end
|
27
27
|
|
28
|
+
class << self
|
29
|
+
#
|
30
|
+
# @return [String]
|
31
|
+
#
|
32
|
+
def class_key
|
33
|
+
"virustotal_intelligence"
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# @return [Array<String>, nil]
|
38
|
+
#
|
39
|
+
def class_key_aliases
|
40
|
+
["vt_intel"]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
28
44
|
private
|
29
45
|
|
30
46
|
#
|
@@ -73,9 +73,9 @@ module Mihari
|
|
73
73
|
data = match["ip"]
|
74
74
|
|
75
75
|
if data.is_a?(Array)
|
76
|
-
data.map { |d| Artifact.new(data: d,
|
76
|
+
data.map { |d| Artifact.new(data: d, metadata: match) }
|
77
77
|
else
|
78
|
-
Artifact.new(data: data,
|
78
|
+
Artifact.new(data: data, metadata: match)
|
79
79
|
end
|
80
80
|
end.flatten
|
81
81
|
end
|
data/lib/mihari/base.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mihari
|
4
|
+
#
|
5
|
+
# Base class for Analyzer, Emitter and Enricher
|
6
|
+
#
|
7
|
+
class Base
|
8
|
+
# @return [Hash]
|
9
|
+
attr_reader :options
|
10
|
+
|
11
|
+
#
|
12
|
+
# @param [Hash, nil] options
|
13
|
+
#
|
14
|
+
def initialize(*_args, options: nil, **_kwargs)
|
15
|
+
@options = options || {}
|
16
|
+
end
|
17
|
+
|
18
|
+
#
|
19
|
+
# @return [Integer]
|
20
|
+
#
|
21
|
+
def retry_interval
|
22
|
+
options[:retry_interval] || Mihari.config.retry_interval
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# @return [Boolean]
|
27
|
+
#
|
28
|
+
def retry_exponential_backoff
|
29
|
+
options[:retry_exponential_backoff] || Mihari.config.retry_exponential_backoff
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# @return [Integer]
|
34
|
+
#
|
35
|
+
def retry_times
|
36
|
+
options[:retry_times] || Mihari.config.retry_times
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
# @return [Integer, nil]
|
41
|
+
#
|
42
|
+
def timeout
|
43
|
+
options[:timeout]
|
44
|
+
end
|
45
|
+
|
46
|
+
class << self
|
47
|
+
#
|
48
|
+
# @return [String]
|
49
|
+
#
|
50
|
+
def class_key
|
51
|
+
to_s.split("::").last
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
# @return [Array<String>, nil]
|
56
|
+
#
|
57
|
+
def class_key_aliases
|
58
|
+
nil
|
59
|
+
end
|
60
|
+
|
61
|
+
#
|
62
|
+
# @return [Array<String>]
|
63
|
+
#
|
64
|
+
def class_keys
|
65
|
+
([class_key] + [class_key_aliases]).flatten.compact
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/mihari/cli/main.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "thor"
|
4
|
+
require "thor/hollaback"
|
4
5
|
|
5
6
|
# Commands
|
6
7
|
require "mihari/commands/alert"
|
@@ -19,10 +20,45 @@ require "mihari/cli/rule"
|
|
19
20
|
module Mihari
|
20
21
|
module CLI
|
21
22
|
class Main < Base
|
23
|
+
class_option :debug, desc: "Sets up debug mode", aliases: ["-d"], type: :boolean
|
24
|
+
class_around :safe_execute
|
25
|
+
|
22
26
|
include Mihari::Commands::Search
|
23
27
|
include Mihari::Commands::Version
|
24
28
|
include Mihari::Commands::Web
|
25
29
|
|
30
|
+
no_commands do
|
31
|
+
def unwrap_error(err)
|
32
|
+
return err unless err.is_a?(Dry::Monads::UnwrapError)
|
33
|
+
|
34
|
+
# NOTE: UnwrapError's receiver can be either of:
|
35
|
+
# - Dry::Monads::Try::Error
|
36
|
+
# - Dry::Monads::Result::Failure
|
37
|
+
receiver = err.receiver
|
38
|
+
return receiver.exception if receiver.is_a?(Dry::Monads::Try::Error)
|
39
|
+
|
40
|
+
receiver.failure
|
41
|
+
end
|
42
|
+
|
43
|
+
def safe_execute
|
44
|
+
yield
|
45
|
+
rescue StandardError => e
|
46
|
+
err = unwrap_error(e)
|
47
|
+
|
48
|
+
raise err if options["debug"]
|
49
|
+
|
50
|
+
case err
|
51
|
+
when ValidationError
|
52
|
+
warn JSON.pretty_generate(err.errors.to_h)
|
53
|
+
when StandardError
|
54
|
+
Sentry.capture_exception(err) if Sentry.initialized?
|
55
|
+
warn err
|
56
|
+
end
|
57
|
+
|
58
|
+
exit 1
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
26
62
|
desc "db", "Sub commands for DB"
|
27
63
|
subcommand "db", Database
|
28
64
|
|
@@ -14,30 +14,14 @@ module Mihari
|
|
14
14
|
#
|
15
15
|
def add(path)
|
16
16
|
Mihari::Database.with_db_connection do
|
17
|
-
builder =
|
17
|
+
builder = Services::AlertBuilder.new(path)
|
18
18
|
|
19
19
|
run_proxy_l = ->(proxy) { run_proxy proxy }
|
20
|
-
|
20
|
+
result = builder.result.bind(run_proxy_l)
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
alert = result.value!
|
26
|
-
data = Mihari::Entities::Alert.represent(alert)
|
27
|
-
puts JSON.pretty_generate(data.as_json)
|
28
|
-
return
|
29
|
-
end
|
30
|
-
|
31
|
-
failure = result.failure
|
32
|
-
case failure
|
33
|
-
when ValidationError
|
34
|
-
Mihari.logger.error "Failed to parse the input as an alert:"
|
35
|
-
Mihari.logger.error JSON.pretty_generate(failure.errors.to_h)
|
36
|
-
when StandardError
|
37
|
-
raise failure
|
38
|
-
else
|
39
|
-
Mihari.logger.info failure
|
40
|
-
end
|
22
|
+
alert = result.value!
|
23
|
+
data = Entities::Alert.represent(alert)
|
24
|
+
puts JSON.pretty_generate(data.as_json)
|
41
25
|
end
|
42
26
|
end
|
43
27
|
|
@@ -47,21 +31,10 @@ module Mihari
|
|
47
31
|
#
|
48
32
|
def run_proxy(proxy)
|
49
33
|
Dry::Monads::Try[StandardError] do
|
50
|
-
runner =
|
34
|
+
runner = Services::AlertRunner.new(proxy)
|
51
35
|
runner.run
|
52
36
|
end.to_result
|
53
37
|
end
|
54
|
-
|
55
|
-
#
|
56
|
-
# @param [Mihari::Alert, nil] alert_or_nil
|
57
|
-
#
|
58
|
-
def check_nil(alert_or_nil)
|
59
|
-
if alert_or_nil.nil?
|
60
|
-
Failure "There is no new artifact found"
|
61
|
-
else
|
62
|
-
Success alert_or_nil
|
63
|
-
end
|
64
|
-
end
|
65
38
|
end
|
66
39
|
end
|
67
40
|
end
|
data/lib/mihari/commands/rule.rb
CHANGED
@@ -8,7 +8,7 @@ module Mihari
|
|
8
8
|
class << self
|
9
9
|
def included(thor)
|
10
10
|
thor.class_eval do
|
11
|
-
include Dry::Monads[:
|
11
|
+
include Dry::Monads[:try, :result]
|
12
12
|
|
13
13
|
desc "validate [PATH]", "Validate a rule file"
|
14
14
|
#
|
@@ -18,16 +18,11 @@ module Mihari
|
|
18
18
|
#
|
19
19
|
def validate(path)
|
20
20
|
res = Dry::Monads::Try[ValidationError] do
|
21
|
-
Services::RuleProxy.from_yaml
|
22
|
-
end.fmap do |rule|
|
23
|
-
Mihari.logger.info "Valid format. The input is parsed as the following:"
|
24
|
-
Mihari.logger.info rule.data.to_yaml
|
21
|
+
Services::RuleProxy.from_yaml File.read(path)
|
25
22
|
end
|
26
23
|
|
27
|
-
|
28
|
-
|
29
|
-
Mihari.logger.error "Failed to parse the input as a rule:"
|
30
|
-
Mihari.logger.error JSON.pretty_generate(res.exception.errors.to_h)
|
24
|
+
rule = res.value!
|
25
|
+
puts rule.data.to_yaml
|
31
26
|
end
|
32
27
|
|
33
28
|
desc "init [PATH]", "Initialize a new rule file"
|
@@ -43,14 +38,14 @@ module Mihari
|
|
43
38
|
|
44
39
|
initialize_rule path
|
45
40
|
|
46
|
-
|
41
|
+
puts "A new rule file has been initialized: #{path}."
|
47
42
|
end
|
48
43
|
|
49
44
|
no_commands do
|
50
45
|
#
|
51
46
|
# @return [Mihari::Services::Rule]
|
52
47
|
#
|
53
|
-
def
|
48
|
+
def rule
|
54
49
|
Services::RuleProxy.from_yaml File.read(File.expand_path("../templates/rule.yml.erb", __dir__))
|
55
50
|
end
|
56
51
|
|
@@ -63,7 +58,7 @@ module Mihari
|
|
63
58
|
# @return [nil]
|
64
59
|
#
|
65
60
|
def initialize_rule(path, files = Dry::Files.new)
|
66
|
-
files.write(path,
|
61
|
+
files.write(path, rule.yaml)
|
67
62
|
end
|
68
63
|
end
|
69
64
|
end
|
@@ -6,10 +6,10 @@ module Mihari
|
|
6
6
|
class << self
|
7
7
|
def included(thor)
|
8
8
|
thor.class_eval do
|
9
|
-
include Dry::Monads[:
|
9
|
+
include Dry::Monads[:try, :result]
|
10
10
|
|
11
|
-
desc "search [PATH_OR_ID]", "Search by a rule"
|
12
|
-
method_option :force_overwrite, type: :boolean, aliases: "-f", desc: "Force
|
11
|
+
desc "search [PATH_OR_ID]", "Search by a rule (Outputs null if there is no new finding)"
|
12
|
+
method_option :force_overwrite, type: :boolean, aliases: "-f", desc: "Force overwriting a rule"
|
13
13
|
#
|
14
14
|
# Search by a rule
|
15
15
|
#
|
@@ -21,27 +21,12 @@ module Mihari
|
|
21
21
|
|
22
22
|
check_diff_l = ->(rule) { check_diff rule }
|
23
23
|
update_and_run_l = ->(runner) { update_and_run runner }
|
24
|
-
check_nil_l = ->(alert_or_nil) { check_nil alert_or_nil }
|
25
24
|
|
26
|
-
result = builder.result.bind(check_diff_l).bind(update_and_run_l)
|
25
|
+
result = builder.result.bind(check_diff_l).bind(update_and_run_l)
|
27
26
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
puts JSON.pretty_generate(data.as_json)
|
32
|
-
return
|
33
|
-
end
|
34
|
-
|
35
|
-
failure = result.failure
|
36
|
-
case failure
|
37
|
-
when ValidationError
|
38
|
-
Mihari.logger.error "Failed to parse the input as a rule:"
|
39
|
-
Mihari.logger.error JSON.pretty_generate(failure.errors.to_h)
|
40
|
-
when StandardError
|
41
|
-
raise failure
|
42
|
-
else
|
43
|
-
Mihari.logger.info failure
|
44
|
-
end
|
27
|
+
alert = result.value!
|
28
|
+
data = Entities::Alert.represent(alert)
|
29
|
+
puts JSON.pretty_generate(data.as_json)
|
45
30
|
end
|
46
31
|
end
|
47
32
|
|
@@ -51,27 +36,14 @@ module Mihari
|
|
51
36
|
#
|
52
37
|
def check_diff(rule)
|
53
38
|
force_overwrite = options["force_overwrite"] || false
|
54
|
-
|
55
|
-
|
39
|
+
message = "There is a diff in the rule. Are you sure you want to overwrite the rule? (y/n)"
|
40
|
+
runner = Services::RuleRunner.new(rule)
|
56
41
|
|
57
|
-
if runner.diff? && !force_overwrite && !yes?(message)
|
58
|
-
return Failure("Stop overwriting the rule (#{rule.id})")
|
59
|
-
end
|
42
|
+
exit 0 if runner.diff? && !force_overwrite && !yes?(message)
|
60
43
|
|
61
44
|
Success runner
|
62
45
|
end
|
63
46
|
|
64
|
-
#
|
65
|
-
# @param [Mihari::Alert, nil] alert_or_nil
|
66
|
-
#
|
67
|
-
def check_nil(alert_or_nil)
|
68
|
-
if alert_or_nil.nil?
|
69
|
-
Failure "There is no new artifact found"
|
70
|
-
else
|
71
|
-
Success alert_or_nil
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
47
|
#
|
76
48
|
# @param [Mihari::Services::RuleRunner] runner
|
77
49
|
#
|
data/lib/mihari/constants.rb
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
module Mihari
|
4
4
|
# @return [Array<String>]
|
5
|
-
DEFAULT_DATA_TYPES =
|
5
|
+
DEFAULT_DATA_TYPES = Types::DataTypes.values.freeze
|
6
6
|
|
7
7
|
# @return [Array<Hash>]
|
8
|
-
DEFAULT_EMITTERS = %w[database
|
8
|
+
DEFAULT_EMITTERS = %w[database].map { |name| { emitter: name } }.freeze
|
9
9
|
|
10
10
|
# @return [Array<Hash>]
|
11
|
-
DEFAULT_ENRICHERS =
|
11
|
+
DEFAULT_ENRICHERS = Mihari.enricher_to_class.keys.map { |name| { enricher: name.downcase } }.freeze
|
12
12
|
end
|