mihari 7.1.3 → 7.3.0

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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +2 -2
  3. data/Rakefile +8 -1
  4. data/lefthook.yml +4 -1
  5. data/lib/mihari/actor.rb +16 -0
  6. data/lib/mihari/analyzers/base.rb +7 -25
  7. data/lib/mihari/analyzers/binaryedge.rb +0 -6
  8. data/lib/mihari/analyzers/censys.rb +0 -9
  9. data/lib/mihari/analyzers/circl.rb +0 -6
  10. data/lib/mihari/analyzers/fofa.rb +0 -6
  11. data/lib/mihari/analyzers/greynoise.rb +0 -6
  12. data/lib/mihari/analyzers/hunterhow.rb +0 -6
  13. data/lib/mihari/analyzers/onyphe.rb +0 -6
  14. data/lib/mihari/analyzers/otx.rb +0 -6
  15. data/lib/mihari/analyzers/passivetotal.rb +0 -4
  16. data/lib/mihari/analyzers/pulsedive.rb +0 -6
  17. data/lib/mihari/analyzers/securitytrails.rb +0 -4
  18. data/lib/mihari/analyzers/shodan.rb +0 -6
  19. data/lib/mihari/analyzers/urlscan.rb +0 -6
  20. data/lib/mihari/analyzers/virustotal.rb +0 -4
  21. data/lib/mihari/analyzers/virustotal_intelligence.rb +7 -6
  22. data/lib/mihari/analyzers/zoomeye.rb +0 -6
  23. data/lib/mihari/commands/web.rb +1 -1
  24. data/lib/mihari/concerns/falsepositive_normalizable.rb +30 -0
  25. data/lib/mihari/concerns/falsepositive_validatable.rb +1 -17
  26. data/lib/mihari/config.rb +1 -1
  27. data/lib/mihari/database.rb +18 -1
  28. data/lib/mihari/emitters/database.rb +0 -6
  29. data/lib/mihari/emitters/misp.rb +0 -6
  30. data/lib/mihari/emitters/slack.rb +5 -21
  31. data/lib/mihari/emitters/the_hive.rb +0 -6
  32. data/lib/mihari/enrichers/base.rb +54 -12
  33. data/lib/mihari/enrichers/google_public_dns.rb +28 -7
  34. data/lib/mihari/enrichers/mmdb.rb +25 -7
  35. data/lib/mihari/enrichers/shodan.rb +35 -4
  36. data/lib/mihari/enrichers/whois.rb +37 -31
  37. data/lib/mihari/entities/artifact.rb +6 -2
  38. data/lib/mihari/entities/autonomous_system.rb +1 -1
  39. data/lib/mihari/entities/cpe.rb +1 -1
  40. data/lib/mihari/entities/port.rb +1 -1
  41. data/lib/mihari/entities/vulnerability.rb +10 -0
  42. data/lib/mihari/errors.rb +2 -0
  43. data/lib/mihari/models/alert.rb +12 -0
  44. data/lib/mihari/models/artifact.rb +118 -159
  45. data/lib/mihari/models/rule.rb +21 -0
  46. data/lib/mihari/models/vulnerability.rb +12 -0
  47. data/lib/mihari/rule.rb +44 -29
  48. data/lib/mihari/schemas/alert.rb +3 -3
  49. data/lib/mihari/schemas/analyzer.rb +27 -27
  50. data/lib/mihari/schemas/emitter.rb +9 -9
  51. data/lib/mihari/schemas/macros.rb +2 -2
  52. data/lib/mihari/schemas/options.rb +2 -5
  53. data/lib/mihari/schemas/rule.rb +19 -12
  54. data/lib/mihari/services/builders.rb +0 -134
  55. data/lib/mihari/services/enrichers.rb +3 -1
  56. data/lib/mihari/services/feed.rb +2 -5
  57. data/lib/mihari/services/getters.rb +1 -1
  58. data/lib/mihari/services/proxies.rb +3 -3
  59. data/lib/mihari/structs/censys.rb +2 -2
  60. data/lib/mihari/structs/greynoise.rb +1 -1
  61. data/lib/mihari/structs/onyphe.rb +1 -1
  62. data/lib/mihari/structs/shodan.rb +59 -21
  63. data/lib/mihari/version.rb +1 -1
  64. data/lib/mihari/web/endpoints/artifacts.rb +4 -2
  65. data/lib/mihari/web/endpoints/rules.rb +1 -1
  66. data/lib/mihari/web/public/assets/{index-TOeU8PE2.js → index-JHS0L8KZ.js} +47 -47
  67. data/lib/mihari/web/public/assets/{index-dVaNxqTC.css → index-ReF8ffd-.css} +1 -1
  68. data/lib/mihari/web/public/index.html +2 -2
  69. data/lib/mihari/web/public/redoc-static.html +17 -17
  70. data/lib/mihari.rb +3 -0
  71. data/mihari.gemspec +2 -2
  72. data/requirements.txt +1 -1
  73. metadata +11 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d7463506c12a2476cfea5dda939e88e01150fb6539c232e102c15fca4c57ab99
