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
@@ -67,17 +67,25 @@ module Mihari
67
67
  # @return [Integer]
68
68
  attribute :port, Types::Int
69
69
 
70
+ # @!attribute [r] cpe
71
+ # @return [Array<String>]
72
+ attribute :cpe, Types::Array(Types::String)
73
+
74
+ # @!attribute [r] vulns
75
+ # @return [Hash]
76
+ attribute :vulns, Types::Hash
77
+
70
78
  # @!attribute [r] metadata
71
79
  # @return [Hash]
72
80
  attribute :metadata, Types::Hash
73
81
 
74
82
  #
75
- # @return [Mihari::AutonomousSystem, nil]
83
+ # @return [Mihari::Models::AutonomousSystem, nil]
76
84
  #
77
- def _asn
85
+ def autonomous_system
78
86
  return nil if asn.nil?
79
87
 
80
- Mihari::Models::AutonomousSystem.new(asn: normalize_asn(asn))
88
+ Models::AutonomousSystem.new(number: normalize_asn(asn))
81
89
  end
82
90
 
83
91
  class << self
@@ -103,6 +111,8 @@ module Mihari
103
111
  domains: d.fetch("domains"),
104
112
  ip_str: d.fetch("ip_str"),
105
113
  port: d.fetch("port"),
114
+ cpe: d["cpe"] || [],
115
+ vulns: d["vulns"] || {},
106
116
  metadata: d
107
117
  )
108
118
  end
@@ -110,6 +120,8 @@ module Mihari
110
120
  end
111
121
 
112
122
  class Response < Dry::Struct
123
+ prepend MemoWise
124
+
113
125
  # @!attribute [r] matches
114
126
  # @return [Array<Match>]
115
127
  attribute :matches, Types.Array(Match)
@@ -118,6 +130,16 @@ module Mihari
118
130
  # @return [Integer]
119
131
  attribute :total, Types::Int
120
132
 
133
+ #
134
+ # @param [String] ip
135
+ #
136
+ # @return [Array<Mihari::Structs::Shodan::Match>]
137
+ #
138
+ def select_matches_by_ip(ip)
139
+ matches.select { |match| match.ip_str == ip }
140
+ end
141
+ memo_wise :select_matches_by_ip
142
+
121
143
  #
122
144
  # Collect metadata from matches
123
145
  #
@@ -126,7 +148,7 @@ module Mihari
126
148
  # @return [Array<Hash>]
127
149
  #
128
150
  def collect_metadata_by_ip(ip)
129
- matches.select { |match| match.ip_str == ip }.map(&:metadata)
151
+ select_matches_by_ip(ip).map(&:metadata)
130
152
  end
131
153
 
132
154
  #
@@ -137,7 +159,7 @@ module Mihari
137
159
  # @return [Array<String>]
138
160
  #
139
161
  def collect_ports_by_ip(ip)
140
- matches.select { |match| match.ip_str == ip }.map(&:port)
162
+ select_matches_by_ip(ip).map(&:port)
141
163
  end
142
164
 
143
165
  #
@@ -148,7 +170,30 @@ module Mihari
148
170
  # @return [Array<String>]
149
171
  #
150
172
  def collect_hostnames_by_ip(ip)
151
- matches.select { |match| match.ip_str == ip }.map(&:hostnames).flatten.uniq
173
+ select_matches_by_ip(ip).map(&:hostnames).flatten.uniq
174
+ end
175
+
176
+ #
177
+ # Collect CPE from matches
178
+ #
179
+ # @param [String] ip
180
+ #
181
+ # @return [Array<String>]
182
+ #
183
+ def collect_cpes_by_ip(ip)
184
+ select_matches_by_ip(ip).map(&:cpe).flatten.uniq
185
+ end
186
+
187
+ #
188
+ # Collect vulnerabilities from matches
189
+ #
190
+ # @param [String] ip
191
+ #
192
+ # @return [Array<String>]
193
+ #
194
+ def collect_vulns_by_ip(ip)
195
+ # NOTE: vuln keys = CVE IDs
196
+ select_matches_by_ip(ip).map { |match| match.vulns.keys }.flatten.uniq
152
197
  end
