mihari 7.0.3 → 7.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a5bc182d0f1c5e1ecfb08cd72d083c30a5775ea2890795dc812357da493ffb63
4
- data.tar.gz: 87fe1706c56301184b79f02d1d8be0b2deeb5591b8cfd76c3bea6d1a12005311
3
+ metadata.gz: aca048c797a73db1c57f0a5ad5f29066fbfba69451c71df1a26c3ea57853a1e3
4
+ data.tar.gz: 5ce2d298f325cc9d0ae50a98a6c18a64ec11cc06386504535c8ca647c7c68ad0
5
5
  SHA512:
6
- metadata.gz: 543e6850571451e862e95db1697482c39e796783728ddcb48dca6008b0a29e10bbe75dfabb4512dcf0b9c4ff6d31168138bc6285c2ff7131a82a2e61d5353ab1
7
- data.tar.gz: fc4e7fe111efa45d68a7b106cc61b5312f5c5be62fdc662e4daf2d3e466054deaf66eac08217f35680fbab0688eaad7642dc1a17df4bcf385b7b61c38eb31906
6
+ metadata.gz: ac1d6ab49351a2c9c60e703a5fe5fe0f94c84c4294271f297d529a781d3a87a37b515d090a78676bcaf3da225f03617f8900966d108e31557e86319a2407b00b
7
+ data.tar.gz: f5aad0f8648783f4d3c3cc42a087f646358e5e93586cb394a2644e8f6c32a296b3668ea7758348c0dd55d4868527b4aa6a3d774ee113d5a4a14c39ae3ecd2857
@@ -92,7 +92,15 @@ module Mihari
92
92
 
93
93
  return res.recover { [] } if ignore_error?
94
94
 
95
- res.to_result
95
+ result = res.to_result
96
+ return result if result.success?
97
+
98
+ # Wrap failure with AnalyzerError to explicitly name a failed analyzer
99
+ Failure AnalyzerError.new(
100
+ result.failure.message,
101
+ self.class.class_key,
102
+ cause: result.failure
103
+ )
96
104
  end
97
105
 
98
106
  class << self
@@ -41,17 +41,17 @@ module Mihari
41
41
  def safe_execute
42
42
  yield
43
43
  rescue StandardError => e
44
- err = unwrap_error(e)
44
+ error = unwrap_error(e)
45
45
 
46
- raise err if options["debug"]
46
+ raise error if options["debug"]
47
47
 
48
- case err
49
- when ValidationError
50
- warn JSON.pretty_generate(err.errors.to_h)
51
- when StandardError
52
- Sentry.capture_exception(err) if Sentry.initialized?
53
- warn err
54
- end
48
+ data = Entities::ErrorMessage.represent(
49
+ message: error.message,
50
+ detail: error.respond_to?(:detail) ? error.detail : nil
51
+ )
52
+ warn JSON.pretty_generate(data.as_json)
53
+
54
+ Sentry.capture_exception(error) if Sentry.initialized? && !error.is_a?(ValidationError)
55
55
 
56
56
  exit 1
57
57
  end
@@ -52,7 +52,10 @@ module Mihari
52
52
  def search(query, page:, size: PAGE_SIZE)
53
53
  qbase64 = Base64.urlsafe_encode64(query)
54
54
  params = { qbase64: qbase64, size: size, page: page, email: email, key: api_key }.compact
55
- Structs::Fofa::Response.from_dynamic! get_json("/api/v1/search/all", params: params)
55
+ res = Structs::Fofa::Response.from_dynamic!(get_json("/api/v1/search/all", params: params))
56
+ raise ResponseError, res.errmsg if res.error
57
+
58
+ res
56
59
  end
57
60
 
58
61
  #
@@ -34,8 +34,6 @@ module Mihari
34
34
  return if Pathname(path).exist? && !(yes? warning)
35
35
 
36
36
  Services::RuleInitializer.call(path)
37
-
38
- puts "A new rule file has been initialized: #{path}."
39
37
  end
40
38
 
41
39
  desc "list [QUERY]", "List/search rules"
@@ -17,9 +17,10 @@ module Mihari
17
17
  receiver = err.receiver
18
18
  case receiver
19
19
  when Dry::Monads::Try::Error
20
- receiver.exception
20
+ # Error may be wrapped like Matryoshka
21
+ unwrap_error receiver.exception
21
22
  when Dry::Monads::Failure
22
- receiver.failure
23
+ unwrap_error receiver.failure
23
24
  else
24
25
  err
25
26
  end
@@ -8,35 +8,48 @@ module Mihari
8
8
  module Retriable
9
9
  extend ActiveSupport::Concern
10
10
 
