mihari 8.2.0 → 8.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.
@@ -3,243 +3,337 @@
3
3
  module Mihari
4
4
  module Structs
5
5
  module Censys
6
- class AutonomousSystem < Dry::Struct
7
- include Concerns::AutonomousSystemNormalizable
8
-
9
- # @!attribute [r] asn
10
- # @return [Integer]
11
- attribute :asn, Types::Int
12
-
13
- #
14
- # @return [Mihari::AutonomousSystem]
15
- #
16
- def as
17
- Mihari::Models::AutonomousSystem.new(number: normalize_asn(asn))
18
- end
6
+ module V2
7
+ class AutonomousSystem < Dry::Struct
8
+ include Concerns::AutonomousSystemNormalizable
9
+
10
+ # @!attribute [r] asn
11
+ # @return [Integer]
12
+ attribute :asn, Types::Int
19
13
 
20
- class << self
21
14
  #
22
- # @param [Hash] d
15
+ # @return [Mihari::AutonomousSystem]
23
16
  #
24
- def from_dynamic!(d)
25
- return nil if d.nil?
17
+ def as
18
+ Mihari::Models::AutonomousSystem.new(number: normalize_asn(asn))
19
+ end
26
20
 
27
- d = Types::Hash[d]
28
- new(
29
- asn: d.fetch("asn")
30
- )
21
+ class << self
22
+ #
23
+ # @param [Hash] d
24
+ #
25
+ def from_dynamic!(d)
26
+ return nil if d.nil?
27
+
28
+ d = Types::Hash[d]
29
+ new(
30
+ asn: d.fetch("asn")
31
+ )
32
+ end
31
33
  end
32
34
  end
33
- end
34
35
 
35
- class Location < Dry::Struct
36
- # @!attribute [r] country
37
- # @return [String, nil]
38
- attribute :country, Types::String.optional
39
-
40
- # @!attribute [r] country_code
41
- # @return [String, nil]
42
- attribute :country_code, Types::String.optional
43
-
44
- #
45
- # @return [Mihari::Geolocation] <description>
46
- #
47
- def geolocation
48
- # sometimes Censys overlooks country
49
- # then set geolocation as nil
50
- return nil if country.nil?
51
-
52
- Mihari::Models::Geolocation.new(
53
- country:,
54
- country_code:
55
- )
56
- end
36
+ class Location < Dry::Struct
37
+ # @!attribute [r] country
38
+ # @return [String, nil]
39
+ attribute :country, Types::String.optional
40
+
41
+ # @!attribute [r] country_code
42
+ # @return [String, nil]
43
+ attribute :country_code, Types::String.optional
57
44
 
58
- class << self
59
45
  #
60
- # @param [Hash] d
46
+ # @return [Mihari::Geolocation] <description>
61
47
  #
