mihari 5.4.9 → 5.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/docs/analyzers/binaryedge.md +2 -2
  3. data/docs/analyzers/censys.md +3 -3
  4. data/docs/analyzers/circl.md +3 -3
  5. data/docs/analyzers/crtsh.md +2 -2
  6. data/docs/analyzers/dnstwister.md +1 -1
  7. data/docs/analyzers/feed.md +7 -7
  8. data/docs/analyzers/greynoise.md +2 -2
  9. data/docs/analyzers/hunterhow.md +4 -4
  10. data/docs/analyzers/index.md +13 -8
  11. data/docs/analyzers/onyphe.md +2 -2
  12. data/docs/analyzers/otx.md +2 -2
  13. data/docs/analyzers/passivetotal.md +3 -3
  14. data/docs/analyzers/pulsedive.md +2 -2
  15. data/docs/analyzers/securitytrails.md +2 -2
  16. data/docs/analyzers/shodan.md +2 -2
  17. data/docs/analyzers/urlscan.md +2 -2
  18. data/docs/analyzers/virustotal.md +2 -2
  19. data/docs/analyzers/virustotal_intelligence.md +2 -2
  20. data/docs/analyzers/zoomeye.md +3 -3
  21. data/docs/emitters/hive.md +3 -3
  22. data/docs/emitters/index.md +29 -0
  23. data/docs/emitters/misp.md +2 -2
  24. data/docs/emitters/slack.md +2 -2
  25. data/docs/emitters/webhook.md +4 -4
  26. data/docs/enrichers/index.md +29 -0
  27. data/docs/enrichers/ipinfo.md +7 -0
  28. data/docs/index.md +0 -2
  29. data/docs/installation.md +1 -1
  30. data/docs/rule.md +11 -11
  31. data/frontend/package-lock.json +294 -2772
  32. data/frontend/package.json +10 -10
  33. data/lib/mihari/analyzers/base.rb +15 -8
  34. data/lib/mihari/analyzers/binaryedge.rb +5 -1
  35. data/lib/mihari/analyzers/censys.rb +6 -1
  36. data/lib/mihari/analyzers/greynoise.rb +5 -1
  37. data/lib/mihari/analyzers/hunterhow.rb +5 -1
  38. data/lib/mihari/analyzers/onyphe.rb +5 -1
  39. data/lib/mihari/analyzers/rule.rb +43 -7
  40. data/lib/mihari/analyzers/shodan.rb +5 -1
  41. data/lib/mihari/analyzers/urlscan.rb +5 -1
  42. data/lib/mihari/analyzers/virustotal_intelligence.rb +5 -1
  43. data/lib/mihari/analyzers/zoomeye.rb +5 -1
  44. data/lib/mihari/clients/base.rb +7 -7
  45. data/lib/mihari/clients/binaryedge.rb +10 -4
  46. data/lib/mihari/clients/censys.rb +11 -4
  47. data/lib/mihari/clients/greynoise.rb +10 -4
  48. data/lib/mihari/clients/hunterhow.rb +10 -4
  49. data/lib/mihari/clients/misp.rb +3 -2
  50. data/lib/mihari/clients/onyphe.rb +10 -4
  51. data/lib/mihari/clients/shodan.rb +10 -4
  52. data/lib/mihari/clients/the_hive.rb +3 -2
  53. data/lib/mihari/clients/urlscan.rb +9 -3
  54. data/lib/mihari/clients/virustotal.rb +10 -4
  55. data/lib/mihari/clients/zoomeye.rb +11 -5
  56. data/lib/mihari/config.rb +8 -0
  57. data/lib/mihari/emitters/base.rb +49 -12
  58. data/lib/mihari/emitters/misp.rb +7 -6
  59. data/lib/mihari/emitters/slack.rb +24 -6
  60. data/lib/mihari/emitters/the_hive.rb +8 -7
  61. data/lib/mihari/emitters/webhook.rb +31 -29
  62. data/lib/mihari/enrichers/base.rb +53 -16
  63. data/lib/mihari/enrichers/google_public_dns.rb +33 -42
  64. data/lib/mihari/enrichers/ipinfo.rb +32 -34
  65. data/lib/mihari/enrichers/shodan.rb +18 -26
  66. data/lib/mihari/enrichers/whois.rb +121 -111
  67. data/lib/mihari/mixins/retriable.rb +4 -2
  68. data/lib/mihari/models/artifact.rb +37 -23
  69. data/lib/mihari/models/autonomous_system.rb +3 -2
  70. data/lib/mihari/models/cpe.rb +3 -2
  71. data/lib/mihari/models/dns.rb +3 -2
  72. data/lib/mihari/models/geolocation.rb +3 -2
  73. data/lib/mihari/models/port.rb +3 -2
  74. data/lib/mihari/models/reverse_dns.rb +3 -2
  75. data/lib/mihari/models/whois.rb +4 -3
  76. data/lib/mihari/schemas/analyzer.rb +2 -1
  77. data/lib/mihari/schemas/emitter.rb +39 -25
  78. data/lib/mihari/schemas/enricher.rb +28 -2
  79. data/lib/mihari/schemas/rule.rb +6 -2
  80. data/lib/mihari/version.rb +1 -1
  81. data/lib/mihari/web/endpoints/ip_addresses.rb +1 -1
  82. data/lib/mihari/web/public/assets/index-b5d817a3.js +1749 -0
  83. data/lib/mihari/web/public/index.html +1 -1
  84. data/lib/mihari/web/public/redoc-static.html +400 -400
  85. data/mihari.gemspec +2 -2
  86. data/mkdocs.yml +8 -6
  87. data/requirements.txt +1 -1
  88. metadata +7 -7
  89. data/lib/mihari/web/public/assets/index-a92abd57.js +0 -1740
