mihari 5.7.2 → 6.1.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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -0
  3. data/config.ru +2 -0
  4. data/lib/mihari/actor.rb +1 -1
  5. data/lib/mihari/analyzers/base.rb +3 -0
  6. data/lib/mihari/analyzers/dnstwister.rb +2 -4
  7. data/lib/mihari/analyzers/hunterhow.rb +1 -1
  8. data/lib/mihari/analyzers/urlscan.rb +1 -4
  9. data/lib/mihari/cli/main.rb +2 -12
  10. data/lib/mihari/commands/database.rb +0 -1
  11. data/lib/mihari/config.rb +5 -1
  12. data/lib/mihari/database.rb +9 -5
  13. data/lib/mihari/emitters/misp.rb +2 -2
  14. data/lib/mihari/emitters/slack.rb +8 -11
  15. data/lib/mihari/emitters/the_hive.rb +5 -9
  16. data/lib/mihari/enrichers/base.rb +2 -0
  17. data/lib/mihari/enrichers/google_public_dns.rb +2 -7
  18. data/lib/mihari/enrichers/ipinfo.rb +2 -3
  19. data/lib/mihari/enrichers/shodan.rb +2 -3
  20. data/lib/mihari/enrichers/whois.rb +11 -20
  21. data/lib/mihari/entities/artifact.rb +1 -0
  22. data/lib/mihari/mixins/falsepositive.rb +2 -2
  23. data/lib/mihari/mixins/refang.rb +1 -4
  24. data/lib/mihari/mixins/unwrap_error.rb +27 -0
  25. data/lib/mihari/models/alert.rb +1 -3
  26. data/lib/mihari/models/artifact.rb +18 -12
  27. data/lib/mihari/models/rule.rb +1 -2
  28. data/lib/mihari/rule.rb +14 -10
  29. data/lib/mihari/service.rb +2 -0
  30. data/lib/mihari/services/rule_builder.rb +2 -4
  31. data/lib/mihari/structs/fofa.rb +2 -0
  32. data/lib/mihari/version.rb +1 -1
  33. data/lib/mihari/web/app.rb +5 -3
  34. data/lib/mihari/web/endpoints/alerts.rb +14 -18
  35. data/lib/mihari/web/endpoints/artifacts.rb +17 -22
  36. data/lib/mihari/web/endpoints/configs.rb +0 -1
  37. data/lib/mihari/web/endpoints/ip_addresses.rb +1 -1
  38. data/lib/mihari/web/endpoints/rules.rb +27 -32
  39. data/lib/mihari/web/endpoints/tags.rb +7 -9
  40. data/lib/mihari/web/middleware/connection_adapter.rb +3 -5
  41. data/lib/mihari/web/middleware/error_notification_adapter.rb +10 -6
  42. data/lib/mihari/web/public/assets/{index-ec641cb0.js → index-216d49d1.js} +42 -42
  43. data/lib/mihari/web/public/assets/{index-56fc2187.css → index-4c8509ee.css} +1 -1
  44. data/lib/mihari/web/public/index.html +2 -2
  45. data/lib/mihari/web/public/redoc-static.html +29 -49
  46. data/lib/mihari.rb +9 -10
  47. data/mihari.gemspec +11 -13
  48. data/mkdocs.yml +1 -0
  49. data/requirements.txt +1 -1
  50. metadata +76 -34
  51. data/lib/mihari/services/rule_runner.rb +0 -19
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5c7fa4de39ec815c08930311d4ed642857236021301508c47d298cba157dd811
4
- data.tar.gz: 1598a9992be6739957bc5145c11d93f8a869588d42985799d527703178f78a8f
3
+ metadata.gz: dc2d39ad14f53cf88ed2c68bb2e2668e9a43f36444099401ef9fbe1347948644
4
+ data.tar.gz: 2d08fed6ec6f82da36f72a2fb05ae1ede82ba65d6bc9bcdb7c60b8f7e244c71a
5
5
  SHA512:
