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
@@ -6,6 +6,27 @@ module Mihari
6
6
  # Rule model
7
7
  #
8
8
  class Rule < ActiveRecord::Base
9
+ # @!attribute [rw] id
10
+ # @return [String]
11
+
12
+ # @!attribute [rw] title
13
+ # @return [String]
14
+
15
+ # @!attribute [rw] description
16
+ # @return [String]
17
+
18
+ # @!attribute [rw] data
19
+ # @return [Hash]
20
+
21
+ # @!attribute [rw] created_at
22
+ # @return [DateTime]
23
+
24
+ # @!attribute [rw] updated_at
25
+ # @return [DateTime]
26
+
27
+ # @!attribute [r] alerts
28
+ # @return [Array<Mihari::Models::Alert>]
29
+
9
30
  has_many :alerts, dependent: :destroy
10
31
  has_many :taggings, dependent: :destroy
11
32
  has_many :tags, through: :taggings
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mihari
4
+ module Models
5
+ #
6
+ # Vulnerability model
7
+ #
8
+ class Vulnerability < ActiveRecord::Base
9
+ belongs_to :artifact
10
+ end
11
+ end
12
+ end
data/lib/mihari/rule.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Mihari
4
4
  class Rule < Service
5
+ include Concerns::FalsePositiveNormalizable
5
6
  include Concerns::FalsePositiveValidatable
6
7
 
7
8
  # @return [Hash]
@@ -136,8 +137,7 @@ module Mihari
136
137
  analyzer_results.flat_map do |result|
137
138
  artifacts = result.value!
138
139
  artifacts.map do |artifact|
139
- artifact.rule_id = id
140
- artifact
140
+ artifact.tap { |tapped| tapped.rule_id = id }
141
141
  end
142
142
  end
143
143
  end
@@ -174,8 +174,10 @@ module Mihari
174
174
  # @return [Array<Mihari::Models::Artifact>]
175
175
  #
176
176
  def enriched_artifacts
177
- @enriched_artifacts ||= Parallel.map(unique_artifacts) do |artifact|
178
- enrichers.each { |enricher| artifact.enrich_by_enricher enricher }
177
+ @enriched_artifacts ||= unique_artifacts.map do |artifact|
178
+ serial_enrichers.each { |enricher| enricher.result(artifact) }
179
+ Parallel.each(parallel_enrichers) { |enricher| enricher.result(artifact) }
180
+
179
181
  artifact
180
182
  end
181
183
  end
@@ -188,9 +190,10 @@ module Mihari
188
190
  def bulk_emit
189
191
  return [] if enriched_artifacts.empty?
190
192
 
191
- Parallel.map(emitters) do |emitter|
192
- emitter.result(enriched_artifacts).value_or nil
193
- end.compact
193
+ [].tap do |out|
194
+ out << serial_emitters.map { |emitter| emitter.result(enriched_artifacts).value_or(nil) }
195
+ out << Parallel.map(parallel_emitters) { |emitter| emitter.result(enriched_artifacts).value_or(nil) }
196
+ end.flatten.compact
194
197
  end
195
198
 
196
199
  #
@@ -291,11 +294,11 @@ module Mihari
291
294
  #
292
295
  # @return [Boolean]
293
296
  #
294
- def falsepositive?(value)
295
- return true if falsepositives.include?(value)
297
+ def falsepositive?(artifact)
298
+ return true if falsepositives.include?(artifact)
296
299
 
297
300
  regexps = falsepositives.select { |fp| fp.is_a?(Regexp) }
298
- regexps.any? { |fp| fp.match?(value) }
301
+ regexps.any? { |fp| fp.match?(artifact) }
299
302
  end
300
303
 
301
304
  #
@@ -315,12 +318,12 @@ module Mihari
315
318
  # @return [Array<Mihari::Analyzers::Base>]
316
319
  #
317
320
  def analyzers
318
- @analyzers ||= queries.map do |params|
319
- analyzer_name = params[:analyzer]
320
- klass = get_analyzer_class(analyzer_name)
321
- analyzer = klass.from_query(params)
322
- analyzer.validate_configuration!
323
- analyzer
321
+ @analyzers ||= queries.deep_dup.map do |params|
322
+ name = params.delete(:analyzer)
323
+ klass = get_analyzer_class(name)
324
+ klass.from_params(params).tap do |analyzer|
325
+ analyzer.validate_configuration!
326
+ end
324
327
  end
325
328
  end