@@ -5,122 +5,132 @@ require "whois-parser"
5
5
  module Mihari
6
6
  module Enrichers
7
7
  class Whois < Base
8
- # @type [Hash]
9
- @memo = {}
8
+ # @return [Hash]
9
+ attr_accessor :memo
10
10
 
11
- # @return [Boolean]
12
- def valid?
13
- true
11
+ #
12
+ # @param [Hash, nil] options
13
+ #
14
+ def initialize(options: nil)
15
+ super(options: options)
16
+
17
+ @memo = {}
18
+ end
19
+
20
+ #
21
+ # Query IAIA Whois API
22
+ #
23
+ # @param [String] name
24
+ #
25
+ # @return [Mihari::WhoisRecord, nil]
26
+ #
27
+ def query(domain)
28
+ domain = PublicSuffix.domain(domain)
29
+
30
+ # check memo
31
+ return memo[domain].dup if memo.key?(domain)
32
+
33
+ record = whois.lookup(domain)
34
+ parser = record.parser
35
+ return nil if parser.available?
36
+
37
+ whois_record = WhoisRecord.new(
38
+ domain: domain,
39
+ created_on: get_created_on(parser),
40
+ updated_on: get_updated_on(parser),
41
+ expires_on: get_expires_on(parser),
42
+ registrar: get_registrar(parser),
43
+ contacts: get_contacts(parser)
44
+ )
45
+
46
+ # set memo
47
+ memo[domain] = whois_record
48
+
49
+ whois_record
14
50
  end
15
51
 
16
- class << self
17
- include Dry::Monads[:result]
18
-
19
- #
20
- # Query IAIA Whois API
21
- #
22
- # @param [String] name
23
- #
24
- # @return [Mihari::WhoisRecord, nil]
25
- #
26
- def query(domain)
27
- domain = PublicSuffix.domain(domain)
28
-
29
- # check memo
30
- if @memo.key?(domain)
31
- whois_record = @memo[domain]
32
- # return clone of the record
33
- return whois_record.dup
52
+ def reset_cache
53
+ @memo = {}
54
+ end
55
+
56
+ private
57
+
58
+ #
59
+ # @return [::Whois::Client]
60
+ #
61
+ def whois
62
+ @whois ||= [].tap do |out|
63
+ out << if timeout.nil?
64
+ ::Whois::Client.new
65
+ else
66
+ ::Whois::Client.new(timeout: timeout)
34
67
  end