6
- metadata.gz: 6c96715d51778d724b4df1c502d3083c4af084997019cc7ef43fc5d131cfeb02c4b0f06d6353c0d8e75e26dec0fb20038fa36718f9a65bb1a3a771224442bc3e
7
- data.tar.gz: 655c0efa77ff2602fef509b351ebf02ab9c222728ddfcfc0da36ba8fa27a4e1518f1d70406b5ba3ace86012c196954a98c9af10a4de58c5dcdf73bbc760d2056
6
+ metadata.gz: 684c4106554f9c11bc7ff7f8633b32c5fa24b4127001415e6d14bd4de73e51a04084eb802a29cac109d8b56c71bc7068c8657f4d6ec7cd53e0519ef353aa859f
7
+ data.tar.gz: 4a4c3183ca85d47b54f7a98837059a9fae585c2d079fe005336520c62290bc317aa61880695f923a2ad0ad7ba5cf18e3e888990a9394d82b4ac41939f34b3403
data/.rubocop.yml CHANGED
@@ -13,6 +13,10 @@ Metrics/MethodLength:
13
13
  Max: 50
14
14
  Metrics/AbcSize:
15
15
  Max: 50
16
+ RSpec/MultipleMemoizedHelpers:
17
+ Max: 10
18
+ RSpec/ExampleLength:
19
+ Max: 20
16
20
  require:
17
21
  - rubocop-rspec
18
22
  - rubocop-yard
data/config.ru CHANGED
@@ -1,5 +1,7 @@
1
1
  require "./lib/mihari"
2
2
 
3
+ require "better_errors"
4
+
3
5
  # set rack env as development
4
6
  ENV["RACK_ENV"] ||= "development"
5
7
 
data/lib/mihari/actor.rb CHANGED
@@ -78,7 +78,7 @@ module Mihari
78
78
  # @return [String]
79
79
  #
80
80
  def class_key
81
- to_s.split("::").last
81
+ to_s.split("::").last.downcase
82
82
  end
83
83
 
84
84
  #
@@ -60,7 +60,10 @@ module Mihari
60
60
  # No need to set data_type manually
61
61
  # It is set automatically in #initialize
62
62
  artifact = artifact.is_a?(Models::Artifact) ? artifact : Models::Artifact.new(data: artifact)
63
+
63
64
  artifact.source = self.class.class_key
65
+ artifact.query = query
66
+
64
67
  artifact
65
68
  end.select(&:valid?).uniq(&:data)
66
69
  end
@@ -25,9 +25,7 @@ module Mihari
25
25
  raise ValueError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
26
26
 
27
27
  domains = client.fuzz(query)
28
- Parallel.map(domains) do |domain|
29
- resolvable?(domain) ? domain : nil
30
- end.compact
28
+ Parallel.map(domains) { |domain| resolvable?(domain) ? domain : nil }.compact
31
29
  end
32
30
 
33
31
  private
@@ -55,7 +53,7 @@ module Mihari
55
53
  def resolvable?(domain)
56
54
  Resolv.getaddress domain
57
55
  true
58
- rescue Resolv::ResolvError => _e
56
+ rescue Resolv::ResolvError
59
57
  false
60
58
  end
61
59
  end
@@ -22,7 +22,7 @@ module Mihari
22
22
  # @param [Hash, nil] options
23
23
  # @param [String, nil] api_key
24
24
  #
25
- def initialize(query, start_time:, end_time:, options: nil, api_key: nil)
25
+ def initialize(query, start_time: nil, end_time: nil, options: nil, api_key: nil)
26
26
  super(query, options: options)
27
27
 
28
28
  @api_key = api_key || Mihari.config.hunterhow_api_key
@@ -34,10 +34,7 @@ module Mihari
34
34
  def artifacts
35
35
  # @type [Array<Mihari::Models::Artifact>]
36
36
  artifacts = client.search_with_pagination(query, pagination_limit: pagination_limit).map(&:artifacts).flatten
37
-
38
- artifacts.select do |artifact|
39
- allowed_data_types.include? artifact.data_type
40
- end
37
+ artifacts.select { |artifact| allowed_data_types.include? artifact.data_type }
41
38
  end
42
39
 
43
40
  def configuration_keys
@@ -32,19 +32,9 @@ module Mihari
32
32
  include Mihari::Commands::Version
33
33
  include Mihari::Commands::Web
34
34
 