326
329
 
@@ -356,19 +359,25 @@ module Mihari
356
359
  # @return [Array<Mihari::Emitters::Base>]
357
360
  #
358
361
  def emitters
359
- @emitters ||= data[:emitters].map(&:deep_dup).map do |params|
360
- name = params[:emitter]
361
- options = params[:options]
362
-
363
- %i[emitter options].each { |key| params.delete key }
362
+ @emitters ||= data[:emitters].deep_dup.map do |params|
363
+ name = params.delete(:emitter)
364
+ options = params.delete(:options)
364
365
 
365
366
  klass = get_emitter_class(name)
366
- emitter = klass.new(rule: self, options: options, **params)
367
- emitter.validate_configuration!
368
- emitter
367
+ klass.new(rule: self, options: options, **params).tap do |emitter|
368
+ emitter.validate_configuration!
369
+ end
369
370
  end
370
371
  end
371
372
 
373
+ def parallel_emitters
374
+ emitters.select(&:parallel?)
375
+ end
376
+
377
+ def serial_emitters
378
+ emitters.reject(&:parallel?)
379
+ end
380
+
372
381
  #
373
382
  # Get enricher class
374
383
  #
@@ -386,17 +395,23 @@ module Mihari
386
395
  # @return [Array<Mihari::Enrichers::Base>] enrichers
387
396
  #
388
397
  def enrichers
389
- @enrichers ||= data[:enrichers].map(&:deep_dup).map do |params|
390
- name = params[:enricher]
391
- options = params[:options]
392
-
393
- %i[enricher options].each { |key| params.delete key }
398
+ @enrichers ||= data[:enrichers].deep_dup.map do |params|
399
+ name = params.delete(:enricher)
400
+ options = params.delete(:options)
394
401
 
395
402
  klass = get_enricher_class(name)
396
403
  klass.new(options: options, **params)
397
404
  end
398
405
  end
399
406
 
407
+ def parallel_enrichers
408
+ enrichers.select(&:parallel?)
409
+ end
410
+
411
+ def serial_enrichers
412
+ enrichers.reject(&:parallel?)
413
+ end
414
+
400
415
  #
401
416
  # Validate the data format
402
417
  #
@@ -3,9 +3,9 @@
3
3
  module Mihari
4
4
  module Schemas
5
5
  Alert = Dry::Schema.Params do
6
- required(:rule_id).value(:string)
7
- required(:artifacts).value(array[:string])
8
- optional(:source).value(:string)
6
+ required(:rule_id).filled(:string)
7
+ required(:artifacts).array { filled(:string) }
8
+ optional(:source).filled(:string)
9
9
  end
10
10
 
11
11
  #
@@ -20,8 +20,8 @@ module Mihari
20
20
  key = keys.first
21
21
  const_set(key.upcase, Dry::Schema.Params do
22
22
  required(:analyzer).value(Types::String.enum(*keys))
23
- required(:query).value(:string)
24
- optional(:api_key).value(:string)
23
+ required(:query).filled(:string)
24
+ optional(:api_key).filled(:string)
25
25
  optional(:options).hash(AnalyzerPaginationOptions)
26
26
  end)
27
27
  end
@@ -36,60 +36,60 @@ module Mihari
36
36
  key = keys.first
37
37
  const_set(key.upcase, Dry::Schema.Params do
38
38
  required(:analyzer).value(Types::String.enum(*keys))
39
- required(:query).value(:string)
40
- optional(:api_key).value(:string)
39
+ required(:query).filled(:string)
40
+ optional(:api_key).filled(:string)
41
41
  optional(:options).hash(AnalyzerOptions)
42
42
  end)
43
43
  end
44
44
 
45
45
  DNSTwister = Dry::Schema.Params do
46
46
  required(:analyzer).value(Types::String.enum(*Mihari::Analyzers::DNSTwister.keys))
47
- required(:query).value(:string)
47
+ required(:query).filled(:string)
48
48
  optional(:options).hash(AnalyzerOptions)
49
49
  end
50
50
 
51
51
  Censys = Dry::Schema.Params do
52
52
  required(:analyzer).value(Types::String.enum(*Mihari::Analyzers::Censys.keys))
53
- required(:query).value(:string)
54
- optional(:id).value(:string)
55
- optional(:secret).value(:string)
53
+ required(:query).filled(:string)
54
+ optional(:id).filled(:string)
55
+ optional(:secret).filled(:string)
56
56
  optional(:options).hash(AnalyzerPaginationOptions)