68
+ end.last
69
+ end
70
+
71
+ #
72
+ # Get created_on
73
+ #
74
+ # @param [::Whois::Parser:] parser
75
+ #
76
+ # @return [Date, nil]
77
+ #
78
+ def get_created_on(parser)
79
+ parser.created_on
80
+ rescue ::Whois::AttributeNotImplemented
81
+ nil
82
+ end
83
+
84
+ #
85
+ # Get updated_on
86
+ #
87
+ # @param [::Whois::Parser:] parser
88
+ #
89
+ # @return [Date, nil]
90
+ #
91
+ def get_updated_on(parser)
92
+ parser.updated_on
93
+ rescue ::Whois::AttributeNotImplemented
94
+ nil
95
+ end
96
+
97
+ #
98
+ # Get expires_on
99
+ #
100
+ # @param [::Whois::Parser:] parser
101
+ #
102
+ # @return [Date, nil]
103
+ #
104
+ def get_expires_on(parser)
105
+ parser.expires_on
106
+ rescue ::Whois::AttributeNotImplemented
107
+ nil
108
+ end
109
+
110
+ #
111
+ # Get registrar
112
+ #
113
+ # @param [::Whois::Parser:] parser
114
+ #
115
+ # @return [Hash, nil]
116
+ #
117
+ def get_registrar(parser)
118
+ parser.registrar&.to_h
119
+ rescue ::Whois::AttributeNotImplemented
120
+ nil
121
+ end
35
122
 
36
- record = ::Whois.whois(domain)
37
- parser = record.parser
38
-
39
- return nil if parser.available?
40
-
41
- whois_record = WhoisRecord.new(
42
- domain: domain,
43
- created_on: get_created_on(parser),
44
- updated_on: get_updated_on(parser),
45
- expires_on: get_expires_on(parser),
46
- registrar: get_registrar(parser),
47
- contacts: get_contacts(parser)
48
- )
49
- # set memo
50
- @memo[domain] = whois_record
51
- whois_record
52
- end
53
-
54
- def reset_cache
55
- @memo = {}
56
- end
57
-
58
- private
59
-
60
- #
61
- # Get created_on
62
- #
63
- # @param [::Whois::Parser:] parser
64
- #
65
- # @return [Date, nil]
66
- #
67
- def get_created_on(parser)
68
- parser.created_on
69
- rescue ::Whois::AttributeNotImplemented
70
- nil
71
- end
72
-
73
- #
74
- # Get updated_on
75
- #
76
- # @param [::Whois::Parser:] parser
77
- #
78
- # @return [Date, nil]
79
- #
80
- def get_updated_on(parser)
81
- parser.updated_on
82
- rescue ::Whois::AttributeNotImplemented
83
- nil
84
- end
85
-
86
- #
87
- # Get expires_on
88
- #
89
- # @param [::Whois::Parser:] parser
90
- #
91
- # @return [Date, nil]
92
- #
93
- def get_expires_on(parser)
94
- parser.expires_on
95
- rescue ::Whois::AttributeNotImplemented
96
- nil
97
- end
98
-
99
- #
100
- # Get registrar
101
- #
102
- # @param [::Whois::Parser:] parser
103
- #
104
- # @return [Hash, nil]
105
- #
106
- def get_registrar(parser)
107
- parser.registrar&.to_h
108
- rescue ::Whois::AttributeNotImplemented
109
- nil
110
- end
111
-
112
- #
113
- # Get contacts
114
- #
115
- # @param [::Whois::Parser:] parser
116
- #
117
- # @return [Array[Hash], nil]
118
- #
119
- def get_contacts(parser)
120
- parser.contacts.map(&:to_h)
121
- rescue ::Whois::AttributeNotImplemented
122
- nil
123
- end
123
+ #
124
+ # Get contacts
125
+ #
126
+ # @param [::Whois::Parser:] parser
127
+ #
128
+ # @return [Array[Hash], nil]
129
+ #
130
+ def get_contacts(parser)
131
+ parser.contacts.map(&:to_h)
132
+ rescue ::Whois::AttributeNotImplemented
133
+ nil
124
134
  end
125
135
  end
126
136
  end
@@ -19,15 +19,17 @@ module Mihari
19
19
  #
20
20
  # @param [Integer] times
21
21
  # @param [Integer] interval
22
+ # @param [Boolean] exponential_backoff
22
23
  # @param [Array<StandardError>] on
23
24
  #
24
- def retry_on_error(times: 3, interval: 5, on: DEFAULT_ON)
25
+ def retry_on_error(times: 3, interval: 5, exponential_backoff: true, on: DEFAULT_ON)
25
26
  try = 0
26
27
  begin
27
28
  try += 1
28
29
  yield
29
30
  rescue *on => e
30
- sleep interval
31
+ sleep_seconds = exponential_backoff ? interval * (2**(try - 1)) : interval
32
+ sleep sleep_seconds
31
33
  retry if try < times
32
34
  raise e