4
- data.tar.gz: fdb700f461140badc1d90b3199999a97e483e57ae9c9bfed2d0157a94f35529d
3
+ metadata.gz: 328a34edf637c36456cc7de39fccabdaaf75937b449b5e8a8e0434e71b9328c2
4
+ data.tar.gz: 8483c669cfb3e715c86b4a3878961885e4cd4f93611e487c4360cb3030267c18
5
5
  SHA512:
6
- metadata.gz: 18d9dd1f63056cd2731caa823904fd9df80ca21e63a540eab89d06e7951fa10cde498344049f3ba7de8740da2e8cfb9da9fe660f77c3a1abca5e308e36a6973c
7
- data.tar.gz: 5a8ce97455cb0e68538af1ca2c8af5b89ea96f817d4f11cf6c00851035a20288f9bc2d2de16b58b52f3e72e988c77a2e4d8744a3fbebfd27c2cb6895d4c4dccb
6
+ metadata.gz: 9858608c1ceb30f846a27b487e4bcbbad463aba56a401991fdade512bf6a7a7f028e5facecd764b200d94795a1da1a2b631b85540dfe1bbf002c756100b6627f
7
+ data.tar.gz: c050fdafab0e7855eb610ad3d50762583ca931d31afeeb57fc654a2dc65ff381f20cb31eb28d0a61b5888c0a19df02320b09a529830fe332389b59f739721aba
data/Dockerfile CHANGED
@@ -1,8 +1,8 @@
1
- FROM ruby:3.2.2-alpine3.19
1
+ FROM ruby:3.3.0-alpine3.19
2
2
 
3
3
  ARG MIHARI_VERSION=0.0.0
4
4
 
5
- RUN apk --no-cache add build-base ruby-dev libpq-dev && \
5
+ RUN apk --no-cache add build-base ruby-dev libpq-dev whois && \
6
6
  echo 'gem: --no-document' >> /usr/local/etc/gemrc && \
7
7
  gem install pg && \
8
8
  gem install mihari -v ${MIHARI_VERSION} && \
data/Rakefile CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "time"
4
+
3
5
  require "rspec/core/rake_task"
4
6
  require "standard/rake"
5
7
 
@@ -46,13 +48,18 @@ namespace :build do
46
48
  desc "Build Swagger doc"
47
49
  task :swagger, [:path] do |_t, args|
48
50
  args.with_defaults(path: "./frontend/swagger.yaml")
51
+
52
+ started_at = Time.now
49
53
  build_swagger_doc args.path
54
+ elapsed = (Time.now - started_at).floor(2)
55
+
56
+ puts "Swagger doc is built in #{elapsed}s"
50
57
  end
51
58
  end
52
59
 
53
60
  task :build do
54
- # Build Swagger dos
55
61
  Rake::Task["build:swagger"].invoke
62
+
56
63
  # Build ReDocs docs & frontend assets
57
64
  sh "cd frontend && npm install && npm run docs && npm run build-only"
58
65
  # Copy built assets into ./lib/web/public/
data/lefthook.yml CHANGED
@@ -1,5 +1,4 @@
1
1
  pre-commit:
2
- parallel: true
3
2
  commands:
4
3
  standard:
5
4
  glob: "*.rb"
@@ -15,6 +14,10 @@ pre-commit:
15
14
  glob: "*.{js,ts,vue}"
16
15
  run: npx prettier --write {staged_files}
17
16
  stage_fixed: true
17
+ type-check:
18
+ root: "frontend/"
19
+ glob: "*.{js,ts,vue}"
20
+ run: npm run type-check
18
21
  actionlint:
19
22
  glob: ".github/workflows/*.yaml"
20
23
  run: actionlint
data/lib/mihari/actor.rb CHANGED
@@ -50,6 +50,13 @@ module Mihari
50
50
  options[:timeout]
51
51
  end