11
- DEFAULT_ON = [
12
- Errno::ECONNRESET,
13
- Errno::ECONNABORTED,
14
- Errno::EPIPE,
11
+ RETRIABLE_ERRORS = [
15
12
  OpenSSL::SSL::SSLError,
16
13
  Timeout::Error,
17
- RetryableError,
18
- NetworkError,
19
- TimeoutError,
20
- StatusCodeError
14
+ ::HTTP::ConnectionError,
15
+ ::HTTP::ResponseError,
16
+ ::HTTP::TimeoutError
21
17
  ].freeze
22
18
 
19
+ DEFAULT_CONDITION = lambda do |error|
20
+ return true if RETRIABLE_ERRORS.any? { |klass| error.is_a? klass }
21
+
22
+ case error
23
+ when StatusCodeError
24
+ error.status_code != 404
25
+ else
26
+ false
27
+ end
28
+ end
29
+
23
30
  #
24
31
  # Retry on error
25
32
  #
26
33
  # @param [Integer] times
27
34
  # @param [Integer] interval
28
35
  # @param [Boolean] exponential_backoff
29
- # @param [Array<StandardError>] on
36
+ # @param [Proc] condition
30
37
  #
31
- def retry_on_error(times: 3, interval: 5, exponential_backoff: true, on: DEFAULT_ON)
38
+ # @param [Object] on
39
+ def retry_on_error(times: 3, interval: 5, exponential_backoff: true, condition: DEFAULT_CONDITION)
32
40
  try = 0
33
41
  begin
34
42
  try += 1
35
43
  yield
36
- rescue *on => e
44
+ rescue StandardError => e
45
+ # Raise error if it's not a retriable error
46
+ raise e unless condition.call(e)
47
+
37
48
  sleep_seconds = exponential_backoff ? interval * (2**(try - 1)) : interval
38
49
  sleep sleep_seconds
39
50
  retry if try < times
51
+
52
+ # Raise error if retry times exceed a given times
40
53
  raise e
41
54
  end
42
55
  end
data/lib/mihari/errors.rb CHANGED
@@ -1,27 +1,45 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "http"
4
+
3
5
  module Mihari
4
6
  class Error < StandardError; end
5
7
 
6
8
  class ValueError < Error; end
7
9
 
8
- class RetryableError < Error; end
9
-
10
10
  class ConfigurationError < Error; end
11
11
 
12
- class IntegrityError < Error; end
12
+ class ResponseError < Error; end
13
13
 
14
- # errors for HTTP interactions
15
- class HTTPError < Error; end
14
+ class AnalyzerError < Error
15
+ # @return [StandardException, nil]
16
+ attr_reader :cause
16
17
 
17
- class NetworkError < HTTPError; end
18
+ #
19
+ # @param [String] msg
20
+ # @param [String] analyzer
21
+ # @param [StandardException, nil] cause
22
+ #
23
+ def initialize(msg, analyzer, cause: nil)
24
+ super("#{msg} (from #{analyzer})")
25
+
26
+ @cause = cause
27
+ set_backtrace(cause.backtrace) if cause
28
+ end
18
29
 
19
- class TimeoutError < HTTPError; end
30
+ def detail
31
+ return nil unless cause.respond_to?(:detail)
32
+
33
+ cause.detail
34
+ end
35
+ end
36
+
37
+ class IntegrityError < Error; end
20
38
 
21
39
  #
22
40
  # HTTP status code error
23
41
  #
24
- class StatusCodeError < HTTPError
42
+ class StatusCodeError < ::HTTP::Error
25
43
  # @return [Integer]
26
44
  attr_reader :status_code
27
45
 
@@ -39,6 +57,10 @@ module Mihari
39
57
  @status_code = status_code
40
58
  @body = body
41
59
  end
60
+
61
+ def detail
62
+ { status_code: status_code, body: body }
63
+ end
42
64
  end
43
65
 
44
66
  #
@@ -56,5 +78,9 @@ module Mihari
56
78
 
57
79
  @errors = errors
58
80
  end
81
+
82
+ def detail
83
+ errors.to_h
84
+ end
59
85
  end
60
86
  end
data/lib/mihari/http.rb CHANGED
@@ -18,11 +18,6 @@ module Mihari
18
18
  )
19
19
  end
20
20
 
21
- def on_error(_request, error)
22
- raise TimeoutError, error if error.is_a?(::HTTP::TimeoutError)
23
- raise NetworkError, error if error.is_a?(::HTTP::Error)
24
- end
25
-
26
21
  ::HTTP::Options.register_feature(:better_error, self)
27
22
  end
28
23
 
data/lib/mihari/rule.rb CHANGED
@@ -83,6 +83,20 @@ module Mihari
83
83
  data[:data_types]
84
84
  end
85
85
 
86
+ #
87
+ # @return [Date, nil]
88
+ #
89
+ def created_on
90
+ data[:created_on]
91
+ end
92
+
93
+ #
94
+ # @return [Date, nil]
95
+ #
96
+ def updated_on
97
+ data[:updated_on]
98
+ end
99
+
86
100
  #
87
101
  # @return [Array<Mihari::Models::Tag>]
88
102
  #
@@ -231,7 +245,7 @@ module Mihari
231
245
  #
232
246
  def diff?
233
247
  model = Mihari::Models::Rule.find(id)
234
- model.data != data.deep_stringify_keys
248
+ model.data != diff_comparable_data
235
249
  rescue ActiveRecord::RecordNotFound
236
250
  false
237
251
  end
@@ -272,6 +286,18 @@ module Mihari
272
286
 