33
35
  end
@@ -73,64 +73,78 @@ module Mihari
73
73
  #
74
74
  # Enrich(add) whois record
75
75
  #
76
- def enrich_whois
76
+ # @param [Mihari::Enrichers::Whois] enricher
77
+ #
78
+ def enrich_whois(enricher = Enrichers::Whois.new)
77
79
  return unless can_enrich_whois?
78
80
 
79
- self.whois_record = WhoisRecord.build_by_domain(normalize_as_domain(data))
81
+ self.whois_record = WhoisRecord.build_by_domain(normalize_as_domain(data), enricher: enricher)
80
82
  end
81
83
 
82
84
  #
83
85
  # Enrich(add) DNS records
84
86
  #
85
- def enrich_dns
87
+ # @param [Mihari::Enrichers::GooglePublicDNS] enricher
88
+ #
89
+ def enrich_dns(enricher = Enrichers::GooglePublicDNS.new)
86
90
  return unless can_enrich_dns?
87
91
 
88
- self.dns_records = DnsRecord.build_by_domain(normalize_as_domain(data))
92
+ self.dns_records = DnsRecord.build_by_domain(normalize_as_domain(data), enricher: enricher)
89
93
  end
90
94
 
91
95
  #
92
96
  # Enrich(add) reverse DNS names
93
97
  #
94
- def enrich_reverse_dns
98
+ # @param [Mihari::Enrichers::Shodan] enricher
99
+ #
100
+ def enrich_reverse_dns(enricher = Enrichers::Shodan.new)
95
101
  return unless can_enrich_revese_dns?
96
102
 
97
- self.reverse_dns_names = ReverseDnsName.build_by_ip(data)
103
+ self.reverse_dns_names = ReverseDnsName.build_by_ip(data, enricher: enricher)
98
104
  end
99
105
 
100
106
  #
101
107
  # Enrich(add) geolocation
102
108
  #
103
- def enrich_geolocation
109
+ # @param [Mihari::Enrichers::IPInfo] enricher
110
+ #
111
+ def enrich_geolocation(enricher = Enrichers::IPInfo.new)
104
112
  return unless can_enrich_geolocation?
105
113
 
106
- self.geolocation = Geolocation.build_by_ip(data)
114
+ self.geolocation = Geolocation.build_by_ip(data, enricher: enricher)
107
115
  end
108
116
 
109
117
  #
110
118
  # Enrich AS
111
119
  #
112
- def enrich_autonomous_system
120
+ # @param [Mihari::Enrichers::IPInfo] enricher
121
+ #
122
+ def enrich_autonomous_system(enricher = Enrichers::IPInfo.new)
113
123
  return unless can_enrich_autonomous_system?
114
124
 
115
- self.autonomous_system = AutonomousSystem.build_by_ip(data)
125
+ self.autonomous_system = AutonomousSystem.build_by_ip(data, enricher: enricher)
116
126
  end
117
127
 
118
128
  #
119
129
  # Enrich ports
120
130
  #
121
- def enrich_ports
131
+ # @param [Mihari::Enrichers::Shodan] enricher
132
+ #
133
+ def enrich_ports(enricher = Enrichers::Shodan.new)
122
134
  return unless can_enrich_ports?
123
135
 
124
- self.ports = Port.build_by_ip(data)
136
+ self.ports = Port.build_by_ip(data, enricher: enricher)
125
137
  end
126
138
 
127
139
  #
128
140
  # Enrich CPEs
129
141
  #
130
- def enrich_cpes
142
+ # @param [Mihari::Enrichers::Shodan] enricher
143
+ #
144
+ def enrich_cpes(enricher = Enrichers::Shodan.new)
131
145
  return unless can_enrich_cpes?
132
146
 
133
- self.cpes = CPE.build_by_ip(data)
147
+ self.cpes = CPE.build_by_ip(data, enricher: enricher)
134
148
  end
135
149
 
136
150
  #
@@ -147,32 +161,32 @@ module Mihari
147
161
  end
148
162
 
