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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aca048c797a73db1c57f0a5ad5f29066fbfba69451c71df1a26c3ea57853a1e3
4
- data.tar.gz: 5ce2d298f325cc9d0ae50a98a6c18a64ec11cc06386504535c8ca647c7c68ad0
3
+ metadata.gz: 8ea0adf73bba53f264c22f8885d44e24de5657a2a77c2b7ea3533bb5acf6e78b
4
+ data.tar.gz: 77ab077d9322a22b0e399c81c057485aec1c4cdb1a14f15cbd81f1a3650f37a6
5
5
  SHA512:
6
- metadata.gz: ac1d6ab49351a2c9c60e703a5fe5fe0f94c84c4294271f297d529a781d3a87a37b515d090a78676bcaf3da225f03617f8900966d108e31557e86319a2407b00b
7
- data.tar.gz: f5aad0f8648783f4d3c3cc42a087f646358e5e93586cb394a2644e8f6c32a296b3668ea7758348c0dd55d4868527b4aa6a3d774ee113d5a4a14c39ae3ecd2857
6
+ metadata.gz: 3b68702c146819189140c0c5626c27f7b53f94021dd21138f0ac2758366486cebb7c450e0168f76cfc3d9940ee86eb2b279eb1725a3922614f51930dcb0e6b71
7
+ data.tar.gz: e30c01360a6d382e73a8e88a40a5cd3f1dba6bac9ebbbe9b87b9b51cff4a6dc59676d19eec4fe757a3a40d78a560886e434802b74fdf125e663b73ae4c7abd8a
data/.rubocop.yml CHANGED
@@ -17,6 +17,8 @@ RSpec/MultipleMemoizedHelpers:
17
17
  Max: 10
18
18
  RSpec/ExampleLength:
19
19
  Max: 20
20
+ RSpec/FilePath:
21
+ SpecSuffixOnly: true
20
22
  require:
21
23
  - rubocop-factory_bot
22
24
  - rubocop-rake
data/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # mihari
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/mihari.svg)](https://badge.fury.io/rb/mihari)
4
- [![Ruby CI](https://github.com/ninoseki/mihari/actions/workflows/test.yml/badge.svg)](https://github.com/ninoseki/mihari/actions/workflows/test.yml)
4
+ [![Ruby CI](https://github.com/ninoseki/mihari/actions/workflows/ruby.yml/badge.svg)](https://github.com/ninoseki/mihari/actions/workflows/ruby.yml)
5
+ [![Node.js CI](https://github.com/ninoseki/mihari/actions/workflows/node.yml/badge.svg)](https://github.com/ninoseki/mihari/actions/workflows/node.yml)
5
6
  [![Coverage Status](https://coveralls.io/repos/github/ninoseki/mihari/badge.svg?branch=master)](https://coveralls.io/github/ninoseki/mihari?branch=master)
6
7
  [![CodeFactor](https://www.codefactor.io/repository/github/ninoseki/mihari/badge)](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 "run rackup (via rerun)"
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
- res = Try[StandardError] do
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
- Failure AnalyzerError.new(
100
- result.failure.message,
101
- self.class.class_key,
102
- cause: result.failure
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
- desc "create [PATH]", "Create an alert"
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 [QUERY]", "List/search alerts"
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
- filter = Structs::Filters::Search.new(q: q, page: options["page"], limit: options["limit"])
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 "get [ID]", "Get an alert"
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 [ID]", "Delete an alert"
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
- desc "list [QUERY]", "List/search artifacts"
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
- filter = Structs::Filters::Search.new(q: q, page: options["page"], limit: options["limit"])
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 "get [ID]", "Get an artifact"
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 [ID]", "Enrich an artifact"
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 [ID]", "Delete an artifact"
88
+ desc "delete ID", "Delete an artifact"
54
89
  around :with_db_connection
55
90
  #
56
91
  # @param [Integer] id
@@ -9,7 +9,7 @@ module Mihari
9
9
  class << self
10
10
  def included(thor)
11
11
  thor.class_eval do
12
- desc "list", "List config"
12
+ desc "list", "List configs"
13
13
  def list
14
14
  configs = Services::ConfigSearcher.call
15
15
  data = configs.map { |config| Entities::Config.represent(config) }
@@ -12,7 +12,21 @@ module Mihari
12
12
  thor.class_eval do
13
13
  include Concerns::DatabaseConnectable
14
14
 
15
- desc "validate [PATH]", "Validate a rule file"
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 [PATH]", "Initialize a new rule file"
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 [QUERY]", "List/search rules"
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
- filter = Structs::Filters::Search.new(q: q, page: options["page"], limit: options["limit"])
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 "get [ID]", "Get a rule"
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 [ID]", "Delete a rule"
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 [PATH_OR_ID]", "Search by a rule"
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"
@@ -11,7 +11,21 @@ module Mihari
11
11
  thor.class_eval do
12
12
  include Concerns::DatabaseConnectable
13
13
 
14
- desc "list", "List/search tags"
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
- filter = Structs::Filters::Search.new(q: q, page: options["page"], limit: options["limit"])
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 "delete [ID]", "Delete a tag"
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
@@ -20,8 +20,8 @@ module Mihari
20
20
  return true if RETRIABLE_ERRORS.any? { |klass| error.is_a? klass }
21
21
 
22
22
  case error
23
- when StatusCodeError
24
- error.status_code != 404
23
+ when StatusError
24
+ ![401, 404].include?(error.status_code)
25
25
  else
26
26
  false
27
27
  end
@@ -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.to_result.value_or(false)
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.to_result.value_or(false)
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.to_result.value_or(false)
47
+ end.recover { false }.value!
48
48
  end
49
49
 
50
50
  # @return [Boolean]
@@ -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("The DB migration is not yet complete. Please run 'mihari db migrate'.")
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
@@ -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, nil]
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
- options = {}
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
  #