57
57
  end
58
58
 
59
59
  CIRCL = Dry::Schema.Params do
60
60
  required(:analyzer).value(Types::String.enum(*Mihari::Analyzers::CIRCL.keys))
61
- required(:query).value(:string)
62
- optional(:username).value(:string)
63
- optional(:password).value(:string)
61
+ required(:query).filled(:string)
62
+ optional(:username).filled(:string)
63
+ optional(:password).filled(:string)
64
64
  optional(:options).hash(AnalyzerOptions)
65
65
  end
66
66
 
67
67
  Fofa = Dry::Schema.Params do
68
68
  required(:analyzer).value(Types::String.enum(*Mihari::Analyzers::Fofa.keys))
69
- required(:query).value(:string)
70
- optional(:api_key).value(:string)
71
- optional(:email).value(:string)
69
+ required(:query).filled(:string)
70
+ optional(:api_key).filled(:string)
71
+ optional(:email).filled(:string)
72
72
  optional(:options).hash(AnalyzerPaginationOptions)
73
73
  end
74
74
 
75
75
  PassiveTotal = Dry::Schema.Params do
76
76
  required(:analyzer).value(Types::String.enum(*Mihari::Analyzers::PassiveTotal.keys))
77
- required(:query).value(:string)
78
- optional(:username).value(:string)
79
- optional(:api_key).value(:string)
77
+ required(:query).filled(:string)
78
+ optional(:username).filled(:string)
79
+ optional(:api_key).filled(:string)
80
80
  optional(:options).hash(AnalyzerOptions)
81
81
  end
82
82
 
83
83
  ZoomEye = Dry::Schema.Params do
84
84
  required(:analyzer).value(Types::String.enum(*Mihari::Analyzers::ZoomEye.keys))
85
- required(:query).value(:string)
85
+ required(:query).filled(:string)
86
86
  required(:type).value(Types::String.enum("host", "web"))
87
87
  optional(:options).hash(AnalyzerPaginationOptions)
88
88
  end
89
89
 
90
90
  Crtsh = Dry::Schema.Params do
91
91
  required(:analyzer).value(Types::String.enum(*Mihari::Analyzers::Crtsh.keys))
92
- required(:query).value(:string)
92
+ required(:query).filled(:string)
93
93
  optional(:exclude_expired).value(:bool).default(true)
94
94
  optional(:match).value(Types::String.enum("=", "ILIKE", "LIKE", "single", "any", "FTS")).default(nil)
95
95
  optional(:options).hash(AnalyzerOptions)
@@ -97,22 +97,22 @@ module Mihari
97
97
 
98
98
  HunterHow = Dry::Schema.Params do
99
99
  required(:analyzer).value(Types::String.enum(*Mihari::Analyzers::HunterHow.keys))
100
- required(:query).value(:string)
100
+ required(:query).filled(:string)
101
101
  required(:start_time).value(:date)
102
102
  required(:end_time).value(:date)
103
- optional(:api_key).value(:string)
103
+ optional(:api_key).filled(:string)
104
104
  optional(:options).hash(AnalyzerPaginationOptions)
105
105
  end
106
106
 
107
107
  Feed = Dry::Schema.Params do
108
108
  required(:analyzer).value(Types::String.enum(*Mihari::Analyzers::Feed.keys))
109
- required(:query).value(:string)
110
- required(:selector).value(:string)
109
+ required(:query).filled(:string)
110
+ required(:selector).filled(:string)
111
111
  optional(:method).value(Types::HTTPRequestMethods).default("GET")
112
- optional(:headers).value(:hash).default({})
113
- optional(:params).value(:hash)
114
- optional(:form).value(:hash)
115
- optional(:json).value(:hash)
112
+ optional(:headers).filled(:hash)
113
+ optional(:params).filled(:hash)
114
+ optional(:form).filled(:hash)
115
+ optional(:json).filled(:hash)
116
116
  optional(:options).hash(AnalyzerOptions)
117
117
  end
118
118
  end
@@ -15,31 +15,31 @@ module Mihari
15
15
 
16
16
  MISP = Dry::Schema.Params do
17
17
  required(:emitter).value(Types::String.enum(*Mihari::Emitters::MISP.keys))
18
- optional(:url).value(:string)
19
- optional(:api_key).value(:string)
18
+ optional(:url).filled(:string)
19
+ optional(:api_key).filled(:string)
20
20
  optional(:options).hash(Options)
