mihari 5.7.2 → 6.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -0
- data/config.ru +2 -0
- data/lib/mihari/actor.rb +1 -1
- data/lib/mihari/analyzers/base.rb +3 -0
- data/lib/mihari/analyzers/dnstwister.rb +2 -4
- data/lib/mihari/analyzers/hunterhow.rb +1 -1
- data/lib/mihari/analyzers/urlscan.rb +1 -4
- data/lib/mihari/cli/main.rb +2 -12
- data/lib/mihari/commands/database.rb +0 -1
- data/lib/mihari/config.rb +5 -1
- data/lib/mihari/database.rb +9 -5
- data/lib/mihari/emitters/misp.rb +2 -2
- data/lib/mihari/emitters/slack.rb +8 -11
- data/lib/mihari/emitters/the_hive.rb +5 -9
- data/lib/mihari/enrichers/base.rb +2 -0
- data/lib/mihari/enrichers/google_public_dns.rb +2 -7
- data/lib/mihari/enrichers/ipinfo.rb +2 -3
- data/lib/mihari/enrichers/shodan.rb +2 -3
- data/lib/mihari/enrichers/whois.rb +11 -20
- data/lib/mihari/entities/artifact.rb +1 -0
- data/lib/mihari/mixins/falsepositive.rb +2 -2
- data/lib/mihari/mixins/refang.rb +1 -4
- data/lib/mihari/mixins/unwrap_error.rb +27 -0
- data/lib/mihari/models/alert.rb +1 -3
- data/lib/mihari/models/artifact.rb +18 -12
- data/lib/mihari/models/rule.rb +1 -2
- data/lib/mihari/rule.rb +14 -10
- data/lib/mihari/service.rb +2 -0
- data/lib/mihari/services/rule_builder.rb +2 -4
- data/lib/mihari/structs/fofa.rb +2 -0
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/app.rb +5 -3
- data/lib/mihari/web/endpoints/alerts.rb +14 -18
- data/lib/mihari/web/endpoints/artifacts.rb +17 -22
- data/lib/mihari/web/endpoints/configs.rb +0 -1
- data/lib/mihari/web/endpoints/ip_addresses.rb +1 -1
- data/lib/mihari/web/endpoints/rules.rb +27 -32
- data/lib/mihari/web/endpoints/tags.rb +7 -9
- data/lib/mihari/web/middleware/connection_adapter.rb +3 -5
- data/lib/mihari/web/middleware/error_notification_adapter.rb +10 -6
- data/lib/mihari/web/public/assets/{index-ec641cb0.js → index-216d49d1.js} +42 -42
- data/lib/mihari/web/public/assets/{index-56fc2187.css → index-4c8509ee.css} +1 -1
- data/lib/mihari/web/public/index.html +2 -2
- data/lib/mihari/web/public/redoc-static.html +29 -49
- data/lib/mihari.rb +9 -10
- data/mihari.gemspec +11 -13
- data/mkdocs.yml +1 -0
- data/requirements.txt +1 -1
- metadata +76 -34
- data/lib/mihari/services/rule_runner.rb +0 -19
data/lib/mihari/service.rb
CHANGED
@@ -20,10 +20,8 @@ module Mihari
|
|
20
20
|
# @return [Hash]
|
21
21
|
#
|
22
22
|
def data
|
23
|
-
|
24
|
-
|
25
|
-
return rule.data
|
26
|
-
end
|
23
|
+
result = Try { Mihari::Models::Rule.find path_or_id }.to_result
|
24
|
+
return result.value! if result.success?
|
27
25
|
|
28
26
|
raise ArgumentError, "#{path_or_id} does not exist" unless Pathname(path_or_id).exist?
|
29
27
|
|
data/lib/mihari/structs/fofa.rb
CHANGED
data/lib/mihari/version.rb
CHANGED
data/lib/mihari/web/app.rb
CHANGED
@@ -39,7 +39,7 @@ module Mihari
|
|
39
39
|
|
40
40
|
def call(env)
|
41
41
|
status, headers, body = API.call(env)
|
42
|
-
return [status, headers, body] unless headers["
|
42
|
+
return [status, headers, body] unless headers["x-cascade"] == "pass"
|
43
43
|
|
44
44
|
# Check if the App wants us to pass the response along to others
|
45
45
|
request_path = env["PATH_INFO"]
|
@@ -53,17 +53,19 @@ module Mihari
|
|
53
53
|
|
54
54
|
class << self
|
55
55
|
def instance
|
56
|
-
|
56
|
+
Rack::Builder.new do
|
57
57
|
use Rack::Cors do
|
58
58
|
allow do
|
59
59
|
origins "*"
|
60
60
|
resource "*", headers: :any, methods: %i[get post put delete options]
|
61
61
|
end
|
62
62
|
end
|
63
|
-
|
64
63
|
use Middleware::ConnectionAdapter
|
65
64
|
use Middleware::ErrorNotificationAdapter
|
66
65
|
|
66
|
+
use Sentry::Rack::CaptureExceptions if Sentry.initialized?
|
67
|
+
use BetterErrors::Middleware if ENV["RACK_ENV"] == "development" && defined?(BetterErrors::Middleware)
|
68
|
+
|
67
69
|
run App.new
|
68
70
|
end.to_app
|
69
71
|
end
|
@@ -77,7 +77,6 @@ module Mihari
|
|
77
77
|
desc "Search alerts", {
|
78
78
|
is_array: true,
|
79
79
|
success: Entities::AlertsWithPagination,
|
80
|
-
failure: [{ code: 404, message: "Not found", model: Entities::Message }],
|
81
80
|
summary: "Search alerts"
|
82
81
|
}
|
83
82
|
params do
|
@@ -103,31 +102,30 @@ module Mihari
|
|
103
102
|
end
|
104
103
|
|
105
104
|
desc "Delete an alert", {
|
106
|
-
success: Entities::Message,
|
107
|
-
failure: [{ code: 404,
|
105
|
+
success: { code: 204, model: Entities::Message },
|
106
|
+
failure: [{ code: 404, model: Entities::Message }],
|
108
107
|
summary: "Delete an alert"
|
109
108
|
}
|
110
109
|
params do
|
111
110
|
requires :id, type: Integer
|
112
111
|
end
|
113
112
|
delete "/:id" do
|
113
|
+
status 204
|
114
|
+
|
114
115
|
id = params["id"].to_i
|
115
116
|
result = AlertDestroyer.result(id)
|
116
|
-
if result.success?
|
117
|
-
status 204
|
118
|
-
return present({ message: "" }, with: Entities::Message)
|
119
|
-
end
|
117
|
+
return present({ message: "" }, with: Entities::Message) if result.success?
|
120
118
|
|
121
|
-
|
122
|
-
case failure
|
119
|
+
case result.failure
|
123
120
|
when ActiveRecord::RecordNotFound
|
124
121
|
error!({ message: "ID:#{id} is not found" }, 404)
|
125
122
|
end
|
126
|
-
raise failure
|
123
|
+
raise result.failure
|
127
124
|
end
|
128
125
|
|
129
126
|
desc "Create an alert", {
|
130
|
-
success: Entities::Alert,
|
127
|
+
success: { code: 201, model: Entities::Alert },
|
128
|
+
failure: [{ code: 404, model: Entities::Message }],
|
131
129
|
summary: "Create an alert"
|
132
130
|
}
|
133
131
|
params do
|
@@ -135,18 +133,16 @@ module Mihari
|
|
135
133
|
requires :artifacts, type: Array, documentation: { type: String, is_array: true, param_type: "body" }
|
136
134
|
end
|
137
135
|
post "/" do
|
136
|
+
status 201
|
137
|
+
|
138
138
|
result = AlertCreator.result(params)
|
139
|
-
if result.success?
|
140
|
-
status 201
|
141
|
-
return present(result.value!, with: Entities::Alert)
|
142
|
-
end
|
139
|
+
return present(result.value!, with: Entities::Alert) if result.success?
|
143
140
|
|
144
|
-
|
145
|
-
case failure
|
141
|
+
case result.failure
|
146
142
|
when ActiveRecord::RecordNotFound
|
147
143
|
error!({ message: "Rule:#{params["ruleId"]} is not found" }, 404)
|
148
144
|
end
|
149
|
-
raise failure
|
145
|
+
raise result.failure
|
150
146
|
end
|
151
147
|
end
|
152
148
|
end
|
@@ -64,7 +64,7 @@ module Mihari
|
|
64
64
|
namespace :artifacts do
|
65
65
|
desc "Get an artifact", {
|
66
66
|
success: Entities::Artifact,
|
67
|
-
failure: [{ code: 404,
|
67
|
+
failure: [{ code: 404, model: Entities::Message }],
|
68
68
|
summary: "Get an artifact"
|
69
69
|
}
|
70
70
|
params do
|
@@ -75,60 +75,55 @@ module Mihari
|
|
75
75
|
result = ArtifactGetter.result(id)
|
76
76
|
return present(result.value!, with: Entities::Artifact) if result.success?
|
77
77
|
|
78
|
-
|
79
|
-
case failure
|
78
|
+
case result.failure
|
80
79
|
when ActiveRecord::RecordNotFound
|
81
80
|
error!({ message: "ID:#{id} is not found" }, 404)
|
82
81
|
end
|
83
|
-
raise failure
|
82
|
+
raise result.failure
|
84
83
|
end
|
85
84
|
|
86
85
|
desc "Enrich an artifact", {
|
87
|
-
success: Entities::Message,
|
88
|
-
failure: [{ code: 404,
|
86
|
+
success: { code: 201, model: Entities::Message },
|
87
|
+
failure: [{ code: 404, model: Entities::Message }],
|
89
88
|
summary: "Enrich an artifact"
|
90
89
|
}
|
91
90
|
params do
|
92
91
|
requires :id, type: Integer
|
93
92
|
end
|
94
93
|
get "/:id/enrich" do
|
94
|
+
status 201
|
95
|
+
|
95
96
|
id = params["id"].to_i
|
96
97
|
result = ArtifactEnricher.result(id)
|
97
|
-
if result.success?
|
98
|
-
status 201
|
99
|
-
return present({ message: "" }, with: Entities::Message)
|
100
|
-
end
|
98
|
+
return present({ message: "#{id} has been enriched" }, with: Entities::Message) if result.success?
|
101
99
|
|
102
|
-
|
103
|
-
case failure
|
100
|
+
case result.failure
|
104
101
|
when ActiveRecord::RecordNotFound
|
105
102
|
error!({ message: "ID:#{id} is not found" }, 404)
|
106
103
|
end
|
107
|
-
raise failure
|
104
|
+
raise result.failure
|
108
105
|
end
|
109
106
|
|
110
107
|
desc "Delete an artifact", {
|
111
|
-
success: Entities::Message,
|
112
|
-
failure: [{ code: 404,
|
108
|
+
success: { code: 204, model: Entities::Message },
|
109
|
+
failure: [{ code: 404, model: Entities::Message }],
|
113
110
|
summary: "Delete an artifact"
|
114
111
|
}
|
115
112
|
params do
|
116
113
|
requires :id, type: Integer
|
117
114
|
end
|
118
115
|
delete "/:id" do
|
116
|
+
status 204
|
117
|
+
|
119
118
|
id = params["id"].to_i
|
120
119
|
result = ArtifactDestroyer.result(id)
|
121
|
-
if result.success?
|
122
|
-
status 204
|
123
|
-
return present({ message: "" }, with: Entities::Message)
|
124
|
-
end
|
120
|
+
return present({ message: "" }, with: Entities::Message) if result.success?
|
125
121
|
|
126
|
-
|
127
|
-
case failure
|
122
|
+
case result.failure
|
128
123
|
when ActiveRecord::RecordNotFound
|
129
124
|
error!({ message: "ID:#{id} is not found" }, 404)
|
130
125
|
end
|
131
|
-
raise failure
|
126
|
+
raise result.failure
|
132
127
|
end
|
133
128
|
end
|
134
129
|
end
|
@@ -21,7 +21,7 @@ module Mihari
|
|
21
21
|
namespace :ip_addresses do
|
22
22
|
desc "Get an IP address", {
|
23
23
|
success: Entities::IPAddress,
|
24
|
-
failure: [{ code: 404,
|
24
|
+
failure: [{ code: 404, model: Entities::Message }],
|
25
25
|
summary: "Get an IP address"
|
26
26
|
}
|
27
27
|
params do
|
@@ -128,7 +128,6 @@ module Mihari
|
|
128
128
|
desc "Search rules", {
|
129
129
|
is_array: true,
|
130
130
|
success: Entities::RulesWithPagination,
|
131
|
-
failure: [{ code: 404, message: "Not found", model: Entities::Message }],
|
132
131
|
summary: "Search rules"
|
133
132
|
}
|
134
133
|
params do
|
@@ -153,7 +152,7 @@ module Mihari
|
|
153
152
|
|
154
153
|
desc "Get a rule", {
|
155
154
|
success: Entities::Rule,
|
156
|
-
failure: [{ code: 404,
|
155
|
+
failure: [{ code: 404, model: Entities::Message }],
|
157
156
|
summary: "Get a rule"
|
158
157
|
}
|
159
158
|
params do
|
@@ -164,50 +163,48 @@ module Mihari
|
|
164
163
|
result = RuleGetter.result(params[:id].to_s)
|
165
164
|
return present(result.value!, with: Entities::Rule) if result.success?
|
166
165
|
|
167
|
-
|
168
|
-
case failure
|
166
|
+
case result.failure
|
169
167
|
when ActiveRecord::RecordNotFound
|
170
168
|
error!({ message: "ID:#{id} is not found" }, 404)
|
171
169
|
end
|
172
|
-
raise failure
|
170
|
+
raise result.failure
|
173
171
|
end
|
174
172
|
|
175
173
|
desc "Run a rule", {
|
176
|
-
success: Entities::Message,
|
174
|
+
success: { code: 201, model: Entities::Message },
|
175
|
+
failure: [{ code: 404, model: Entities::Message }],
|
177
176
|
summary: "Run a rule"
|
178
177
|
}
|
179
178
|
params do
|
180
179
|
requires :id, type: String
|
181
180
|
end
|
182
181
|
get "/:id/run" do
|
182
|
+
status 201
|
183
|
+
|
183
184
|
id = params[:id].to_s
|
184
185
|
result = RuleRunner.result(id)
|
185
|
-
if result.success?
|
186
|
-
status 201
|
187
|
-
return present({ message: "ID:#{id}} ran successfully" }, with: Entities::Message)
|
188
|
-
end
|
186
|
+
return present({ message: "ID:#{id}} has been ran" }, with: Entities::Message) if result.success?
|
189
187
|
|
190
|
-
|
191
|
-
case failure
|
188
|
+
case result.failure
|
192
189
|
when ActiveRecord::RecordNotFound
|
193
190
|
error!({ message: "ID:#{id} is not found" }, 404)
|
194
191
|
end
|
195
|
-
raise failure
|
192
|
+
raise result.failure
|
196
193
|
end
|
197
194
|
|
198
195
|
desc "Create a rule", {
|
199
|
-
success: Entities::Rule,
|
196
|
+
success: { code: 201, model: Entities::Rule },
|
197
|
+
failure: [{ code: 404, model: Entities::Message }],
|
200
198
|
summary: "Create a rule"
|
201
199
|
}
|
202
200
|
params do
|
203
201
|
requires :yaml, type: String, documentation: { param_type: "body" }
|
204
202
|
end
|
205
203
|
post "/" do
|
204
|
+
status 201
|
205
|
+
|
206
206
|
result = RuleCreator.result(params[:yaml])
|
207
|
-
if result.success?
|
208
|
-
status 201
|
209
|
-
return present(result.value!.model, with: Entities::Rule)
|
210
|
-
end
|
207
|
+
return present(result.value!.model, with: Entities::Rule) if result.success?
|
211
208
|
|
212
209
|
failure = result.failure
|
213
210
|
case failure
|
@@ -220,7 +217,8 @@ module Mihari
|
|
220
217
|
end
|
221
218
|
|
222
219
|
desc "Update a rule", {
|
223
|
-
success: Entities::Rule,
|
220
|
+
success: { code: 201, model: Entities::Rule },
|
221
|
+
failure: [{ code: 404, model: Entities::Message }],
|
224
222
|
summary: "Update a rule"
|
225
223
|
}
|
226
224
|
params do
|
@@ -228,12 +226,11 @@ module Mihari
|
|
228
226
|
requires :yaml, type: String, documentation: { param_type: "body" }
|
229
227
|
end
|
230
228
|
put "/" do
|
229
|
+
status 201
|
230
|
+
|
231
231
|
id = params[:id].to_s
|
232
232
|
result = RuleUpdater.result(id: id, yaml: params[:yaml].to_s)
|
233
|
-
if result.success?
|
234
|
-
status 201
|
235
|
-
return present(result.value!.model, with: Entities::Rule)
|
236
|
-
end
|
233
|
+
return present(result.value!.model, with: Entities::Rule) if result.success?
|
237
234
|
|
238
235
|
failure = result.failure
|
239
236
|
case failure
|
@@ -248,27 +245,25 @@ module Mihari
|
|
248
245
|
end
|
249
246
|
|
250
247
|
desc "Delete a rule", {
|
251
|
-
success: Entities::Message,
|
252
|
-
failure: [{ code: 404,
|
248
|
+
success: { code: 204, model: Entities::Message },
|
249
|
+
failure: [{ code: 404, model: Entities::Message }],
|
253
250
|
summary: "Delete a rule"
|
254
251
|
}
|
255
252
|
params do
|
256
253
|
requires :id, type: String
|
257
254
|
end
|
258
255
|
delete "/:id" do
|
256
|
+
status 204
|
257
|
+
|
259
258
|
id = params[:id].to_s
|
260
259
|
result = RuleDestroyer.result(id)
|
261
|
-
if result.success?
|
262
|
-
status 204
|
263
|
-
return present({ message: "ID:#{id} is deleted" }, with: Entities::Message)
|
264
|
-
end
|
260
|
+
return present({ message: "ID:#{id} is deleted" }, with: Entities::Message) if result.success?
|
265
261
|
|
266
|
-
|
267
|
-
case failure
|
262
|
+
case result.failure
|
268
263
|
when ActiveRecord::RecordNotFound
|
269
264
|
error!({ message: "ID:#{id} is not found" }, 404)
|
270
265
|
end
|
271
|
-
raise failure
|
266
|
+
raise result.failure
|
272
267
|
end
|
273
268
|
end
|
274
269
|
end
|
@@ -28,27 +28,25 @@ module Mihari
|
|
28
28
|
end
|
29
29
|
|
30
30
|
desc "Delete a tag", {
|
31
|
-
success: Entities::Message,
|
32
|
-
failure: [{ code: 404,
|
31
|
+
success: { code: 204, model: Entities::Message },
|
32
|
+
failure: [{ code: 404, model: Entities::Message }],
|
33
33
|
summary: "Delete a tag"
|
34
34
|
}
|
35
35
|
params do
|
36
36
|
requires :id, type: Integer
|
37
37
|
end
|
38
38
|
delete "/:id" do
|
39
|
+
status 204
|
40
|
+
|
39
41
|
id = params[:id].to_i
|
40
42
|
result = TagDestroyer.result(id)
|
41
|
-
if result.success?
|
42
|
-
status 204
|
43
|
-
return present({ message: "" }, with: Entities::Message)
|
44
|
-
end
|
43
|
+
return present({ message: "" }, with: Entities::Message) if result.success?
|
45
44
|
|
46
|
-
|
47
|
-
case failure
|
45
|
+
case result.failure
|
48
46
|
when ActiveRecord::RecordNotFound
|
49
47
|
error!({ message: "ID:#{id} is not found" }, 404)
|
50
48
|
end
|
51
|
-
raise failure
|
49
|
+
raise result.failure
|
52
50
|
end
|
53
51
|
end
|
54
52
|
end
|
@@ -7,16 +7,14 @@ module Mihari
|
|
7
7
|
# DB connection adapter for Rack app
|
8
8
|
#
|
9
9
|
class ConnectionAdapter
|
10
|
+
attr_reader :app
|
11
|
+
|
10
12
|
def initialize(app)
|
11
13
|
@app = app
|
12
14
|
end
|
13
15
|
|
14
16
|
def call(env)
|
15
|
-
Mihari::Database.with_db_connection
|
16
|
-
status, headers, body = @app.call(env)
|
17
|
-
|
18
|
-
[status, headers, body]
|
19
|
-
end
|
17
|
+
Mihari::Database.with_db_connection { app.call env }
|
20
18
|
end
|
21
19
|
end
|
22
20
|
end
|
@@ -7,6 +7,10 @@ module Mihari
|
|
7
7
|
# Error notification adapter for Rack app
|
8
8
|
#
|
9
9
|
class ErrorNotificationAdapter
|
10
|
+
include Mihari::Mixins::UnwrapError
|
11
|
+
|
12
|
+
attr_reader :app
|
13
|
+
|
10
14
|
def initialize(app)
|
11
15
|
@app = app
|
12
16
|
end
|
@@ -14,16 +18,16 @@ module Mihari
|
|
14
18
|
def with_error_notification
|
15
19
|
yield
|
16
20
|
rescue StandardError => e
|
17
|
-
|
21
|
+
unwrapped = unwrap_error(e)
|
22
|
+
|
23
|
+
Mihari.logger.error unwrapped
|
24
|
+
Sentry.capture_exception(unwrapped) if Sentry.initialized?
|
18
25
|
|
19
|
-
|
26
|
+
raise unwrapped
|
20
27
|
end
|
21
28
|
|
22
29
|
def call(env)
|
23
|
-
with_error_notification
|
24
|
-
status, headers, body = @app.call(env)
|
25
|
-
[status, headers, body]
|
26
|
-
end
|
30
|
+
with_error_notification { app.call(env) }
|
27
31
|
end
|
28
32
|
end
|
29
33
|
end
|