52
52
 
53
+ #
54
+ # @return [Boolean]
55
+ #
56
+ def parallel?
57
+ options[:parallel] || Mihari.config.parallel
58
+ end
59
+
53
60
  def validate_configuration!
54
61
  return if configured?
55
62
 
@@ -93,6 +100,15 @@ module Mihari
93
100
  ([key] + [key_aliases]).flatten.compact.map(&:downcase)
94
101
  end
95
102
 
103
+ def configuration_keys
104
+ # Automatically generate configuration keys based on key
105
+ # For example,
106
+ # - Shodan analyzer's key is "shodan"
107
+ # - Mihari.config has "shodan_api_key"
108
+ # - Select "shodan_api_key" by using "#{key}_" prefix
109
+ Mihari.config.keys.select { |config_key| config_key.start_with?("#{key}_") }
110
+ end
111
+
96
112
  def type
97
113
  return "analyzer" if ancestors.include?(Mihari::Analyzers::Base)
98
114
  return "emitter" if ancestors.include?(Mihari::Emitters::Base)
@@ -40,13 +40,6 @@ module Mihari
40
40
  options[:ignore_error] || Mihari.config.ignore_error
41
41
  end
42
42
 
43
- #
44
- # @return [Boolean]
45
- #
46
- def parallel?
47
- options[:parallel] || Mihari.config.parallel
48
- end
49
-
50
43
  # @return [Array<String>, Array<Mihari::Models::Artifact>]
51
44
  def artifacts
52
45
  raise NotImplementedError, "You must implement #{self.class}##{__method__}"
@@ -63,12 +56,10 @@ module Mihari
63
56
  artifacts.compact.sort.map do |artifact|
64
57
  # No need to set data_type manually
65
58
  # It is set automatically in #initialize
66
- artifact = artifact.is_a?(Models::Artifact) ? artifact : Models::Artifact.new(data: artifact)
67
-
68
- artifact.source = self.class.key
69
- artifact.query = query
70
-
71
- artifact
59
+ (artifact.is_a?(Models::Artifact) ? artifact : Models::Artifact.new(data: artifact)).tap do |normalized|
60
+ normalized.source = self.class.key
61
+ normalized.query = query
62
+ end
72
63
  end.select(&:valid?).uniq(&:data)
73
64
  end
74
65
 
@@ -118,18 +109,9 @@ module Mihari
118
109
  #
119
110
  # @return [Mihari::Analyzers::Base]
120
111
  #
121
- def from_query(params)
122
- copied = params.deep_dup
123
-
124
- # convert params into arguments for initialization
125
- query = copied[:query]
126
-
127
- # delete analyzer and query
128
- %i[analyzer query].each { |key| copied.delete key }
129
-
130
- copied[:options] = copied[:options] || nil
131
-
132
- new(query, **copied)
112
+ def from_params(params)
113
+ query = params.delete(:query)
114
+ new(query, **params)
133
115
  end
134
116
 
135
117
  def inherited(child)
@@ -24,12 +24,6 @@ module Mihari
24
24
  client.search_with_pagination(query, pagination_limit: pagination_limit).map(&:artifacts).flatten
25
25
  end
26
26
 
27
- class << self
28
- def configuration_keys
29
- %w[binaryedge_api_key]
30
- end
31
- end
32
-
33
27
  private
34
28
 
35
29
  #
@@ -41,15 +41,6 @@ module Mihari
41
41
  configuration_keys? || (id? && secret?)
42
42
  end
43
43
 
44
- class << self
45
- #
46
- # @return [Array<String>]
47
- #
48
- def configuration_keys
49
- %w[censys_id censys_secret]
50
- end
51
- end
52
-
53
44
  private
54
45
 
55
46
  #
@@ -47,12 +47,6 @@ module Mihari
47
47
  configuration_keys? || (username? && password?)
48
48
  end
49
49
 
50
- class << self
51
- def configuration_keys
52
- %w[circl_passive_password circl_passive_username]
53
- end
54
- end
55
-
56
50
  private
57
51
 
58
52
  def client
@@ -35,12 +35,6 @@ module Mihari
35
35
  api_key? && email?
36
36
  end
37
37
 
38
- class << self
39
- def configuration_keys
40
- %w[fofa_api_key fofa_email]
41
- end
42
- end
43
-
44
38
  private
45
39
 
46
40
  def email?
@@ -27,12 +27,6 @@ module Mihari
27
27
  ).map(&:artifacts).flatten