21
21
  end
22
22
 
23
23
  TheHive = Dry::Schema.Params do
24
24
  required(:emitter).value(Types::String.enum(*Mihari::Emitters::TheHive.keys))
25
- optional(:url).value(:string)
26
- optional(:api_key).value(:string)
25
+ optional(:url).filled(:string)
26
+ optional(:api_key).filled(:string)
27
27
  optional(:options).hash(Options)
28
28
  end
29
29
 
30
30
  Slack = Dry::Schema.Params do
31
31
  required(:emitter).value(Types::String.enum(*Mihari::Emitters::Slack.keys))
32
- optional(:webhook_url).value(:string)
33
- optional(:channel).value(:string)
32
+ optional(:webhook_url).filled(:string)
33
+ optional(:channel).filled(:string)
34
34
  optional(:options).hash(Options)
35
35
  end
36
36
 
37
37
  Webhook = Dry::Schema.Params do
38
38
  required(:emitter).value(Types::String.enum(*Mihari::Emitters::Webhook.keys))
39
- required(:url).value(:string)
39
+ required(:url).filled(:string)
40
40
  optional(:method).value(Types::HTTPRequestMethods).default("POST")
41
- optional(:headers).value(:hash).default({})
42
- optional(:template).value(:string)
41
+ optional(:headers).filled(:hash)
42
+ optional(:template).filled(:string)
43
43
  optional(:options).hash(Options)
44
44
  end
45
45
  end
@@ -8,9 +8,9 @@ module Dry
8
8
  # (see https://github.com/dry-rb/dry-schema/issues/70)
9
9
  #
10
10
  class DSL
11
- def default(value)
11
+ def default(artifact)
12
12
  schema_dsl.before(:rule_applier) do |result|
13
- result.update(name => value) if result.output && !result[name]
13
+ result.update(name => artifact) if result.output && !result[name]
14
14
  end
15
15
  end
16
16
  end
@@ -7,17 +7,14 @@ module Mihari
7
7
  optional(:retry_interval).value(:integer).default(Mihari.config.retry_interval)
8
8
  optional(:retry_exponential_backoff).value(:bool).default(Mihari.config.retry_exponential_backoff)
9
9
  optional(:timeout).value(:integer)
10
+ optional(:parallel).value(:bool).default(Mihari.config.parallel)
10
11
  end
11
12
 
12
13
  IgnoreErrorOptions = Dry::Schema.Params do
13
14
  optional(:ignore_error).value(:bool).default(Mihari.config.ignore_error)
14
15
  end
15
16
 
16
- ParallelOptions = Dry::Schema.Params do
17
- optional(:parallel).value(:bool).default(Mihari.config.parallel)
18
- end
19
-
20
- AnalyzerOptions = Options | IgnoreErrorOptions | ParallelOptions
17
+ AnalyzerOptions = Options | IgnoreErrorOptions
21
18
 
22
19
  PaginationOptions = Dry::Schema.Params do
23
20
  optional(:pagination_interval).value(:integer).default(Mihari.config.pagination_interval)
@@ -7,27 +7,27 @@ require "mihari/schemas/enricher"
7
7
  module Mihari
8
8
  module Schemas
9
9
  Rule = Dry::Schema.Params do
10
- required(:id).value(:string)
11
- required(:title).value(:string)
12
- required(:description).value(:string)
10
+ required(:id).filled(:string)
11
+ required(:title).filled(:string)
12
+ required(:description).filled(:string)
13
13
 
14
- optional(:tags).value(array[:string]).default([])
14
+ optional(:author).filled(:string)
15
+ optional(:status).filled(:string)
15
16
 
16
- optional(:author).value(:string)
17
- optional(:references).value(array[:string])
18
- optional(:related).value(array[:string])
19
- optional(:status).value(:string)
17
+ optional(:tags).array { filled(:string) }.default([])
18
+ optional(:references).array { filled(:string) }
19
+ optional(:related).array { filled(:string) }
20
20
 
21
21
  optional(:created_on).value(:date)
22
22
  optional(:updated_on).value(:date)
23
23
 
24
24
  required(:queries).value(:array).each { Analyzer } # rubocop:disable Lint/Void
25
-
26
25
  optional(:emitters).value(:array).each { Emitter }.default(DEFAULT_EMITTERS) # rubocop:disable Lint/Void
