mihari 7.0.5 → 7.1.1
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/.rubocop.yml +2 -0
- data/README.md +2 -1
- data/Rakefile +39 -1
- data/lib/mihari/analyzers/base.rb +8 -10
- data/lib/mihari/cli/application.rb +19 -4
- data/lib/mihari/commands/alert.rb +41 -6
- data/lib/mihari/commands/artifact.rb +41 -6
- data/lib/mihari/commands/config.rb +1 -1
- data/lib/mihari/commands/rule.rb +42 -7
- data/lib/mihari/commands/search.rb +1 -1
- data/lib/mihari/commands/tag.rb +39 -4
- data/lib/mihari/concerns/retriable.rb +2 -2
- data/lib/mihari/data_type.rb +3 -3
- data/lib/mihari/database.rb +1 -1
- data/lib/mihari/emitters/base.rb +5 -1
- data/lib/mihari/emitters/webhook.rb +15 -53
- data/lib/mihari/enrichers/base.rb +7 -1
- data/lib/mihari/errors.rb +2 -2
- data/lib/mihari/http.rb +1 -1
- data/lib/mihari/rule.rb +2 -14
- data/lib/mihari/services/renderer.rb +31 -0
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/endpoints/ip_addresses.rb +1 -1
- data/lib/mihari/web/public/assets/{index-geliIfjB.js → index-U5u7qHZZ.js} +18 -18
- data/lib/mihari/web/public/index.html +1 -1
- data/lib/mihari/web/public/redoc-static.html +28 -28
- data/lib/mihari.rb +12 -0
- data/mihari.gemspec +5 -2
- data/mkdocs.yml +1 -1
- data/test.json.jbuilder +7 -0
- metadata +51 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8ea0adf73bba53f264c22f8885d44e24de5657a2a77c2b7ea3533bb5acf6e78b
|
4
|
+
data.tar.gz: 77ab077d9322a22b0e399c81c057485aec1c4cdb1a14f15cbd81f1a3650f37a6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3b68702c146819189140c0c5626c27f7b53f94021dd21138f0ac2758366486cebb7c450e0168f76cfc3d9940ee86eb2b279eb1725a3922614f51930dcb0e6b71
|
7
|
+
data.tar.gz: e30c01360a6d382e73a8e88a40a5cd3f1dba6bac9ebbbe9b87b9b51cff4a6dc59676d19eec4fe757a3a40d78a560886e434802b74fdf125e663b73ae4c7abd8a
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
# mihari
|
2
2
|
|
3
3
|
[](https://badge.fury.io/rb/mihari)
|
4
|
-
[](https://github.com/ninoseki/mihari/actions/workflows/ruby.yml)
|
5
|
+
[](https://github.com/ninoseki/mihari/actions/workflows/node.yml)
|
5
6
|
[](https://coveralls.io/github/ninoseki/mihari?branch=master)
|
6
7
|
[](https://www.codefactor.io/repository/github/ninoseki/mihari)
|
7
8
|
|
data/Rakefile
CHANGED
@@ -7,11 +7,49 @@ RSpec::Core::RakeTask.new(:spec)
|
|
7
7
|
|
8
8
|
task default: :spec
|
9
9
|
|
10
|
-
desc "
|
10
|
+
desc "Run rackup (with rerun)"
|
11
11
|
task :rackup do
|
12
12
|
sh "rerun --pattern '{Gemfile.lock,lib/**/*.rb,lib/*.rb}' -- rackup config.ru"
|
13
13
|
end
|
14
14
|
|
15
|
+
def recursive_delete(hash, to_remove)
|
16
|
+
hash.delete(to_remove)
|
17
|
+
hash.each_value do |value|
|
18
|
+
recursive_delete(value, to_remove) if value.is_a? Hash
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def build_swagger_doc(path)
|
23
|
+
require_relative "lib/mihari"
|
24
|
+
require_relative "lib/mihari/web/application"
|
25
|
+
|
26
|
+
require "rack/test"
|
27
|
+
|
28
|
+
app = Mihari::Web::App.new
|
29
|
+
session = Rack::Test::Session.new(app)
|
30
|
+
|
31
|
+
res = session.request("/api/swagger_doc")
|
32
|
+
|
33
|
+
json = JSON.parse(res.body.to_s)
|
34
|
+
# remove host because it can be varied
|
35
|
+
keys_to_remove = %w[host]
|
36
|
+
keys_to_remove.each do |key|
|
37
|
+
recursive_delete json, key
|
38
|
+
end
|
39
|
+
|
40
|
+
f = File.open(path, "w")
|
41
|
+
f.write json.to_yaml
|
42
|
+
f.close
|
43
|
+
end
|
44
|
+
|
45
|
+
namespace :build do
|
46
|
+
desc "Build Swagger doc"
|
47
|
+
task :swagger, [:path] do |_t, args|
|
48
|
+
args.with_defaults(path: "./frontend/swagger.yaml")
|
49
|
+
build_swagger_doc args.path
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
15
53
|
def ci?
|
16
54
|
ENV.fetch("CI", false)
|
17
55
|
end
|
@@ -80,7 +80,7 @@ module Mihari
|
|
80
80
|
end
|
81
81
|
|
82
82
|
def result(...)
|
83
|
-
|
83
|
+
result = Try[StandardError] do
|
84
84
|
retry_on_error(
|
85
85
|
times: retry_times,
|
86
86
|
interval: retry_interval,
|
@@ -88,19 +88,17 @@ module Mihari
|
|
88
88
|
) do
|
89
89
|
call(...)
|
90
90
|
end
|
91
|
-
end
|
92
|
-
|
93
|
-
return res.recover { [] } if ignore_error?
|
91
|
+
end.to_result
|
94
92
|
|
95
|
-
result = res.to_result
|
96
93
|
return result if result.success?
|
97
94
|
|
98
95
|
# Wrap failure with AnalyzerError to explicitly name a failed analyzer
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
)
|
96
|
+
error = AnalyzerError.new(result.failure.message, self.class.class_key, cause: result.failure)
|
97
|
+
return Failure(error) unless ignore_error?
|
98
|
+
|
99
|
+
# Return Success if ignore_error? is true with logging
|
100
|
+
Mihari.logger.warn("Analyzer:#{self.class.class_key} failed - #{result.failure}")
|
101
|
+
Success([])
|
104
102
|
end
|
105
103
|
|
106
104
|
class << self
|
@@ -38,17 +38,32 @@ module Mihari
|
|
38
38
|
include Concerns::ErrorUnwrappable
|
39
39
|
|
40
40
|
no_commands do
|
41
|
+
#
|
42
|
+
# @param [StandardError] error
|
43
|
+
#
|
44
|
+
# @return [String, Hash, nil]
|
45
|
+
#
|
46
|
+
def error_to_detail(error)
|
47
|
+
# Dirty hack to show the DB error message
|
48
|
+
# (NOTE: #safe_execute block suppress #with_db_connection's error message)
|
49
|
+
if error.is_a?(ActiveRecord::StatementInvalid)
|
50
|
+
return "DB migration is not yet complete. Please run 'mihari db migrate'."
|
51
|
+
end
|
52
|
+
|
53
|
+
error.respond_to?(:detail) ? error.detail : nil
|
54
|
+
end
|
55
|
+
|
41
56
|
def safe_execute
|
42
57
|
yield
|
43
58
|
rescue StandardError => e
|
44
59
|
error = unwrap_error(e)
|
45
60
|
|
61
|
+
# Raise error if it's a Thor::Error to follow Thor's manner
|
62
|
+
raise error if error.is_a?(Thor::Error)
|
63
|
+
# Raise error if debug is set as true
|
46
64
|
raise error if options["debug"]
|
47
65
|
|
48
|
-
data = Entities::ErrorMessage.represent(
|
49
|
-
message: error.message,
|
50
|
-
detail: error.respond_to?(:detail) ? error.detail : nil
|
51
|
-
)
|
66
|
+
data = Entities::ErrorMessage.represent(message: error.message, detail: error_to_detail(error))
|
52
67
|
warn JSON.pretty_generate(data.as_json)
|
53
68
|
|
54
69
|
Sentry.capture_exception(error) if Sentry.initialized? && !error.is_a?(ValidationError)
|
@@ -12,7 +12,21 @@ module Mihari
|
|
12
12
|
thor.class_eval do
|
13
13
|
include Concerns::DatabaseConnectable
|
14
14
|
|
15
|
-
|
15
|
+
no_commands do
|
16
|
+
#
|
17
|
+
# @param [String] q
|
18
|
+
# @param [Integer] page
|
19
|
+
# @param [Integer] limit
|
20
|
+
#
|
21
|
+
# @return [Mihari::Services::ResultValue]
|
22
|
+
#
|
23
|
+
def _search(q, page: 1, limit: 10)
|
24
|
+
filter = Structs::Filters::Search.new(q: q, page: page, limit: limit)
|
25
|
+
Services::AlertSearcher.result(filter).value!
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "create PATH", "Create an alert"
|
16
30
|
around :with_db_connection
|
17
31
|
#
|
18
32
|
# @param [String] path
|
@@ -32,7 +46,7 @@ module Mihari
|
|
32
46
|
puts JSON.pretty_generate(data.as_json)
|
33
47
|
end
|
34
48
|
|
35
|
-
desc "list
|
49
|
+
desc "list QUERY", "List/search alerts"
|
36
50
|
around :with_db_connection
|
37
51
|
method_option :page, type: :numeric, default: 1
|
38
52
|
method_option :limit, type: :numeric, default: 10
|
@@ -40,8 +54,7 @@ module Mihari
|
|
40
54
|
# @param [String] q
|
41
55
|
#
|
42
56
|
def list(q = "")
|
43
|
-
|
44
|
-
value = Services::AlertSearcher.result(filter).value!
|
57
|
+
value = _search(q, page: options["page"], limit: options["limit"])
|
45
58
|
data = Entities::AlertsWithPagination.represent(
|
46
59
|
results: value.results,
|
47
60
|
total: value.total,
|
@@ -51,7 +64,29 @@ module Mihari
|
|
51
64
|
puts JSON.pretty_generate(data.as_json)
|
52
65
|
end
|
53
66
|
|
54
|
-
desc "
|
67
|
+
desc "list-transform QUERY", "List/search alerts with transformation"
|
68
|
+
around :with_db_connection
|
69
|
+
method_option :template, type: :string, required: true, aliases: "-t",
|
70
|
+
desc: "Jbuilder template stringor a path to a template"
|
71
|
+
method_option :page, type: :numeric, default: 1
|
72
|
+
method_option :limit, type: :numeric, default: 10
|
73
|
+
#
|
74
|
+
# @param [String] q
|
75
|
+
#
|
76
|
+
def list_transform(q = "")
|
77
|
+
value = _search(q, page: options["page"], limit: options["limit"])
|
78
|
+
puts Services::JbuilderRenderer.call(
|
79
|
+
options["template"],
|
80
|
+
{
|
81
|
+
results: value.results,
|
82
|
+
total: value.total,
|
83
|
+
current_page: value.filter[:page].to_i,
|
84
|
+
page_size: value.filter[:limit].to_i
|
85
|
+
}
|
86
|
+
)
|
87
|
+
end
|
88
|
+
|
89
|
+
desc "get ID", "Get an alert"
|
55
90
|
around :with_db_connection
|
56
91
|
#
|
57
92
|
# @param [Integer] id
|
@@ -62,7 +97,7 @@ module Mihari
|
|
62
97
|
puts JSON.pretty_generate(data.as_json)
|
63
98
|
end
|
64
99
|
|
65
|
-
desc "delete
|
100
|
+
desc "delete ID", "Delete an alert"
|
66
101
|
around :with_db_connection
|
67
102
|
#
|
68
103
|
# @param [Integer] id
|
@@ -11,7 +11,21 @@ module Mihari
|
|
11
11
|
thor.class_eval do
|
12
12
|
include Concerns::DatabaseConnectable
|
13
13
|
|
14
|
-
|
14
|
+
no_commands do
|
15
|
+
#
|
16
|
+
# @param [String] q
|
17
|
+
# @param [Integer] page
|
18
|
+
# @param [Integer] limit
|
19
|
+
#
|
20
|
+
# @return [Mihari::Services::ResultValue]
|
21
|
+
#
|
22
|
+
def _search(q, page: 1, limit: 10)
|
23
|
+
filter = Structs::Filters::Search.new(q: q, page: page, limit: limit)
|
24
|
+
Services::ArtifactSearcher.result(filter).value!
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "list QUERY", "List/search artifacts"
|
15
29
|
around :with_db_connection
|
16
30
|
method_option :page, type: :numeric, default: 1
|
17
31
|
method_option :limit, type: :numeric, default: 10
|
@@ -19,8 +33,7 @@ module Mihari
|
|
19
33
|
# @param [String] q
|
20
34
|
#
|
21
35
|
def list(q = "")
|
22
|
-
|
23
|
-
value = Services::ArtifactSearcher.result(filter).value!
|
36
|
+
value = _search(q, page: options["page"], limit: options["limit"])
|
24
37
|
data = Entities::ArtifactsWithPagination.represent(
|
25
38
|
results: value.results,
|
26
39
|
total: value.total,
|
@@ -30,7 +43,29 @@ module Mihari
|
|
30
43
|
puts JSON.pretty_generate(data.as_json)
|
31
44
|
end
|
32
45
|
|
33
|
-
desc "
|
46
|
+
desc "list-transform QUERY", "List/search artifacts with transformation"
|
47
|
+
around :with_db_connection
|
48
|
+
method_option :template, type: :string, required: true, aliases: "-t",
|
49
|
+
desc: "Jbuilder template stringor a path to a template"
|
50
|
+
method_option :page, type: :numeric, default: 1
|
51
|
+
method_option :limit, type: :numeric, default: 10
|
52
|
+
#
|
53
|
+
# @param [String] q
|
54
|
+
#
|
55
|
+
def list_transform(q = "")
|
56
|
+
value = _search(q, page: options["page"], limit: options["limit"])
|
57
|
+
puts Services::JbuilderRenderer.call(
|
58
|
+
options["template"],
|
59
|
+
{
|
60
|
+
results: value.results,
|
61
|
+
total: value.total,
|
62
|
+
current_page: value.filter[:page].to_i,
|
63
|
+
page_size: value.filter[:limit].to_i
|
64
|
+
}
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
desc "get ID", "Get an artifact"
|
34
69
|
around :with_db_connection
|
35
70
|
#
|
36
71
|
# @param [Integer] id
|
@@ -41,7 +76,7 @@ module Mihari
|
|
41
76
|
puts JSON.pretty_generate(data.as_json)
|
42
77
|
end
|
43
78
|
|
44
|
-
desc "enrich
|
79
|
+
desc "enrich ID", "Enrich an artifact"
|
45
80
|
around :with_db_connection
|
46
81
|
#
|
47
82
|
# @param [Integer] id
|
@@ -50,7 +85,7 @@ module Mihari
|
|
50
85
|
Services::ArtifactEnricher.result(id).value!
|
51
86
|
end
|
52
87
|
|
53
|
-
desc "delete
|
88
|
+
desc "delete ID", "Delete an artifact"
|
54
89
|
around :with_db_connection
|
55
90
|
#
|
56
91
|
# @param [Integer] id
|
data/lib/mihari/commands/rule.rb
CHANGED
@@ -12,7 +12,21 @@ module Mihari
|
|
12
12
|
thor.class_eval do
|
13
13
|
include Concerns::DatabaseConnectable
|
14
14
|
|
15
|
-
|
15
|
+
no_commands do
|
16
|
+
#
|
17
|
+
# @param [String] q
|
18
|
+
# @param [Integer] page
|
19
|
+
# @param [Integer] limit
|
20
|
+
#
|
21
|
+
# @return [Mihari::Services::ResultValue]
|
22
|
+
#
|
23
|
+
def _search(q, page: 1, limit: 10)
|
24
|
+
filter = Structs::Filters::Search.new(q: q, page: page, limit: limit)
|
25
|
+
Services::RuleSearcher.result(filter).value!
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "validate PATH", "Validate a rule"
|
16
30
|
#
|
17
31
|
# Validate format of a rule
|
18
32
|
#
|
@@ -23,7 +37,7 @@ module Mihari
|
|
23
37
|
puts rule.data.to_yaml
|
24
38
|
end
|
25
39
|
|
26
|
-
desc "init
|
40
|
+
desc "init PATH", "Initialize a new rule"
|
27
41
|
#
|
28
42
|
# Initialize a new rule file
|
29
43
|
#
|
@@ -36,7 +50,7 @@ module Mihari
|
|
36
50
|
Services::RuleInitializer.call(path)
|
37
51
|
end
|
38
52
|
|
39
|
-
desc "list
|
53
|
+
desc "list QUERY", "List/search rules"
|
40
54
|
around :with_db_connection
|
41
55
|
method_option :page, type: :numeric, default: 1
|
42
56
|
method_option :limit, type: :numeric, default: 10
|
@@ -44,8 +58,7 @@ module Mihari
|
|
44
58
|
# @param [String] q
|
45
59
|
#
|
46
60
|
def list(q = "")
|
47
|
-
|
48
|
-
value = Services::RuleSearcher.result(filter).value!
|
61
|
+
value = _search(q, page: options["page"], limit: options["limit"])
|
49
62
|
data = Entities::RulesWithPagination.represent(
|
50
63
|
results: value.results,
|
51
64
|
total: value.total,
|
@@ -55,7 +68,29 @@ module Mihari
|
|
55
68
|
puts JSON.pretty_generate(data.as_json)
|
56
69
|
end
|
57
70
|
|
58
|
-
desc "
|
71
|
+
desc "list-transform QUERY", "List/search rules with transformation"
|
72
|
+
around :with_db_connection
|
73
|
+
method_option :template, type: :string, required: true, aliases: "-t",
|
74
|
+
desc: "Jbuilder template stringor a path to a template"
|
75
|
+
method_option :page, type: :numeric, default: 1
|
76
|
+
method_option :limit, type: :numeric, default: 10
|
77
|
+
#
|
78
|
+
# @param [String] q
|
79
|
+
#
|
80
|
+
def list_transform(q = "")
|
81
|
+
value = _search(q, page: options["page"], limit: options["limit"])
|
82
|
+
puts Services::JbuilderRenderer.call(
|
83
|
+
options["template"],
|
84
|
+
{
|
85
|
+
results: value.results,
|
86
|
+
total: value.total,
|
87
|
+
current_page: value.filter[:page].to_i,
|
88
|
+
page_size: value.filter[:limit].to_i
|
89
|
+
}
|
90
|
+
)
|
91
|
+
end
|
92
|
+
|
93
|
+
desc "get ID", "Get a rule"
|
59
94
|
around :with_db_connection
|
60
95
|
def get(id)
|
61
96
|
value = Services::RuleGetter.result(id).value!
|
@@ -63,7 +98,7 @@ module Mihari
|
|
63
98
|
puts JSON.pretty_generate(data.as_json)
|
64
99
|
end
|
65
100
|
|
66
|
-
desc "delete
|
101
|
+
desc "delete ID", "Delete a rule"
|
67
102
|
around :with_db_connection
|
68
103
|
#
|
69
104
|
# @param [String] id
|
@@ -11,7 +11,7 @@ module Mihari
|
|
11
11
|
thor.class_eval do
|
12
12
|
include Concerns::DatabaseConnectable
|
13
13
|
|
14
|
-
desc "search
|
14
|
+
desc "search PATH_OR_ID", "Search by a rule"
|
15
15
|
around :with_db_connection
|
16
16
|
method_option :force_overwrite, type: :boolean, default: false, aliases: "-f",
|
17
17
|
desc: "Force overwriting a rule"
|
data/lib/mihari/commands/tag.rb
CHANGED
@@ -11,7 +11,21 @@ module Mihari
|
|
11
11
|
thor.class_eval do
|
12
12
|
include Concerns::DatabaseConnectable
|
13
13
|
|
14
|
-
|
14
|
+
no_commands do
|
15
|
+
#
|
16
|
+
# @param [String] q
|
17
|
+
# @param [Integer] page
|
18
|
+
# @param [Integer] limit
|
19
|
+
#
|
20
|
+
# @return [Mihari::Services::ResultValue]
|
21
|
+
#
|
22
|
+
def _search(q, page: 1, limit: 10)
|
23
|
+
filter = Structs::Filters::Search.new(q: q, page: page, limit: limit)
|
24
|
+
Services::TagSearcher.result(filter).value!
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "list QUERY", "List/search tags"
|
15
29
|
around :with_db_connection
|
16
30
|
method_option :page, type: :numeric, default: 1
|
17
31
|
method_option :limit, type: :numeric, default: 10
|
@@ -19,8 +33,7 @@ module Mihari
|
|
19
33
|
# @param [String] q
|
20
34
|
#
|
21
35
|
def list(q = "")
|
22
|
-
|
23
|
-
value = Services::TagSearcher.result(filter).value!
|
36
|
+
value = _search(q, page: options["page"], limit: options["limit"])
|
24
37
|
data = Entities::TagsWithPagination.represent(
|
25
38
|
results: value.results,
|
26
39
|
total: value.total,
|
@@ -30,7 +43,29 @@ module Mihari
|
|
30
43
|
puts JSON.pretty_generate(data.as_json)
|
31
44
|
end
|
32
45
|
|
33
|
-
desc "
|
46
|
+
desc "list-transform QUERY", "List/search tags with transformation"
|
47
|
+
around :with_db_connection
|
48
|
+
method_option :template, type: :string, required: true, aliases: "-t",
|
49
|
+
desc: "Jbuilder template stringor a path to a template"
|
50
|
+
method_option :page, type: :numeric, default: 1
|
51
|
+
method_option :limit, type: :numeric, default: 10
|
52
|
+
#
|
53
|
+
# @param [String] q
|
54
|
+
#
|
55
|
+
def list_transform(q = "")
|
56
|
+
value = _search(q, page: options["page"], limit: options["limit"])
|
57
|
+
puts Services::JbuilderRenderer.call(
|
58
|
+
options["template"],
|
59
|
+
{
|
60
|
+
results: value.results,
|
61
|
+
total: value.total,
|
62
|
+
current_page: value.filter[:page].to_i,
|
63
|
+
page_size: value.filter[:limit].to_i
|
64
|
+
}
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
desc "delete ID", "Delete a tag"
|
34
69
|
around :with_db_connection
|
35
70
|
#
|
36
71
|
# @param [Integer] id
|
data/lib/mihari/data_type.rb
CHANGED
@@ -28,7 +28,7 @@ module Mihari
|
|
28
28
|
def ip?
|
29
29
|
Try[IPAddr::InvalidAddressError] do
|
30
30
|
IPAddr.new(data).to_s == data
|
31
|
-
end.
|
31
|
+
end.recover { false }.value!
|
32
32
|
end
|
33
33
|
|
34
34
|
# @return [Boolean]
|
@@ -36,7 +36,7 @@ module Mihari
|
|
36
36
|
Try[Addressable::URI::InvalidURIError] do
|
37
37
|
uri = Addressable::URI.parse("http://#{data}")
|
38
38
|
uri.host == data && PublicSuffix.valid?(uri.host)
|
39
|
-
end.
|
39
|
+
end.recover { false }.value!
|
40
40
|
end
|
41
41
|
|
42
42
|
# @return [Boolean]
|
@@ -44,7 +44,7 @@ module Mihari
|
|
44
44
|
Try[Addressable::URI::InvalidURIError] do
|
45
45
|
uri = Addressable::URI.parse(data)
|
46
46
|
uri.scheme && uri.host && uri.path && PublicSuffix.valid?(uri.host)
|
47
|
-
end.
|
47
|
+
end.recover { false }.value!
|
48
48
|
end
|
49
49
|
|
50
50
|
# @return [Boolean]
|
data/lib/mihari/database.rb
CHANGED
@@ -156,7 +156,7 @@ module Mihari
|
|
156
156
|
Mihari::Database.connect unless connected?
|
157
157
|
yield
|
158
158
|
rescue ActiveRecord::StatementInvalid
|
159
|
-
Mihari.logger.error("
|
159
|
+
Mihari.logger.error("DB migration is not yet complete. Please run 'mihari db migrate'.")
|
160
160
|
ensure
|
161
161
|
Mihari::Database.close
|
162
162
|
end
|
data/lib/mihari/emitters/base.rb
CHANGED
@@ -30,13 +30,17 @@ module Mihari
|
|
30
30
|
# @return [Dry::Monads::Result::Success<Object>, Dry::Monads::Result::Failure]
|
31
31
|
#
|
32
32
|
def result(artifacts)
|
33
|
-
Try[StandardError] do
|
33
|
+
result = Try[StandardError] do
|
34
34
|
retry_on_error(
|
35
35
|
times: retry_times,
|
36
36
|
interval: retry_interval,
|
37
37
|
exponential_backoff: retry_exponential_backoff
|
38
38
|
) { call(artifacts) }
|
39
39
|
end.to_result
|
40
|
+
|
41
|
+
Mihari.logger.warn("Emitter:#{self.class.class_key} failed - #{result.failure}") if result.failure?
|
42
|
+
|
43
|
+
result
|
40
44
|
end
|
41
45
|
|
42
46
|
class << self
|
@@ -1,49 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "erb"
|
4
|
-
|
5
3
|
module Mihari
|
6
4
|
module Emitters
|
7
|
-
class ERBTemplate < ERB
|
8
|
-
class << self
|
9
|
-
def template
|
10
|
-
%{
|
11
|
-
{
|
12
|
-
"rule": {
|
13
|
-
"id": "<%= @rule.id %>",
|
14
|
-
"title": "<%= @rule.title %>",
|
15
|
-
"description": "<%= @rule.description %>"
|
16
|
-
},
|
17
|
-
"artifacts": [
|
18
|
-
<% @artifacts.each_with_index do |artifact, idx| %>
|
19
|
-
"<%= artifact.data %>"
|
20
|
-
<%= ',' if idx < (@artifacts.length - 1) %>
|
21
|
-
<% end %>
|
22
|
-
],
|
23
|
-
"tags": [
|
24
|
-
<% @rule.tags.each_with_index do |tag, idx| %>
|
25
|
-
"<%= tag.name %>"
|
26
|
-
<%= ',' if idx < (@rule.tags.length - 1) %>
|
27
|
-
<% end %>
|
28
|
-
]
|
29
|
-
}
|
30
|
-
}
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def initialize(artifacts:, rule:, options: {})
|
35
|
-
@artifacts = artifacts
|
36
|
-
@rule = rule
|
37
|
-
|
38
|
-
@template = options.fetch(:template, self.class.template)
|
39
|
-
super(@template)
|
40
|
-
end
|
41
|
-
|
42
|
-
def result
|
43
|
-
super(binding)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
5
|
class Webhook < Base
|
48
6
|
# @return [Addressable::URI, nil]
|
49
7
|
attr_reader :url
|
@@ -54,12 +12,24 @@ module Mihari
|
|
54
12
|
# @return [String]
|
55
13
|
attr_reader :method
|
56
14
|
|
57
|
-
# @return [String
|
15
|
+
# @return [String]
|
58
16
|
attr_reader :template
|
59
17
|
|
60
18
|
# @return [Array<Mihari::Models::Artifact>]
|
61
19
|
attr_accessor :artifacts
|
62
20
|
|
21
|
+
DEFAULT_TEMPLATE = %{
|
22
|
+
json.rule do
|
23
|
+
json.id rule.id
|
24
|
+
json.title rule.title
|
25
|
+
json.description rule.description
|
26
|
+
end
|
27
|
+
|
28
|
+
json.artifacts artifacts.map(&:data)
|
29
|
+
|
30
|
+
json.tags rule.tags.map(&:name)
|
31
|
+
}
|
32
|
+
|
63
33
|
#
|
64
34
|
# @param [Mihari::Rule] rule
|
65
35
|
# @param [Hash, nil] options
|
@@ -71,7 +41,7 @@ module Mihari
|
|
71
41
|
@url = Addressable::URI.parse(params[:url])
|
72
42
|
@headers = params[:headers] || {}
|
73
43
|
@method = params[:method] || "POST"
|
74
|
-
@template = params[:template]
|
44
|
+
@template = params[:template] || DEFAULT_TEMPLATE
|
75
45
|
|
76
46
|
@artifacts = []
|
77
47
|
end
|
@@ -114,15 +84,7 @@ module Mihari
|
|
114
84
|
# @return [String]
|
115
85
|
#
|
116
86
|
def render
|
117
|
-
|
118
|
-
options[:template] = File.read(template) unless template.nil?
|
119
|
-
|
120
|
-
erb_template = ERBTemplate.new(
|
121
|
-
artifacts: artifacts,
|
122
|
-
rule: rule,
|
123
|
-
options: options
|
124
|
-
)
|
125
|
-
erb_template.result
|
87
|
+
Services::JbuilderRenderer.call(template, { rule: rule, artifacts: artifacts })
|
126
88
|
end
|
127
89
|
|
128
90
|
#
|