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
@@ -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