mihari 7.1.3 → 7.3.0

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