62
- def from_dynamic!(d)
63
- d = Types::Hash[d]
64
- new(
65
- country: d["country"],
66
- country_code: d["country_code"]
48
+ def geolocation
49
+ # sometimes Censys overlooks country
50
+ # then set geolocation as nil
51
+ return nil if country.nil?
52
+
53
+ Mihari::Models::Geolocation.new(
54
+ country:,
55
+ country_code:
67
56
  )
68
57
  end
69
- end
70
- end
71
-
72
- class Service < Dry::Struct
73
- # @!attribute [r] port
74
- # @return [Integer, nil]
75
- attribute :port, Types::Int
76
58
 
77
- #
78
- # @return [Mihari::Port]
79
- #
80
- def _port
81
- Models::Port.new(number: port)
59
+ class << self
60
+ #
61
+ # @param [Hash] d
62
+ #
63
+ def from_dynamic!(d)
64
+ d = Types::Hash[d]
65
+ new(
66
+ country: d["country"],
67
+ country_code: d["country_code"]
68
+ )
69
+ end
70
+ end
82
71
  end
83
72
 
84
- class << self
73
+ class Service < Dry::Struct
74
+ # @!attribute [r] port
75
+ # @return [Integer, nil]
76
+ attribute :port, Types::Int
77
+
85
78
  #
86
- # @param [Hash] d
79
+ # @return [Mihari::Port]
87
80
  #
88
- def from_dynamic!(d)
89
- d = Types::Hash[d]
90
- new(
91
- port: d.fetch("port")
92
- )
81
+ def _port
82
+ Models::Port.new(number: port)
93
83
  end
94
- end
95
- end
96
84
 
97
- class Hit < Dry::Struct
98
- # @!attribute [r] ip
99
- # @return [String]
100
- attribute :ip, Types::String
85
+ class << self
86
+ #
87
+ # @param [Hash] d
88
+ #
89
+ def from_dynamic!(d)
90
+ d = Types::Hash[d]
91
+ new(
92
+ port: d.fetch("port")
93
+ )
94
+ end
95
+ end
96
+ end
101
97
 
102
- # @!attribute [r] location
103
- # @return [Location]
104
- attribute :location, Location
98
+ class Hit < Dry::Struct
99
+ # @!attribute [r] ip
100
+ # @return [String]
101
+ attribute :ip, Types::String
105
102
 
106
- # @!attribute [r] autonomous_system
107
- # @return [AutonomousSystem, nil]
108
- attribute :autonomous_system, AutonomousSystem.optional
103
+ # @!attribute [r] location
104
+ # @return [Location]
105
+ attribute :location, Location
109
106
 
110
- # @!attribute [r] metadata
111
- # @return [Hash]
112
- attribute :metadata, Types::Hash
107
+ # @!attribute [r] autonomous_system
108
+ # @return [AutonomousSystem, nil]
109
+ attribute :autonomous_system, AutonomousSystem.optional
113
110
 
114
- # @!attribute [r] services
115
- # @return [Array<Service>]
116
- attribute :services, Types.Array(Service)
111
+ # @!attribute [r] metadata
112
+ # @return [Hash]
113
+ attribute :metadata, Types::Hash
117
114
 
118
- #
119
- # @return [Array<Mihari::Port>]
120
- #
121
- def ports
122
- services.map(&:_port)
123
- end
115
+ # @!attribute [r] services
116
+ # @return [Array<Service>]
117
+ attribute :services, Types.Array(Service)
124
118
 
125
- #
126
- # @return [Mihari::Models::Artifact]
127
- #
128
- def artifact
129
- Models::Artifact.new(
130
- data: ip,
131
- metadata:,
132
- autonomous_system: autonomous_system&.as,
133
- geolocation: location.geolocation,
134
- ports:
135
- )
136
- end
119
+ #
120
+ # @return [Array<Mihari::Port>]
121
+ #
122
+ def ports
123
+ services.map(&:_port)
124
+ end
137
125
 
138
- class << self
139
126
  #
140
- # @param [Hash] d
127
+ # @return [Mihari::Models::Artifact]
141
128
  #
142
- def from_dynamic!(d)
143
- d = Types::Hash[d]
144
- new(
145
- ip: d.fetch("ip"),
146
- location: Location.from_dynamic!(d.fetch("location")),
147
- autonomous_system: AutonomousSystem.from_dynamic!(d["autonomous_system"]),
148
- metadata: d,
149
- services: d.fetch("services", []).map { |x| Service.from_dynamic!(x) }
129
+ def artifact
130
+ Models::Artifact.new(
131
+ data: ip,
132
+ metadata:,
133
+ autonomous_system: autonomous_system&.as,
134
+ geolocation: location.geolocation,
135
+ ports:
150
136
  )
151
137
  end
138
+
139
+ class << self
140
+ #
141
+ # @param [Hash] d
142
+ #
143
+ def from_dynamic!(d)
144
+ d = Types::Hash[d]
145
+ new(
146
+ ip: d.fetch("ip"),
147
+ location: Location.from_dynamic!(d.fetch("location")),
148
+ autonomous_system: AutonomousSystem.from_dynamic!(d["autonomous_system"]),
149
+ metadata: d,
150
+ services: d.fetch("services", []).map { |x| Service.from_dynamic!(x) }
151
+ )
152
+ end
153
+ end
154
+ end
155
+
156
+ class Links < Dry::Struct
157
+ # @!attribute [r] next
158
+ # @return [String, nil]
159
+ attribute :next, Types::String.optional
160
+
161
+ # @!attribute [r] prev
162
+ # @return [String, nil]
163
+ attribute :prev, Types::String.optional
164
+
165
+ class << self
166
+ #
167
+ # @param [Hash] d
168
+ #
169
+ def from_dynamic!(d)
170
+ d = Types::Hash[d]
171
+ new(
172
+ next: d["next"],
173
+ prev: d["prev"]
174
+ )
175
+ end
176
+ end
152
177
  end
153
- end
154
178
 
155
- class Links < Dry::Struct
156
- # @!attribute [r] next
157
- # @return [String, nil]
158
- attribute :next, Types::String.optional
179
+ class Result < Dry::Struct
180
+ # @!attribute [r] query
181
+ # @return [String]
182
+ attribute :query, Types::String
159
183
 
160
- # @!attribute [r] prev
161
- # @return [String, nil]
162
- attribute :prev, Types::String.optional
184
+ # @!attribute [r] total
185
+ # @return [Integer]
186
+ attribute :total, Types::Int
187
+
188
+ # @!attribute [r] hits
189
+ # @return [Array<Hit>]
190
+ attribute :hits, Types.Array(Hit)
191
+
192
+ # @!attribute [r] links
193
+ # @return [Links]
194
+ attribute :links, Links
163
195
 
164
- class << self
165
196
  #
166
- # @param [Hash] d
197
+ # @return [Array<Mihari::Models::Artifact>]
167
198
  #
168
- def from_dynamic!(d)
169
- d = Types::Hash[d]
170
- new(
171
- next: d["next"],
172
- prev: d["prev"]
173
- )
199
+ def artifacts
200
+ hits.map(&:artifact)
201
+ end
202
+
203
+ class << self
204
+ #
205
+ # @param [Hash] d
206
+ #
207
+ def from_dynamic!(d)
208
+ d = Types::Hash[d]
209
+ new(
210
+ query: d.fetch("query"),
211
+ total: d.fetch("total"),
212
+ hits: d.fetch("hits", []).map { |x| Hit.from_dynamic!(x) },
213
+ links: Links.from_dynamic!(d.fetch("links"))
214
+ )
215
+ end
174
216
  end
175
217
  end
176
- end
177
218
 
178
- class Result < Dry::Struct
179
- # @!attribute [r] query
180
- # @return [String]
181
- attribute :query, Types::String
219
+ class Response < Dry::Struct
220
+ # @!attribute [r] code
221
+ # @return [Integer]
222
+ attribute :code, Types::Int
182
223
 
183
- # @!attribute [r] total
184
- # @return [Integer]
185
- attribute :total, Types::Int
224
+ # @!attribute [r] status
225
+ # @return [String]
226
+ attribute :status, Types::String
186
227
 
187
- # @!attribute [r] hits
188
- # @return [Array<Hit>]
189
- attribute :hits, Types.Array(Hit)
228
+ # @!attribute [r] result
229
+ # @return [Result]
230
+ attribute :result, Result
190
231
 
191
- # @!attribute [r] links
192
- # @return [Links]
193
- attribute :links, Links
232
+ def artifacts
233
+ result.artifacts
234
+ end
194
235
 
195
- #
196
- # @return [Array<Mihari::Models::Artifact>]
197
- #
198
- def artifacts
199
- hits.map(&:artifact)
236
+ class << self
237
+ #
238
+ # @param [Hash] d
239
+ #
240
+ def from_dynamic!(d)
241
+ d = Types::Hash[d]
242
+ new(
243
+ code: d.fetch("code"),
244
+ status: d.fetch("status"),
245
+ result: Result.from_dynamic!(d.fetch("result"))
246
+ )
247
+ end
248
+ end
200
249
  end
250
+ end
201
251
 
202
- class << self
203
- #
204
- # @param [Hash] d
205
- #
206
- def from_dynamic!(d)
207
- d = Types::Hash[d]
208
- new(
209
- query: d.fetch("query"),
210
- total: d.fetch("total"),
211
- hits: d.fetch("hits", []).map { |x| Hit.from_dynamic!(x) },
212
- links: Links.from_dynamic!(d.fetch("links"))
252
+ module V3
253
+ class Hit < Dry::Struct
254
+ include Mihari::Concerns::AutonomousSystemNormalizable
255
+
256
+ attribute :ip, Types::String
257
+ attribute :autonomous_system, Types::Hash.optional
258
+ attribute :location, Types::Hash.optional
259
+ attribute :services, Types.Array(Types::Hash).optional
260
+ attribute :metadata, Types::Hash
261
+
262
+ def ports
263
+ (services || []).filter_map { |svc| svc["port"] }
264
+ end
265
+
266
+ def artifact
267
+ Mihari::Models::Artifact.new(
268
+ data: ip,
269
+ metadata: metadata,
270
+ autonomous_system: autonomous_system_model,
271
+ geolocation: geolocation_model,
272
+ ports: ports.map { |p| Mihari::Models::Port.new(number: p) }
213
273
  )
214
274
  end
215
- end
216
- end
217
275
 
218
- class Response < Dry::Struct
219
- # @!attribute [r] code
220
- # @return [Integer]
221
- attribute :code, Types::Int
276
+ def autonomous_system_model
277
+ return nil if autonomous_system.nil?
222
278
 
223
- # @!attribute [r] status
224
- # @return [String]
225
- attribute :status, Types::String
279
+ Mihari::Models::AutonomousSystem.new(number: normalize_asn(autonomous_system["asn"]))
280
+ end
226
281
 
227
- # @!attribute [r] result
228
- # @return [Result]
229
- attribute :result, Result
282
+ def geolocation_model
283
+ return nil if location.nil?
230
284
 
231
- class << self
232
- #
233
- # @param [Hash] d
234
- #
235
- def from_dynamic!(d)
236
- d = Types::Hash[d]
237
- new(
238
- code: d.fetch("code"),
239
- status: d.fetch("status"),
240
- result: Result.from_dynamic!(d.fetch("result"))
285
+ Mihari::Models::Geolocation.new(
286
+ country: location["country"],
287
+ country_code: location["country_code"]
241
288
  )
242
289
  end
290
+
291
+ class << self
292
+ def from_dynamic!(d)
293
+ res = d.dig("host_v1", "resource") || {}
294
+ new(
295
+ ip: res["ip"],
296
+ autonomous_system: res["autonomous_system"],
297
+ location: res["location"],
298
+ services: res["services"],
299
+ metadata: d
300
+ )
301
+ end
302
+ end
303
+ end
304
+
305
+ class Result < Dry::Struct
306
+ attribute :hits, Types.Array(Hit)
307
+ attribute :next_page_token, Types::String.optional
308
+
309
+ def artifacts
310
+ hits.map(&:artifact)
311
+ end
312
+
313
+ class << self
314
+ def from_dynamic!(d)
315
+ new(
316
+ hits: (d["hits"] || []).map { |x| Hit.from_dynamic!(x) },
317
+ next_page_token: d["next_page_token"]
318
+ )
319
+ end
320
+ end
321
+ end
322
+
323
+ class Response < Dry::Struct
324
+ attribute :result, Result
325
+
326
+ def artifacts
327
+ result.artifacts
328
+ end
329
+
330
+ class << self
331
+ def from_dynamic!(d)
332
+ new(
333
+ result: Result.from_dynamic!(d["result"])
334
+ )
335
+ end
336
+ end
243
337
  end
244
338
  end
245
339
  end
@@ -1,3 +1,3 @@
1
1
  module Mihari
2
- VERSION = "8.2.0"
2
+ VERSION = "8.3.0"
3
3
  end
@@ -42,7 +42,7 @@ module Mihari
42
42
  get "/:id" do
43
43
  id = params[:id].to_i
44
44
  result = Services::AlertGetter.get_result(id)
45
- return present(result.value!, with: Entities::Alert) if result.success?
45
+ next present(result.value!, with: Entities::Alert) if result.success?
46
46
 
47
47
  case result.failure
48
48
  when ActiveRecord::RecordNotFound
@@ -62,7 +62,7 @@ module Mihari
62
62
  delete "/:id" do
63
63
  id = params["id"].to_i
64
64
  result = Services::AlertDestroyer.get_result(id)
65
- return if result.success?
65
+ next if result.success?
66
66
 
67
67
  case result.failure
68
68
  when ActiveRecord::RecordNotFound
@@ -87,7 +87,7 @@ module Mihari
87
87
  status 201
88
88
 
89
89
  result = Services::AlertCreator.get_result(params)
90
- return present(result.value!, with: Entities::Alert) if result.success?
90
+ next present(result.value!, with: Entities::Alert) if result.success?
91
91
 
92
92
  case result.failure
93
93
  when ActiveRecord::RecordNotFound
@@ -42,7 +42,7 @@ module Mihari
42
42
  get "/:id" do
43
43
  id = params[:id].to_i
44
44
  result = Services::ArtifactGetter.get_result(id)
45
- return present(result.value!, with: Entities::Artifact) if result.success?
45
+ next present(result.value!, with: Entities::Artifact) if result.success?
46
46
 
47
47
  case result.failure
48
48
  when ActiveRecord::RecordNotFound
@@ -75,7 +75,7 @@ module Mihari
75
75
  end.to_result
76
76
 
77
77
  message = queued ? "ID:#{id}'s enrichment is queued" : "ID:#{id}'s enrichment is successful"
78
- return present({message:, queued:}, with: Entities::QueueMessage) if result.success?
78
+ next present({message:, queued:}, with: Entities::QueueMessage) if result.success?
79
79
 
80
80
  case result.failure
81
81
  when UnenrichableError
@@ -99,7 +99,7 @@ module Mihari
99
99
 
100
100
  id = params["id"].to_i
101
101
  result = Services::ArtifactDestroyer.get_result(id)
102
- return if result.success?
102
+ next if result.success?
103
103
 
104
104
  case result.failure
105
105
  when ActiveRecord::RecordNotFound
@@ -24,7 +24,7 @@ module Mihari
24
24
  result = Services::IPGetter.get_result(ip)
25
25
  if result.success?
26
26
  value = result.value!
27
- return present(
27
+ next present(
28
28
  {
29
29
  country_code: value.country_code,
30
30
  asn: value.asn,
@@ -60,7 +60,7 @@ module Mihari
60
60
  get "/:id" do
61
61
  id = params[:id].to_s
62
62
  result = Services::RuleGetter.get_result(params[:id].to_s)
63
- return present(result.value!, with: Entities::Rule) if result.success?
63
+ next present(result.value!, with: Entities::Rule) if result.success?
64
64
 
65
65
  case result.failure
66
66
  when ActiveRecord::RecordNotFound
@@ -95,7 +95,7 @@ module Mihari
95
95
  end.to_result
96
96
 
97
97
  message = queued ? "ID:#{id}'s search is queued" : "ID:#{id}'s search is successful"
98
- return present({message:, queued:}, with: Entities::QueueMessage) if result.success?
98
+ next present({message:, queued:}, with: Entities::QueueMessage) if result.success?
99
99
 
100
100
  case result.failure
101
101
  when ActiveRecord::RecordNotFound
@@ -121,7 +121,7 @@ module Mihari
121
121
  yaml = params[:yaml].to_s
122
122
 
123
123
  result = RuleCreateUpdater.get_result(yaml, overwrite: false)
124
- return present(result.value!.model, with: Entities::Rule) if result.success?
124
+ next present(result.value!.model, with: Entities::Rule) if result.success?
125
125
 
126
126
  failure = result.failure
127
127
  case failure
@@ -152,7 +152,7 @@ module Mihari
152
152
  yaml = params[:yaml].to_s
153
153
 
154
154
  result = RuleCreateUpdater.get_result(yaml, overwrite: true)
155
- return present(result.value!.model, with: Entities::Rule) if result.success?
155
+ next present(result.value!.model, with: Entities::Rule) if result.success?
156
156
 
157
157
  failure = result.failure
158
158
  case failure
@@ -179,7 +179,7 @@ module Mihari
179
179
 
180
180
  id = params[:id].to_s
181
181
  result = Services::RuleDestroyer.get_result(id)
182
- return if result.success?
182
+ next if result.success?
183
183
 
184
184
  case result.failure
185
185
  when ActiveRecord::RecordNotFound
@@ -44,7 +44,7 @@ module Mihari
44
44
 
45
45
  id = params[:id].to_i
46
46
  result = Services::TagDestroyer.get_result(id)
47
- return if result.success?
47
+ next if result.success?
48
48
 
49
49
  case result.failure
50
50
  when ActiveRecord::RecordNotFound