35
- no_commands do
36
- def unwrap_error(err)
37
- return err unless err.is_a?(Dry::Monads::UnwrapError)
38
-
39
- # NOTE: UnwrapError's receiver can be either of:
40
- # - Dry::Monads::Try::Error
41
- # - Dry::Monads::Result::Failure
42
- receiver = err.receiver
43
- return receiver.exception if receiver.is_a?(Dry::Monads::Try::Error)
44
-
45
- receiver.failure
46
- end
35
+ include Mihari::Mixins::UnwrapError
47
36
 
37
+ no_commands do
48
38
  def safe_execute
49
39
  yield
50
40
  rescue StandardError => e
@@ -19,7 +19,6 @@ module Mihari
19
19
  #
20
20
  def migrate(direction = "up")
21
21
  ActiveRecord::Migration.verbose = options["verbose"]
22
-
23
22
  Mihari::Database.migrate direction.to_sym
24
23
  end
25
24
  end
data/lib/mihari/config.rb CHANGED
@@ -45,7 +45,8 @@ module Mihari
45
45
  retry_exponential_backoff: true,
46
46
  retry_interval: 5,
47
47
  retry_times: 3,
48
- sentry_dsn: nil
48
+ sentry_dsn: nil,
49
+ sentry_trace_sample_rate: 0.25
49
50
  )
50
51
 
51
52
  # @!attribute [r] binaryedge_api_key
@@ -132,6 +133,9 @@ module Mihari
132
133
  # @!attribute [r] sentry_dsn
133
134
  # @return [String, nil]
134
135
 
136
+ # @!attribute [r] sentry_trace_sample_rate
137
+ # @return [Float]
138
+
135
139
  # @!attribute [r] retry_interval
136
140
  # @return [Integer]
137
141
 
@@ -1,9 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Make possible to use upper case acronyms in class names
4
- ActiveSupport::Inflector.inflections(:en) do |inflect|
5
- inflect.acronym "CPE"
6
- end
4
+ ActiveSupport::Inflector.inflections(:en) { |inflect| inflect.acronym "CPE" }
7
5
 
8
6
  def env
9
7
  ENV["APP_ENV"] || ENV["RACK_ENV"]
@@ -113,6 +111,12 @@ class V5Schema < ActiveRecord::Migration[7.1]
113
111
  end
114
112
  end
115
113
 
114
+ class V61Schema < ActiveRecord::Migration[7.1]
115
+ def change
116
+ add_column :artifacts, :query, :string
117
+ end
118
+ end
119
+
116
120
  def adapter
117
121
  return "postgresql" if %w[postgresql postgres].include?(Mihari.config.database_url.scheme)
118
122
  return "mysql2" if Mihari.config.database_url.scheme == "mysql2"
@@ -124,7 +128,7 @@ end
124
128
  # @return [Array<ActiveRecord::Migration>] schemas
125
129
  #
126
130
  def schemas
127
- [V5Schema]
131
+ [V5Schema, V61Schema]
128
132
  end
129
133
 
130
134
  module Mihari
@@ -175,7 +179,7 @@ module Mihari
175
179
  Mihari::Database.connect
176
180
  yield
177
181
  rescue ActiveRecord::StatementInvalid
178
- Mihari.logger.error("You haven't finished the DB migration! Please run 'mihari db migrate'.")
182
+ Mihari.logger.error("The DB migration is not yet complete. Please run 'mihari db migrate'.")
179
183
  ensure
180
184
  Mihari::Database.close
181
185
  end
@@ -56,12 +56,12 @@ module Mihari
56
56
  })
57
57
  end
58
58
 
59
- private
60
-
61
59
  def configuration_keys
62
60
  %w[misp_url misp_api_key]
63
61
  end
64
62
 
63
+ private
64
+
65
65
  def client
66
66
  @client ||= Clients::MISP.new(url, api_key: api_key, timeout: timeout)
67
67
  end
@@ -6,7 +6,7 @@ require "slack-notifier"
6
6
  module Mihari
7
7
  module Emitters
8
8
  class Attachment
9
- include Memist::Memoizable
9
+ prepend MemoWise
10
10
 
11
11
  # @return [String]
12
12
  attr_reader :data