153
198
 
154
199
  #
@@ -158,20 +203,22 @@ module Mihari
158
203
  matches.map do |match|
159
204
  metadata = collect_metadata_by_ip(match.ip_str)
160
205
 
161
- ports = collect_ports_by_ip(match.ip_str).map do |port|
162
- Mihari::Models::Port.new(port: port)
163
- end
206
+ ports = collect_ports_by_ip(match.ip_str).map { |port| Models::Port.new(number: port) }
164
207
  reverse_dns_names = collect_hostnames_by_ip(match.ip_str).map do |name|
165
- Mihari::Models::ReverseDnsName.new(name: name)
208
+ Models::ReverseDnsName.new(name: name)
166
209
  end
210
+ cpes = collect_cpes_by_ip(match.ip_str).map { |name| Models::CPE.new(name: name) }
211
+ vulnerabilities = collect_vulns_by_ip(match.ip_str).map { |name| Models::Vulnerability.new(name: name) }
167
212
 
168
213
  Mihari::Models::Artifact.new(
169
214
  data: match.ip_str,
170
215
  metadata: metadata,
171
- autonomous_system: match._asn,
216
+ autonomous_system: match.autonomous_system,
172
217
  geolocation: match.location.geolocation,
173
218
  ports: ports,
174
- reverse_dns_names: reverse_dns_names
219
+ reverse_dns_names: reverse_dns_names,
220
+ cpes: cpes,
221
+ vulnerabilities: vulnerabilities
175
222
  )
176
223
  end
177
224
  end
@@ -232,15 +279,6 @@ module Mihari
232
279
  vulns: d.fetch("vulns")
233
280
  )
234
281
  end
235
-
236
- #
237
- # @param [String] json
238
- #
239
- # @return [InternetDBResponse]
240
- #
241
- def from_json!(json)
242
- from_dynamic!(JSON.parse(json))
243
- end
244
282
  end
245
283
  end
246
284
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- VERSION = "7.1.3"
4
+ VERSION = "7.3.0"
5
5
  end
@@ -53,7 +53,7 @@ module Mihari
53
53
 
54
54
  desc "Enrich an artifact", {
55
55
  success: { code: 201, model: Entities::Message },
56
- failure: [{ code: 404, model: Entities::ErrorMessage }],
56
+ failure: [{ code: 400, model: Entities::ErrorMessage }, { code: 404, model: Entities::ErrorMessage }],
57
57
  summary: "Enrich an artifact"
58
58
  }
59
59
  params do
@@ -74,10 +74,12 @@ module Mihari
74
74
  end
75
75
  end.to_result
76
76
 
77
- message = queued ? "ID:#{id}'s enrichment has been queued" : "ID:#{id}'s enrichment has been succeeded"
77
+ message = queued ? "ID:#{id}'s enrichment is queued" : "ID:#{id}'s enrichment is successful"
78
78
  return present({ message: message, queued: queued }, with: Entities::QueueMessage) if result.success?
79
79
 
80
80
  case result.failure
81
+ when UnenrichableError
82
+ error!({ message: result.failure.message }, 400)
81
83
  when ActiveRecord::RecordNotFound
82
84
  error!({ message: "ID:#{id} not found" }, 404)
83
85
  end
@@ -94,7 +94,7 @@ module Mihari
94
94
  end
95
95
  end.to_result
96
96
 
97
- message = queued ? "ID:#{id}'s search has been queued" : "ID:#{id}'s search has been succeed"
97
+ message = queued ? "ID:#{id}'s search is queued" : "ID:#{id}'s search is successful"
98
98
  return present({ message: message, queued: queued }, with: Entities::QueueMessage) if result.success?
99
99
 
100
100
  case result.failure