28
28
  end
29
29
 
30
- class << self
31
- def configuration_keys
32
- %w[greynoise_api_key]
33
- end
34
- end
35
-
36
30
  private
37
31
 
38
32
  def client
@@ -44,12 +44,6 @@ module Mihari
44
44
  end.flatten
45
45
  end
46
46
 
47
- class << self
48
- def configuration_keys
49
- %w[hunterhow_api_key]
50
- end
51
- end
52
-
53
47
  private
54
48
 
55
49
  def client
@@ -29,12 +29,6 @@ module Mihari
29
29
  ).map(&:artifacts).flatten
30
30
  end
31
31
 
32
- class << self
33
- def configuration_keys
34
- %w[onyphe_api_key]
35
- end
36
- end
37
-
38
32
  private
39
33
 
40
34
  def client
@@ -38,12 +38,6 @@ module Mihari
38
38
  end
39
39
  end
40
40
 
41
- class << self
42
- def configuration_keys
43
- %w[otx_api_key]
44
- end
45
- end
46
-
47
41
  private
48
42
 
49
43
  def client
@@ -50,10 +50,6 @@ module Mihari
50
50
  end
51
51
 
52
52
  class << self
53
- def configuration_keys
54
- %w[passivetotal_username passivetotal_api_key]
55
- end
56
-
57
53
  #
58
54
  # @return [Array<String>, nil]
59
55
  #
@@ -43,12 +43,6 @@ module Mihari
43
43
  end
44
44
  end
45
45
 
46
- class << self
47
- def configuration_keys
48
- %w[pulsedive_api_key]
49
- end
50
- end
51
-
52
46
  private
53
47
 
54
48
  def client
@@ -44,10 +44,6 @@ module Mihari
44
44
  end
45
45
 
46
46
  class << self
47
- def configuration_keys
48
- %w[securitytrails_api_key]
49
- end
50
-
51
47
  #
52
48
  # @return [Array<String>, nil]
53
49
  #
@@ -27,12 +27,6 @@ module Mihari
27
27
  ).map(&:artifacts).flatten.uniq(&:data)
28
28
  end
29
29
 
30
- class << self
31
- def configuration_keys
32
- %w[shodan_api_key]
33
- end
34
- end
35
-
36
30
  private
37
31
 
38
32
  #
@@ -37,12 +37,6 @@ module Mihari
37
37
  artifacts.select { |artifact| allowed_data_types.include? artifact.data_type }
38
38
  end
39
39
 
40
- class << self
41
- def configuration_keys
42
- %w[urlscan_api_key]
43
- end
44
- end
45
-
46
40
  private
47
41
 
48
42
  def client
@@ -39,10 +39,6 @@ module Mihari
39
39
  end
40
40
 
41
41
  class << self
42
- def configuration_keys
43
- %w[virustotal_api_key]
44
- end
45
-
46
42
  #
47
43
  # @return [Array<String>, nil]
48
44
  #
@@ -24,12 +24,6 @@ module Mihari
24
24
  client.intel_search_with_pagination(query, pagination_limit: pagination_limit).map(&:artifacts).flatten
25
25
  end
26
26
 
27
- class << self
28
- def configuration_keys
29
- %w[virustotal_api_key]
30
- end
31
- end
32
-
33
27
  class << self
34
28
  #
35
29
  # @return [String]
@@ -44,6 +38,13 @@ module Mihari
44
38
  def key_aliases
45
39
  ["vt_intel"]
46
40
  end
41
+
42
+ #
43
+ # @return [Array<String>]
44
+ #
45
+ def configuration_keys
46
+ %w[virustotal_api_key]
47
+ end
47
48
  end
48
49
 
49
50
  private
@@ -40,12 +40,6 @@ module Mihari
40
40
  end
41
41
  end
42
42
 
43
- class << self
44
- def configuration_keys
45
- %w[zoomeye_api_key]
46
- end
47
- end
48
-
49
43
  private
50
44
 
51
45
  #
@@ -12,7 +12,7 @@ module Mihari
12
12
  desc "web", "Start the web app"
13
13
  method_option :port, type: :numeric, default: 9292, desc: "Port to listen on"
14
14
  method_option :host, type: :string, default: "localhost", desc: "Hostname to listen on"
15
- method_option :threads, type: :string, default: "0:5", desc: "min:max threads to use"
15
+ method_option :threads, type: :string, default: "0:3", desc: "min:max threads to use"
16
16
  method_option :verbose, type: :boolean, default: false, desc: "Don't report each request"