@@ -76,7 +76,7 @@ module Mihari
76
76
  "https://urlscan.io/domain/#{uri.hostname}"
77
77
  end
78
78
  end
79
- memoize :_urlscan_link
79
+ memo_wise :_urlscan_link
80
80
 
81
81
  # @return [String, nil]
82
82
  def _vt_link
@@ -93,19 +93,19 @@ module Mihari
93
93
  "https://www.virustotal.com/#/search/#{data}"
94
94
  end
95
95
  end
96
- memoize :_vt_link
96
+ memo_wise :_vt_link
97
97
 
98
98
  # @return [String, nil]
99
99
  def _censys_link
100
100
  (data_type == "ip") ? "https://search.censys.io/hosts/#{data}" : nil
101
101
  end
102
- memoize :_censys_link
102
+ memo_wise :_censys_link
103
103
 
104
104
  # @return [String, nil]
105
105
  def _shodan_link
106
106
  (data_type == "ip") ? "https://www.shodan.io/host/#{data}" : nil
107
107
  end
108
- memoize :_shodan_link
108
+ memo_wise :_shodan_link
109
109
 
110
110
  # @return [String]
111
111
  def sha256
@@ -192,9 +192,7 @@ module Mihari
192
192
  # @return [Array<Mihari::Emitters::Attachment>]
193
193
  #
194
194
  def attachments
195
- artifacts.map do |artifact|
196
- Attachment.new(data: artifact.data, data_type: artifact.data_type).to_a
197
- end.flatten
195
+ artifacts.map { |artifact| Attachment.new(data: artifact.data, data_type: artifact.data_type).to_a }.flatten
198
196
  end
199
197
 
200
198
  #
@@ -205,7 +203,6 @@ module Mihari
205
203
  def text
206
204
  tags = rule.tags
207
205
  tags = ["N/A"] if tags.empty?