273
287
  private
274
288
 
289
+ #
290
+ # @return [Hash]
291
+ #
292
+ def diff_comparable_data
293
+ # data is serialized as JSON so dates (created_on & updated_on) are stringified in there
294
+ # thus dates & (hash) keys have to be stringified when comparing
295
+ data.deep_dup.tap do |data|
296
+ data[:created_on] = created_on.to_s unless created_on.nil?
297
+ data[:updated_on] = updated_on.to_s unless updated_on.nil?
298
+ end.deep_stringify_keys
299
+ end
300
+
275
301
  #
276
302
  # Check whether a value is a falsepositive value or not
277
303
  #
@@ -8,6 +8,10 @@ module Mihari
8
8
  # @return [Boolean]
9
9
  attribute :error, Types::Bool
10
10
 
11
+ # @!attribute [r] errmsg
12
+ # @return [String, nil]
13
+ attribute? :errmsg, Types::String.optional
14
+
11
15
  # @!attribute [r] size
12
16
  # @return [Integer, nil]
13
17
  attribute? :size, Types::Int.optional
@@ -35,6 +39,7 @@ module Mihari
35
39
  def from_dynamic!(d)
36
40
  new(
37
41
  error: d.fetch("error"),
42
+ errmsg: d["errmsg"],
38
43
  size: d["size"],
39
44
  page: d["page"],
40
45
  mode: d["mode"],
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- VERSION = "7.0.3"
4
+ VERSION = "7.0.5"
5
5
  end
@@ -8,13 +8,16 @@ module Mihari
8
8
  #
9
9
  class IPAddresses < Grape::API
10
10
  namespace :ip_addresses do
11
- desc "Get an IP address", {
11
+ desc "Get IP address data", {
12
12
  success: Entities::IPAddress,
13
- failure: [{ code: 404, model: Entities::ErrorMessage }],
14
- summary: "Get an IP address"
13
+ failure: [
14
+ { code: 404, model: Entities::ErrorMessage },
15
+ { code: 422, model: Entities::ErrorMessage }
16
+ ],
17
+ summary: "Get IP address data"
15
18
  }
16
19
  params do
17
- requires :ip, type: String, regexp: /\A[0-9.]+\z/
20
+ requires :ip, type: String
18
21
  end
19
22
  get "/:ip", requirements: { ip: %r{[^/]+} } do
20
23
  ip = params[:ip].to_s
@@ -34,7 +37,8 @@ module Mihari
34
37
  failure = result.failure
35
38
  case failure
36
39
  when Mihari::StatusCodeError
37
- error!({ message: "ID:#{id} not found" }, 404) if failure.status_code == 404
40
+ error!({ message: "IP:#{ip} not found" }, failure.status_code) if failure.status_code == 404
41
+ error!({ message: "IP format invalid" }, failure.status_code) if failure.status_code == 422
38
42
  end
39
43
  raise failure
40
44
  end
@@ -17,7 +17,10 @@ module Mihari
17
17
  def call(yaml, overwrite: true)
18
18
  rule = Rule.from_yaml(yaml)
19
19
 
20
- raise IntegrityError, "ID:#{rule.id} is already registered" if rule.exists? && !overwrite
20
+ # To invoke ActiveRecord::RecordNotFound
21
+ Models::Rule.find(rule.id) if overwrite
22
+
23
+ raise IntegrityError, "ID:#{rule.id} already registered" if rule.exists? && !overwrite
21
24
 
22
25
  rule.update_or_create
23
26
  rule
@@ -141,13 +144,11 @@ module Mihari
141
144
  summary: "Update a rule"
142
145
  }
143
146
  params do
144
- requires :id, type: String, documentation: { param_type: "body" }
145
147
  requires :yaml, type: String, documentation: { param_type: "body" }
146
148
  end
147
149
  put "/" do
148
150
  status 201
149
151
 
150
- id = params[:id].to_s
151
152
  yaml = params[:yaml].to_s
152
153
 
153
154
  result = RuleCreateUpdater.result(yaml, overwrite: true)
@@ -156,11 +157,11 @@ module Mihari
156
157
  failure = result.failure
157
158
  case failure
158
159
  when ActiveRecord::RecordNotFound
159
- error!({ message: "ID:#{id} not found" }, 404)
160
+ error!({ message: "Rule not found" }, 404)
160
161
  when Psych::SyntaxError
161
162
  error!({ message: failure.message }, 422)
162
163
  when ValidationError
163
- error!({ message: "Rule format is invalid", detail: failure.errors.to_h }, 422)
164
+ error!({ message: "Rule format invalid", detail: failure.errors.to_h }, 422)
164
165
  end
165
166
  raise failure
166
167
  end
@@ -9,8 +9,6 @@ module Mihari
9
9
  class CaptureExceptions < Sentry::Rack::CaptureExceptions
10
10
  include Concerns::ErrorUnwrappable
11
11
 
12
- private
13
-
14
12
  def capture_exception(exception, env)
15
13
  unwrapped = unwrap_error(exception)
16
14
  Mihari.logger.error unwrapped