mihari 7.0.5 → 7.1.1

Sign up to get free protection for your applications and to get access to all the features.
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
  #