27
26
  optional(:enrichers).value(:array).each { Enricher }.default(DEFAULT_ENRICHERS) # rubocop:disable Lint/Void
28
27
 
29
- optional(:data_types).value(array[Types::DataTypes]).default(Mihari::Types::DataTypes.values)
30
- optional(:falsepositives).value(array[:string]).default([])
28
+ optional(:data_types).filled(array[Types::DataTypes]).default(Mihari::Types::DataTypes.values)
29
+
30
+ optional(:falsepositives).array { filled(:string) }.default([])
31
31
 
32
32
  optional(:artifact_ttl).value(:integer)
33
33
  end
@@ -42,7 +42,14 @@ module Mihari
42
42
 
43
43
  rule(:falsepositives) do
44
44
  value.each do |v|
45
- key.failure("#{v} is not a valid format.") unless valid_falsepositive?(v)
45
+ key.failure("#{v} is not a valid format") unless valid_falsepositive?(v)
46
+ end
47
+ end
48
+
49
+ rule(:emitters) do
50
+ emitters = value.filter_map { |v| v[:emitter] }
51
+ unless emitters.include?(Mihari::Emitters::Database.key)
52
+ key.failure("Emitter:#{Mihari::Emitters::Database.key} should be included in emitters")
46
53
  end
47
54
  end
48
55
  end
@@ -20,139 +20,5 @@ module Mihari
20
20
  Rule.from_yaml ERB.new(File.read(path_or_id)).result
21
21
  end
22
22
  end
23
-
24
- #
25
- # Autonomous system builder
26
- #
27
- class AutonomousSystemBuilder < Service
28
- #
29
- # @param [String] ip
30
- # @param [Mihari::Enrichers::MMDB] enricher
31
- #
32
- # @return [Mihari::Models::AutonomousSystem, nil]
33
- #
34
- def call(ip, enricher: Enrichers::MMDB.new)
35
- enricher.result(ip).fmap do |res|
36
- Models::AutonomousSystem.new(asn: res.asn) if res.asn
37
- end.value_or nil
38
- end
39
- end
40
-
41
- #
42
- # CPE builder
43
- #
44
- class CPEBuilder < Service
45
- #
46
- # Build CPEs
47
- #
48
- # @param [String] ip
49
- # @param [Mihari::Enrichers::Shodan] enricher
50
- #
51
- # @return [Array<Mihari::Models::CPE>]
52
- #
53
- def call(ip, enricher: Enrichers::Shodan.new)
54
- enricher.result(ip).fmap do |res|
55
- (res&.cpes || []).map { |cpe| Models::CPE.new(cpe: cpe) }
56
- end.value_or []
57
- end
58
- end
59
-
60
- #
61
- # DNS record builder
62
- #
63
- class DnsRecordBuilder < Service
64
- #
65
- # Build DNS records
66
- #
67
- # @param [String] domain
68
- # @param [Mihari::Enrichers::Shodan] enricher
69
- #
70
- # @return [Array<Mihari::Models::DnsRecord>]
71
- #
72
- def call(domain, enricher: Enrichers::GooglePublicDNS.new)
73
- enricher.result(domain).fmap do |res|
74
- res.answers.map { |answer| Models::DnsRecord.new(resource: answer.resource_type, value: answer.data) }
75
- end.value_or []
76
- end
77
- end
78
-
79
- #
80
- # Geolocation builder
81
- #
82
- class GeolocationBuilder < Service
83
- #
84
- # Build Geolocation
85
- #
86
- # @param [String] ip
87
- # @param [Mihari::Enrichers::MMDB] enricher
88
- #
89
- # @return [Mihari::Models::Geolocation, nil]
90
- #
91
- def call(ip, enricher: Enrichers::MMDB.new)
92
- enricher.result(ip).fmap do |res|
93
- if res.country_code
94
- Models::Geolocation.new(
95
- country: NormalizeCountry(res.country_code, to: :short),
96
- country_code: res.country_code
97
- )
98
- end
99
- end.value_or nil
100
- end
101
- end
102
-
103
- #
104
- # Port builder
105
- #
106
- class PortBuilder < Service
107
- #
108
- # Build ports
109
- #
110
- # @param [String] ip
111
- # @param [Mihari::Enrichers::Shodan] enricher
112
- #
113
- # @return [Array<Mihari::Models::Port>]
114
- #
115
- def call(ip, enricher: Enrichers::Shodan.new)
116
- enricher.result(ip).fmap do |res|
117
- (res&.ports || []).map { |port| Models::Port.new(port: port) }
118
- end.value_or []
119
- end
120
- end
121
-
122
- #
123
- # Reverse DNS name builder
124
- #
125
- class ReverseDnsNameBuilder < Service
126
- #
127
- # Build reverse DNS names
128
- #
129
- # @param [String] ip
130
- # @param [Mihari::Enrichers::Shodan] enricher
131
- #
132
- # @return [Array<Mihari::Models::ReverseDnsName>]
133
- #
134
- def call(ip, enricher: Enrichers::Shodan.new)
135
- enricher.result(ip).fmap do |res|
136
- (res&.hostnames || []).map { |name| Models::ReverseDnsName.new(name: name) }
137
- end.value_or []
138
- end
139
- end
140
-
141
- #
142
- # Whois record builder
143
- #
144
- class WhoisRecordBuilder < Service
145
- #
146
- # Build whois record
147
- #
148
- # @param [String] domain
149
- # @param [Mihari::Enrichers::Whois] enricher
150
- #
151
- # @return [Mihari::Models::WhoisRecord, nil]
152
- #
153
- def call(domain, enricher: Enrichers::Whois.new)
154
- enricher.result(domain).value_or nil
155
- end
156
- end
157
23
  end