17
17
  method_option :worker_timeout, type: :numeric, default: 60, desc: "Worker timeout value (in seconds)"
18
18
  method_option :open, type: :boolean, default: true, desc: "Whether to open the app in browser or not"
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mihari
4
+ module Concerns
5
+ #
6
+ # False positive normalizable concern
7
+ #
8
+ module FalsePositiveNormalizable
9
+ extend ActiveSupport::Concern
10
+
11
+ prepend MemoWise
12
+
13
+ #
14
+ # Normalize a falsepositive value
15
+ #
16
+ # @param [String] value
17
+ #
18
+ # @return [String, Regexp]
19
+ #
20
+ def normalize_falsepositive(value)
21
+ return value if !value.start_with?("/") || !value.end_with?("/")
22
+
23
+ # if a value is surrounded by slashes, take it as a regexp
24
+ value_without_slashes = value[1..-2]
25
+ Regexp.compile value_without_slashes.to_s
26
+ end
27
+ memo_wise :normalize_falsepositive
28
+ end
29
+ end
30
+ end
@@ -8,23 +8,7 @@ module Mihari
8
8
  module FalsePositiveValidatable
9
9
  extend ActiveSupport::Concern
10
10
 
11
- prepend MemoWise
12
-
13
- #
14
- # Normalize a falsepositive value
15
- #
16
- # @param [String] value
17
- #
18
- # @return [String, Regexp]
19
- #
20
- def normalize_falsepositive(value)
21
- return value if !value.start_with?("/") || !value.end_with?("/")
22
-
23
- # if a value is surrounded by slashes, take it as a regexp
24
- value_without_slashes = value[1..-2]
25
- Regexp.compile value_without_slashes.to_s
26
- end
27
- memo_wise :normalize_falsepositive
11
+ include FalsePositiveNormalizable
28
12
 
29
13
  #
30
14
  # Check whether a value is valid format as a disallowed data value
data/lib/mihari/config.rb CHANGED
@@ -170,7 +170,7 @@ module Mihari
170
170
  # @return [Array<String>]
171
171
  #
172
172
  def keys
173
- to_h.keys.map(&:to_s).map(&:upcase)
173
+ @keys ||= to_h.keys.map(&:to_s).map(&:downcase)
174
174
  end
175
175
  end
176
176
  end
@@ -104,11 +104,28 @@ class V7Schema < ActiveRecord::Migration[7.1]
104
104
  end
105
105
  end
106
106
 
107
+ class V72Schema < ActiveRecord::Migration[7.1]
108
+ def change
109
+ create_table :vulnerabilities, if_not_exists: true do |t|
110
+ t.string :name, null: false
111
+ t.datetime :created_at
112
+
113
+ t.belongs_to :artifact, foreign_key: true, null: false
114
+ end
115
+
116
+ rename_column :cpes, :cpe, :name if ActiveRecord::Base.connection.column_exists?(:cpes, :cpe)
117
+ rename_column :autonomous_systems, :asn, :number if ActiveRecord::Base.connection.column_exists?(
118
+ :autonomous_systems, :asn
119
+ )
120
+ rename_column :ports, :port, :number if ActiveRecord::Base.connection.column_exists?(:ports, :port)
121
+ end
122
+ end
123
+
107
124
  #
108
125
  # @return [Array<ActiveRecord::Migration>] schemas
109
126
  #
110
127
  def schemas
111
- [V7Schema]
128
+ [V7Schema, V72Schema]
112
129
  end
113
130
 
114
131
  module Mihari
@@ -24,12 +24,6 @@ module Mihari
24
24
  def target
25
25
  Mihari.config.database_url.host || Mihari.config.database_url.to_s
26
26
  end
27
-
28
- class << self
29
- def configuration_keys
30
- %w[database_url]
31
- end
32
- end
33
27
  end
34
28
  end
35
29
  end
@@ -63,12 +63,6 @@ module Mihari
63
63
  URI(url).host || "N/A"
64
64
  end
65
65
 
66
- class << self
67
- def configuration_keys
68
- %w[misp_url misp_api_key]
69
- end
70
- end
71
-
72
66
  private
73
67
 
74
68
  def client
@@ -176,21 +176,11 @@ module Mihari
176
176
  # @return [::Slack::Notifier]
177
177
  #
178
178
  def notifier