208
-
209
206
  [
210
207
  "*#{rule.title}*",
211
208
  "*Desc.*: #{rule.description}",
@@ -217,10 +214,10 @@ module Mihari
217
214
  # @param [Array<Mihari::Models::Artifact>] artifacts
218
215
  #
219
216
  def call(artifacts)
220
- return if artifacts.empty?
221
-
222
217
  @artifacts = artifacts
223
218
 
219
+ return if artifacts.empty?
220
+
224
221
  notifier.post(text: text, attachments: attachments, mrkdwn: true)
225
222
  end
226
223
 
@@ -43,10 +43,10 @@ module Mihari
43
43
  # @param [Array<Mihari::Models::Artifact>] artifacts
44
44
  #
45
45
  def call(artifacts)
46
- return if artifacts.empty?
47
-
48
46
  @artifacts = artifacts
49
47
 
48
+ return if artifacts.empty?
49
+
50
50
  client.alert payload
51
51
  end
52
52
 
@@ -61,21 +61,17 @@ module Mihari
61
61
  @normalized_api_version ||= [].tap do |out|
62
62
  # v4 does not have version prefix in path (/api/)
63
63
  # v5 has version prefix in path (/api/v1/)
64
- table = {
65
- "" => nil,
66
- "v4" => nil,
67
- "v5" => "v1"
68
- }
64
+ table = { "" => nil, "v4" => nil, "v5" => "v1" }
69
65
  out << table[api_version.to_s.downcase]
70
66
  end.first
71
67
  end
72
68
 
73
- private
74
-
75
69
  def configuration_keys
76
70
  %w[thehive_url thehive_api_key]
77
71
  end
78
72
 
73
+ private
74
+
79
75
  def client
80
76
  @client ||= Clients::TheHive.new(url, api_key: api_key, api_version: normalized_api_version, timeout: timeout)
81
77
  end
@@ -6,6 +6,8 @@ module Mihari
6
6
  # Base class for enrichers
7
7
  #
8
8
  class Base < Actor
9
+ prepend MemoWise
10
+
9
11
  def initialize(options: nil)
10
12
  super(options: options)
11
13
  end
@@ -14,9 +14,7 @@ module Mihari
14
14
  # @return [Array<Mihari::Structs::GooglePublicDNS::Response>]
15
15
  #
16
16
  def call(name)
17
- %w[A AAAA CNAME TXT NS].filter_map do |resource_type|
18
- query_by_type(name, resource_type)
19
- end
17
+ %w[A AAAA CNAME TXT NS].filter_map { |resource_type| query_by_type(name, resource_type) }
20
18
  end
21
19
 
22
20
  #
@@ -31,10 +29,7 @@ module Mihari
31
29
  url = "https://dns.google/resolve"
32
30
  params = { name: name, type: resource_type }
33
31
  res = http.get(url, params: params)
34
-
35
- data = JSON.parse(res.body.to_s)
36
-
37
- Structs::GooglePublicDNS::Response.from_dynamic! data
32
+ Structs::GooglePublicDNS::Response.from_dynamic! JSON.parse(res.body.to_s)
38
33
  rescue HTTPError
39
34
  nil
40
35
  end
@@ -33,10 +33,9 @@ module Mihari
33
33
  def call(ip)
34
34
  url = "https://ipinfo.io/#{ip}/json"
35
35
  res = http.get(url)
36
- data = JSON.parse(res.body.to_s)
37
-
38
- Structs::IPInfo::Response.from_dynamic! data
36
+ Structs::IPInfo::Response.from_dynamic! JSON.parse(res.body.to_s)
39
37
  end
38
+ memo_wise :call
40
39
 
41
40
  private
42
41
 
@@ -16,10 +16,9 @@ module Mihari
16
16
  def call(ip)
17
17
  url = "https://internetdb.shodan.io/#{ip}"
18
18
  res = http.get(url)
19
- data = JSON.parse(res.body.to_s)
20
-
21
- Structs::Shodan::InternetDBResponse.from_dynamic! data
19
+ Structs::Shodan::InternetDBResponse.from_dynamic! JSON.parse(res.body.to_s)
22
20
  end
21
+ memo_wise :call
23
22
 
24
23
  private
25
24
 
@@ -8,16 +8,11 @@ module Mihari
8
8
  # Whois enricher
9
9
  #
10
10
  class Whois < Base
11
- # @return [Hash]
12
- attr_accessor :memo
13
-
14
11
  #
15
12
  # @param [Hash, nil] options
16
13
  #
17
14
  def initialize(options: nil)
18
15
  super(options: options)
19
-
20
- @memo = {}
21
16
  end
22
17
 
23
18
  #
@@ -28,16 +23,22 @@ module Mihari
28
23
  # @return [Mihari::Models::WhoisRecord, nil]
29
24
  #
30
25
  def call(domain)
31
- domain = PublicSuffix.domain(domain)
26
+ _call PublicSuffix.domain(domain)
27
+ end
32
28
 
33
- # check memo
34
- return memo[domain].dup if memo.key?(domain)
29
+ private
35
30
 
31
+ #
32
+ # @param [String] domain
33
+ #
34
+ # @return [Mihari::Models::WhoisRecord, nil]
35
+ #
36
+ def _call(domain)
36
37
  record = whois.lookup(domain)
37
38
  parser = record.parser
38
39
  return nil if parser.available?
39
40
 
40
- whois_record = Models::WhoisRecord.new(
41
+ Models::WhoisRecord.new(
41
42
  domain: domain,
42
43
  created_on: get_created_on(parser),
43
44
  updated_on: get_updated_on(parser),
@@ -45,18 +46,8 @@ module Mihari
45
46
  registrar: get_registrar(parser),
46
47
  contacts: get_contacts(parser)
47
48
  )
48
-
49
- # set memo
50
- memo[domain] = whois_record
51
-
52
- whois_record
53
49
  end
54
-
55
- def reset_cache
56
- @memo = {}
57
- end
58
-
59
- private
50
+ memo_wise :_call
60
51
 
61
52
  #
62
53
  # @return [::Whois::Client]
@@ -11,6 +11,7 @@ module Mihari
11
11
  expose :data, documentation: { type: String, required: true }
12
12
  expose :data_type, documentation: { type: String, required: true }, as: :dataType
13
13
  expose :source, documentation: { type: String, required: true }
14
+ expose :query, documentation: { type: String, required: false }
14
15
  expose :tags, documentation: { type: String, is_array: true }
15
16
  end
16
17
 
@@ -6,7 +6,7 @@ module Mihari
6
6
  # False positive mixins
7
7
  #
8
8
  module FalsePositive
9
- include Memist::Memoizable
9
+ prepend MemoWise
10
10
 
11
11
  #
12
12
  # Normalize a falsepositive value
@@ -22,7 +22,7 @@ module Mihari
22
22
  value_without_slashes = value[1..-2]
23
23
  Regexp.compile value_without_slashes.to_s
24
24
  end
25
- memoize :normalize_falsepositive
25
+ memo_wise :normalize_falsepositive
26
26
 
27
27
  #
28
28
  # Check whether a value is valid format as a disallowed data value
@@ -14,10 +14,7 @@ module Mihari
14
14
  # @return [String]
15
15
  #
16
16
  def refang(indicator)
17
- return indicator.gsub("[.]", ".").gsub("(.)", ".") if indicator.is_a?(String)
18
-
19
- # for RSpec & Ruby 2.7
20
- indicator
17
+ indicator.gsub("[.]", ".").gsub("(.)", ".")
21
18
  end
22
19
  end
23
20
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mihari
4
+ module Mixins
5
+ #
6
+ # Unwrap error mixins
7
+ #
8
+ module UnwrapError
9
+ def unwrap_error(err)
10
+ return err unless err.is_a?(Dry::Monads::UnwrapError)
11
+
12
+ # NOTE: UnwrapError's receiver can be either of:
13
+ # - Dry::Monads::Try::Error
14
+ # - Dry::Monads::Result::Failure
15
+ receiver = err.receiver
16
+ case receiver
17
+ when Dry::Monads::Try::Error
18
+ receiver.exception
19
+ when Dry::Monads::Failure
20
+ receiver.failure
21
+ else
22
+ err
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -30,7 +30,6 @@ module Mihari
30
30
  offset = (page - 1) * limit
31
31
 
32
32
  relation = build_relation(filter.without_pagination)
33
-
34
33
  alert_ids = relation.limit(limit).offset(offset).order(id: :desc).pluck(:id).uniq
35
34
  eager_load(:artifacts, :tags).where(id: [alert_ids]).order(id: :desc)
36
35
  end
@@ -75,8 +74,7 @@ module Mihari
75
74
  def build_relation(filter)
76
75
  artifact_ids = get_artifact_ids_by_filter(filter)
77
76
 
78
- relation = self
79
- relation = relation.includes(:artifacts, :tags)
77
+ relation = includes(:artifacts, :tags)
80
78
 
81
79
  relation = relation.where(artifacts: { id: artifact_ids }) unless artifact_ids.empty?
82
80
  relation = relation.where(tags: { name: filter.tag_name }) if filter.tag_name
@@ -78,7 +78,7 @@ module Mihari
78
78
  end
79
79
 
80
80
  #
81
- # Enrich(add) whois record
81
+ # Enrich whois record
82
82
  #
83
83
  # @param [Mihari::Enrichers::Whois] enricher
84
84
  #
@@ -89,7 +89,7 @@ module Mihari
89
89
  end
90
90
 
91
91
  #
92
- # Enrich(add) DNS records
92
+ # Enrich DNS records
93
93
  #
94
94
  # @param [Mihari::Enrichers::GooglePublicDNS] enricher
95
95
  #
@@ -100,7 +100,7 @@ module Mihari
100
100
  end
101
101
 
102
102
  #
103
- # Enrich(add) reverse DNS names
103
+ # Enrich reverse DNS names
104
104
  #
105
105
  # @param [Mihari::Enrichers::Shodan] enricher
106
106
  #
@@ -111,7 +111,7 @@ module Mihari
111
111
  end
112
112
 
113
113
  #
114
- # Enrich(add) geolocation
114
+ # Enrich geolocation
115
115
  #
116
116
  # @param [Mihari::Enrichers::IPInfo] enricher
117
117
  #
@@ -158,13 +158,13 @@ module Mihari
158
158
  # Enrich all the enrichable relationships of the artifact
159
159
  #
160
160
  def enrich_all
161
- enrich_autonomous_system
161
+ enrich_autonomous_system ipinfo
162
162
  enrich_dns
163
- enrich_geolocation
164
- enrich_reverse_dns
163
+ enrich_geolocation ipinfo
164
+ enrich_reverse_dns shodan
165
165
  enrich_whois
166
- enrich_ports
167
- enrich_cpes
166
+ enrich_ports shodan
167
+ enrich_cpes shodan
168
168
  end
169
169
 
170
170
  ENRICH_METHODS_BY_ENRICHER = {
@@ -192,13 +192,19 @@ module Mihari
192
192
  #
193
193
  def enrich_by_enricher(enricher)
194
194
  methods = ENRICH_METHODS_BY_ENRICHER[enricher.class] || []
195
- methods.each do |method|
196
- send(method, enricher) if respond_to?(method)
197
- end
195
+ methods.each { |method| send(method, enricher) if respond_to?(method) }
198
196
  end
199
197
 
200
198
  private
201
199
 
200
+ def ipinfo
201
+ @ipinfo ||= Enrichers::IPInfo.new
202
+ end
203
+
204
+ def shodan
205
+ @shodan ||= Enrichers::Shodan.new
206
+ end
207
+
202
208
  def normalize_as_domain(url_or_domain)
203
209
  return url_or_domain if data_type == "domain"
204
210
 
@@ -66,8 +66,7 @@ module Mihari
66
66
  # @return [Mihari::Models::Rule]
67
67
  #
68
68
  def build_relation(filter)
69
- relation = self
70
- relation = relation.includes(alerts: :tags)
69
+ relation = includes(alerts: :tags)
71
70
 
72
71
  relation = relation.where(alerts: { tags: { name: filter.tag_name } }) if filter.tag_name
73
72
 
data/lib/mihari/rule.rb CHANGED
@@ -113,15 +113,15 @@ module Mihari
113
113
  analyzers.flat_map do |analyzer|
114
114
  # @type [Dry::Monads::Result::Success<Array<Mihari::Models::Artifact>>, Dry::Monads::Result::Failure]
115
115
  result = analyzer.result
116
-
117
- if result.failure?
118
- raise result.failure unless analyzer.ignore_error?
119
- else
116
+ case result
117
+ when Success
120
118
  artifacts = result.value!
121
119
  artifacts.map do |artifact|
122
120
  artifact.rule_id = id
123
121
  artifact
124
122
  end
123
+ else
124
+ raise result.failure unless analyzer.ignore_error?
125
125
  end
126
126
  end.compact
127
127
  end
@@ -177,8 +177,14 @@ module Mihari
177
177
  results = Parallel.map(emitters) { |emitter| emitter.result enriched_artifacts }
178
178
  results.zip(emitters).map do |result_and_emitter|
179
179
  result, emitter = result_and_emitter
180
- Mihari.logger.info "Emission by #{emitter.class} is failed: #{result.failure}" if result.failure?
181
- Mihari.logger.info "Emission by #{emitter.class} is succeeded" if result.success?
180
+
181
+ case result
182
+ when Success
183
+ Mihari.logger.info "Emission by #{emitter.class} succeed"
184
+ else
185
+ Mihari.logger.info "Emission by #{emitter.class} failed: #{result.failure}"
186
+ end
187
+
182
188
  result.value_or nil
183
189
  end.compact
184
190
  end
@@ -289,8 +295,7 @@ module Mihari
289
295
  @analyzers ||= queries.map do |query_params|
290
296
  analyzer_name = query_params[:analyzer]
291
297
  klass = get_analyzer_class(analyzer_name)
292
- klass.from_query(query_params)
293
- end.map do |analyzer|
298
+ analyzer = klass.from_query(query_params)
294
299
  analyzer.validate_configuration!
295
300
  analyzer
296
301
  end
@@ -320,8 +325,7 @@ module Mihari
320
325
  %i[emitter options].each { |key| params.delete key }
321
326
 
322
327
  klass = get_emitter_class(name)
323
- klass.new(rule: self, options: options, **params)
324
- end.map do |emitter|
328
+ emitter = klass.new(rule: self, options: options, **params)
325
329
  emitter.validate_configuration!
326
330
  emitter
327
331
  end