mihari 5.7.2 → 6.1.0

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