179
- @notifier ||= [].tap do |out|
180
- out << if timeout.nil?
181
- ::Slack::Notifier.new(
182
- webhook_url,
183
- channel: channel, username: username
184
- )
185
- else
186
- ::Slack::Notifier.new(
187
- webhook_url,
188
- channel: channel,
189
- username: username,
190
- http_options: { timeout: timeout }
191
- )
192
- end
193
- end.first
179
+ @notifier ||= lambda do
180
+ return ::Slack::Notifier.new(webhook_url, channel: channel, username: username) if timeout.nil?
181
+
182
+ ::Slack::Notifier.new(webhook_url, channel: channel, username: username, http_options: { timeout: timeout })
183
+ end.call
194
184
  end
195
185
 
196
186
  #
@@ -227,12 +217,6 @@ module Mihari
227
217
 
228
218
  notifier.post(text: text, attachments: attachments, mrkdwn: true)
229
219
  end
230
-
231
- class << self
232
- def configuration_keys
233
- %w[slack_webhook_url slack_channel]
234
- end
235
- end
236
220
  end
237
221
  end
238
222
  end
@@ -53,12 +53,6 @@ module Mihari
53
53
  client.alert payload
54
54
  end
55
55
 
56
- class << self
57
- def configuration_keys
58
- %w[thehive_url thehive_api_key]
59
- end
60
- end
61
-
62
56
  private
63
57
 
64
58
  def client
@@ -6,46 +6,88 @@ module Mihari
6
6
  # Base class for enrichers
7
7
  #
8
8
  class Base < Actor
9
- prepend MemoWise
10
-
9
+ #
10
+ # @param [Hash, nil] options
11
+ #
11
12
  def initialize(options: nil)
12
13
  super(options: options)
13
14
  end
14
15
 
15
16
  #
16
- # @param [String] value
17
+ # Enrich an artifact
18
+ #
19
+ # @param [Mihari::Models::Artifact] artifact
17
20
  #
18
- def call(value)
21
+ # @return [Mihari::Models::Artifact]
22
+ #
23
+ def call(artifact)
19
24
  raise NotImplementedError, "You must implement #{self.class}##{__method__}"
20
25
  end
21
26
 
22
27
  #
23
- # @param [Mihari::Models::Artifact] value
28
+ # @param [Mihari::Models::Artifact] artifact
24
29
  #
25
30
  # @return [Dry::Monads::Result::Success<Object>, Dry::Monads::Result::Failure]
26
31
  #
27
- def result(value)
32
+ def result(artifact)
33
+ return unless callable?(artifact)
34
+
28
35
  result = Try[StandardError] do
29
- retry_on_error(
30
- times: retry_times,
31
- interval: retry_interval,
32
- exponential_backoff: retry_exponential_backoff
33
- ) { call value }
36
+ retry_on_error(times: retry_times, interval: retry_interval,
37
+ exponential_backoff: retry_exponential_backoff) do
38
+ call artifact
39
+ end
34
40
  end.to_result
35
41
 
36
42
  if result.failure?
37
- Mihari.logger.warn("Enricher:#{self.class.key} for #{value.truncate(32)} failed: #{result.failure}")
43
+ Mihari.logger.warn("Enricher:#{self.class.key} for #{artifact.data.truncate(32)} failed: #{result.failure}")
38
44
  end
39
45
 
40
46
  result
41
47
  end
42
48
 
49
+ #
50
+ # @param [Mihari::Models::Artifact] artifact
51
+ #
52
+ # @return [Boolean]
53
+ #
54
+ def callable?(artifact)
55
+ callable_data_type?(artifact) && callable_relationships?(artifact)
56
+ end
57
+
43
58
  class << self
44
59
  def inherited(child)
45
60
  super
46
61
  Mihari.enrichers << child
47
62
  end
48
63
  end
64
+
65
+ private
66
+
67
+ #
68
+ # @param [Mihari::Models::Artifact] artifact
69
+ #
70
+ # @return [Boolean]
71
+ #
72
+ def callable_data_type?(artifact)
73
+ supported_data_types.include? artifact.data_type
74
+ end
75
+
76
+ #
77
+ # @param [Mihari::Models::Artifact] artifact
78
+ #
79
+ # @return [Boolean]
80
+ #
81
+ def callable_relationships?(artifact)
82
+ raise NotImplementedError, "You must implement #{self.class}##{__method__}"
83
+ end
84
+
85
+ #
86
+ # @return [Array<String>]
87
+ #
88
+ def supported_data_types
89
+ []
90
+ end
49
91
  end
50
92
  end
51
93
  end