149
163
  ENRICH_METHODS_BY_ENRICHER = {
150
- whois: [
151
- :enrich_whois
164
+ Enrichers::Whois => %i[
165
+ enrich_whois
152
166
  ],
153
- ipinfo: %i[
167
+ Enrichers::IPInfo => %i[
154
168
  enrich_autonomous_system
155
169
  enrich_geolocation
156
170
  ],
157
- shodan: %i[
171
+ Enrichers::Shodan => %i[
158
172
  enrich_ports
159
173
  enrich_cpes
160
174
  enrich_reverse_dns
161
175
  ],
162
- google_public_dns: [
163
- :enrich_dns
176
+ Enrichers::GooglePublicDNS => %i[
177
+ enrich_dns
164
178
  ]
165
179
  }.freeze
166
180
 
167
181
  #
168
182
  # Enrich by name of enricher
169
183
  #
170
- # @param [String] enricher
184
+ # @param [Mihari::Enrichers::Base] enricher
171
185
  #
172
186
  def enrich_by_enricher(enricher)
173
- methods = ENRICH_METHODS_BY_ENRICHER[enricher.downcase.to_sym] || []
187
+ methods = ENRICH_METHODS_BY_ENRICHER[enricher.class] || []
174
188
  methods.each do |method|
175
- send(method) if respond_to?(method)
189
+ send(method, enricher) if respond_to?(method)
176
190
  end
177
191
  end
178
192
 
@@ -11,11 +11,12 @@ module Mihari
11
11
  # Build AS
12
12
  #
13
13
  # @param [String] ip
14
+ # @param [Mihari::Enrichers::IPInfo] enricher
14
15
  #
15
16
  # @return [Mihari::AutonomousSystem, nil]
16
17
  #
17
- def build_by_ip(ip)
18
- result = Enrichers::IPInfo.query_result(ip).bind do |res|
18
+ def build_by_ip(ip, enricher: Enrichers::IPInfo.new)
19
+ result = enricher.query_result(ip).bind do |res|
19
20
  value = res&.asn
20
21
  if value.nil?
21
22
  Success nil
@@ -11,11 +11,12 @@ module Mihari
11
11
  # Build CPEs
12
12
  #
13
13
  # @param [String] ip
14
+ # @param [Mihari::Enrichers::Shodan] enricher
14
15
  #
15
16
  # @return [Array<Mihari::CPE>]
16
17
  #
17
- def build_by_ip(ip)
18
- result = Enrichers::Shodan.query_result(ip).bind do |res|
18
+ def build_by_ip(ip, enricher: Enrichers::Shodan.new)
19
+ result = enricher.query_result(ip).bind do |res|
19
20
  if res.nil?
20
21
  Success []
21
22
  else
@@ -11,11 +11,12 @@ module Mihari
11
11
  # Build DNS records
12
12
  #
13
13
  # @param [String] domain
14
+ # @param [Mihari::Enrichers::Shodan] enricher
14
15
  #
15
16
  # @return [Array<Mihari::DnsRecord>]
16
17
  #
17
- def build_by_domain(domain)
18
- result = Enrichers::GooglePublicDNS.query_result(domain).bind do |responses|
18
+ def build_by_domain(domain, enricher: Enrichers::GooglePublicDNS.new)
19
+ result = enricher.query_result(domain).bind do |responses|
19
20
  Success(
20
21
  responses.map do |res|
21
22
  res.answers.map do |answer|
@@ -13,11 +13,12 @@ module Mihari
13
13
  # Build Geolocation
14
14
  #
15
15
  # @param [String] ip
16
+ # @param [Mihari::Enrichers::IPinfo] enricher
16
17
  #
17
18
  # @return [Mihari::Geolocation, nil]
18
19
  #
19
- def build_by_ip(ip)
20
- result = Enrichers::IPInfo.query_result(ip).bind do |res|
20
+ def build_by_ip(ip, enricher: Enrichers::IPInfo.new)
21
+ result = enricher.query_result(ip).bind do |res|
21
22
  value = res&.country_code
22
23
  if value.nil?
23
24
  Success nil
@@ -11,11 +11,12 @@ module Mihari
11
11
  # Build ports
12
12
  #
13
13
  # @param [String] ip
14
+ # @param [Mihari::Enrichers::Shodan] enricher
14
15
  #
15
16
  # @return [Array<Mihari::Port>]
16
17
  #
17
- def build_by_ip(ip)
18
- result = Enrichers::Shodan.query_result(ip).bind do |res|
18
+ def build_by_ip(ip, enricher: Enrichers::Shodan.new)
19
+ result = enricher.query_result(ip).bind do |res|
19
20
  if res.nil?
20
21
  Success []
21
22
  else
@@ -11,11 +11,12 @@ module Mihari
11
11
  # Build reverse DNS names
12
12
  #
13
13
  # @param [String] ip
14
+ # @param [Mihari::Enrichers::Shodan] enricher
14
15
  #
15
16
  # @return [Array<Mihari::ReverseDnsName>]
16
17
  #
17
- def build_by_ip(ip)
18
- result = Enrichers::Shodan.query_result(ip).bind do |res|
18
+ def build_by_ip(ip, enricher: Enrichers::Shodan.new)
19
+ result = enricher.query_result(ip).bind do |res|
19
20
  if res.nil?
20
21
  Success []
21
22
  else
@@ -12,12 +12,13 @@ module Mihari
12
12
  #
13
13
  # Build whois record
14
14
  #
15
- # @param [Stinrg] domain
15
+ # @param [String] domain
16
+ # @param [Mihari::Enrichers::Whois] enricher
16
17
  #
17
18
  # @return [WhoisRecord, nil]
18
19
  #
19
- def build_by_domain(domain)
20
- result = Enrichers::Whois.query_result(domain)
20
+ def build_by_domain(domain, enricher: Enrichers::Whois.new)
21
+ result = enricher.query_result(domain)
21
22
  result.value_or nil
22
23
  end
23
24
  end
@@ -3,10 +3,11 @@
3
3
  module Mihari
4
4
  module Schemas
5
5
  AnalyzerOptions = Dry::Schema.Params do
6
- optional(:interval).value(:integer)
6
+ optional(:pagination_interval).value(:integer).default(Mihari.config.pagination_interval)
7
7
  optional(:pagination_limit).value(:integer).default(Mihari.config.pagination_limit)
8
8
  optional(:retry_times).value(:integer).default(Mihari.config.retry_times)
9
9
  optional(:retry_interval).value(:integer).default(Mihari.config.retry_interval)
10
+ optional(:retry_exponential_backoff).value(:bool).default(Mihari.config.retry_exponential_backoff)
10
11
  optional(:ignore_error).value(:bool).default(Mihari.config.ignore_error)
11
12
  optional(:timeout).value(:integer)
12
13
  end
@@ -2,35 +2,49 @@
2
2
 
3
3
  module Mihari
4
4
  module Schemas
5
- Database = Dry::Schema.Params do
6
- required(:emitter).value(Types::String.enum("database"))
7
- end
5
+ module Emitters
6
+ EmitterOptions = Dry::Schema.Params do
7
+ optional(:retry_times).value(:integer).default(Mihari.config.retry_times)
8
+ optional(:retry_interval).value(:integer).default(Mihari.config.retry_interval)
9
+ optional(:retry_exponential_backoff).value(:bool).default(Mihari.config.retry_exponential_backoff)
10
+ optional(:timeout).value(:integer)
11
+ end
8
12
 
9
- MISP = Dry::Schema.Params do
10
- required(:emitter).value(Types::String.enum("misp"))
11
- optional(:url).value(:string)
12
- optional(:api_key).value(:string)
13
- end
13
+ Database = Dry::Schema.Params do
14
+ required(:emitter).value(Types::String.enum("database"))
15
+ optional(:options).hash(EmitterOptions)
16
+ end
14
17
 
15
- TheHive = Dry::Schema.Params do
16
- required(:emitter).value(Types::String.enum("the_hive"))
17
- optional(:url).value(:string)
18
- optional(:api_key).value(:string)
19
- optional(:api_version).value(Types::String.enum("v4", "v5")).default("v4")
20
- end
18
+ MISP = Dry::Schema.Params do
19
+ required(:emitter).value(Types::String.enum("misp"))
20
+ optional(:url).value(:string)
21
+ optional(:api_key).value(:string)
22
+ optional(:options).hash(EmitterOptions)
23
+ end
21
24
 
22
- Slack = Dry::Schema.Params do
23
- required(:emitter).value(Types::String.enum("slack"))
24
- optional(:webhook_url).value(:string)
25
- optional(:channel).value(:string)
26
- end
25
+ TheHive = Dry::Schema.Params do
26
+ required(:emitter).value(Types::String.enum("the_hive"))
27
+ optional(:url).value(:string)
28
+ optional(:api_key).value(:string)
29
+ optional(:api_version).value(Types::String.enum("v4", "v5")).default("v4")
30
+ optional(:options).hash(EmitterOptions)
31
+ end
32
+
33
+ Slack = Dry::Schema.Params do
34
+ required(:emitter).value(Types::String.enum("slack"))
35
+ optional(:webhook_url).value(:string)
36
+ optional(:channel).value(:string)
37
+ optional(:options).hash(EmitterOptions)
38
+ end
27
39
 
28
- Webhook = Dry::Schema.Params do
29
- required(:emitter).value(Types::String.enum("webhook"))
30
- required(:url).value(:string)
31
- optional(:method).value(Types::HTTPRequestMethods).default("POST")
32
- optional(:headers).value(:hash).default({})
33
- optional(:template).value(:string)
40
+ Webhook = Dry::Schema.Params do
41
+ required(:emitter).value(Types::String.enum("webhook"))
42
+ required(:url).value(:string)
43
+ optional(:method).value(Types::HTTPRequestMethods).default("POST")
44
+ optional(:headers).value(:hash).default({})
45
+ optional(:template).value(:string)
46
+ optional(:options).hash(EmitterOptions)
47
+ end
34
48
  end
35
49
  end
36
50
  end
@@ -2,8 +2,34 @@
2
2
 
3
3
  module Mihari
4
4
  module Schemas
5
- Enricher = Dry::Schema.Params do
6
- required(:enricher).value(Types::EnricherTypes)
5
+ module Enrichers
6
+ EnricherOptions = Dry::Schema.Params do
7
+ optional(:retry_times).value(:integer).default(Mihari.config.retry_times)
8
+ optional(:retry_interval).value(:integer).default(Mihari.config.retry_interval)
9
+ optional(:retry_exponential_backoff).value(:bool).default(Mihari.config.retry_exponential_backoff)
10
+ optional(:timeout).value(:integer)
11
+ end
12
+
13
+ IPInfo = Dry::Schema.Params do
14
+ required(:enricher).value(Types::String.enum("ipinfo"))
15
+ optional(:api_key).value(:string)
16
+ optional(:options).hash(EnricherOptions)
17
+ end
18
+
19
+ Whois = Dry::Schema.Params do
20
+ required(:enricher).value(Types::String.enum("whois"))
21
+ optional(:options).hash(EnricherOptions)
22
+ end
23
+
24
+ Shodan = Dry::Schema.Params do
25
+ required(:enricher).value(Types::String.enum("shodan"))
26
+ optional(:options).hash(EnricherOptions)
27
+ end
28
+
29
+ GooglePublicDNS = Dry::Schema.Params do
30
+ required(:enricher).value(Types::String.enum("google_public_dns"))
31
+ optional(:options).hash(EnricherOptions)
32
+ end
7
33
  end
8
34
  end
9
35
  end
@@ -25,9 +25,13 @@ module Mihari
25
25
  AnalyzerWithoutAPIKey | AnalyzerWithAPIKey | Censys | CIRCL | PassiveTotal | ZoomEye | Crtsh | Feed | HunterHow
26
26
  end
27
27
 
28
- optional(:emitters).value(:array).each { Database | MISP | TheHive | Slack | Webhook }.default(DEFAULT_EMITTERS)
28
+ optional(:emitters).value(:array).each do
29
+ Emitters::Database | Emitters::MISP | Emitters::TheHive | Emitters::Slack | Emitters::Webhook
30
+ end.default(DEFAULT_EMITTERS)
29
31
 
30
- optional(:enrichers).value(:array).each(Enricher).default(DEFAULT_ENRICHERS)
32
+ optional(:enrichers).value(:array).each do
33
+ Enrichers::Whois | Enrichers::IPInfo | Enrichers::Shodan | Enrichers::GooglePublicDNS
34
+ end.default(DEFAULT_ENRICHERS)
31
35
 
32
36
  optional(:data_types).value(array[Types::DataTypes]).default(DEFAULT_DATA_TYPES)
33
37
  optional(:falsepositives).value(array[:string]).default([])
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- VERSION = "5.4.9"
4
+ VERSION = "5.5.0"
5
5
  end
@@ -15,7 +15,7 @@ module Mihari
15
15
  get "/:ip", requirements: { ip: %r{[^/]+} } do
16
16
  ip = params[:ip].to_s
17
17
 
18
- data = Enrichers::IPInfo.query(ip)
18
+ data = Enrichers::IPInfo.new.query(ip)
19
19
  error!({ message: "IP:#{ip} is not found" }, 404) if data.nil?
20
20
 
21
21
  present data, with: Entities::IPAddress