158
24
  end
@@ -17,7 +17,9 @@ module Mihari
17
17
  :ports
18
18
  ).find(id)
19
19
 
20
- artifact.enrich_all
20
+ raise UnenrichableError.new, "#{artifact.id} is already enriched or unenrichable" unless artifact.enrichable?
21
+
22
+ artifact.enrich
21
23
  artifact.save
22
24
  end
23
25
  end
@@ -93,12 +93,9 @@ module Mihari
93
93
  #
94
94
  # @param [Object] read_data
95
95
  def call(input_enumerator, selector)
96
- parsed = proc do
97
- $SAFE = 1
98
- input_enumerator.instance_eval(selector)
99
- end.call
96
+ parsed = input_enumerator.instance_eval(selector)
100
97
 
101
- raise TypeError unless parsed.all?(String)
98
+ raise TypeError unless parsed.is_a?(Array) || parsed.all?(String)
102
99
 
103
100
  parsed
104
101
  end
@@ -51,7 +51,7 @@ module Mihari
51
51
  # @return [Mihari::Structs::MMDB::Response]
52
52
  #
53
53
  def call(ip)
54
- Mihari::Enrichers::MMDB.new.call ip
54
+ Clients::MMDB.new.query ip
55
55
  end
56
56
  end
57
57
  end
@@ -79,10 +79,10 @@ module Mihari
79
79
  # @return [Mihari::Rule]
80
80
  #
81
81
  def rule
82
- @rule ||= [].tap do |out|
82
+ @rule ||= lambda do
83
83
  data = Mihari::Models::Rule.find(rule_id).data
84
- out << Rule.new(**data)
85
- end.first
84
+ Rule.new(**data)
85
+ end.call
86
86
  end
87
87
  end
88
88
  end
@@ -14,7 +14,7 @@ module Mihari
14
14
  # @return [Mihari::AutonomousSystem]
15
15
  #
16
16
  def as
17
- Mihari::Models::AutonomousSystem.new(asn: normalize_asn(asn))
17
+ Mihari::Models::AutonomousSystem.new(number: normalize_asn(asn))
18
18
  end
19
19
 
20
20
  class << self
@@ -76,7 +76,7 @@ module Mihari
76
76
  # @return [Mihari::Port]
77
77
  #
78
78
  def _port
79
- Models::Port.new(port: port)
79
+ Models::Port.new(number: port)
80
80
  end
81
81
 
82
82
  class << self
@@ -22,7 +22,7 @@ module Mihari
22
22
  # @return [Mihari::AutonomousSystem]
23
23
  #
24
24
  def as
25
- Mihari::Models::AutonomousSystem.new(asn: normalize_asn(asn))
25
+ Mihari::Models::AutonomousSystem.new(number: normalize_asn(asn))
26
26
  end
27
27
 
28
28
  #
@@ -51,7 +51,7 @@ module Mihari
51
51
  # @return [Mihari::AutonomousSystem]
52
52
  #
53
53
  def as
54
- Mihari::Models::AutonomousSystem.new(asn: normalize_asn(asn))
54
+ Mihari::Models::AutonomousSystem.new(number: normalize_asn(asn))
55
55
  end
56
56
 
57
57
  class << self