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
@@ -11,14 +11,20 @@ module Mihari
11
11
  # @param [String] base_url
12
12
  # @param [String, nil] api_key
13
13
  # @param [Hash] headers
14
- # @param [Integer, nil] interval
14
+ # @param [Integer] pagination_interval
15
15
  # @param [Integer, nil] timeout
16
16
  #
17
- def initialize(base_url = "https://api.zoomeye.org", api_key:, headers: {}, interval: nil, timeout: nil)
17
+ def initialize(
18
+ base_url = "https://api.zoomeye.org",
19
+ api_key:,
20
+ headers: {},
21
+ pagination_interval: 0,
22
+ timeout: nil
23
+ )
18
24
  raise(ArgumentError, "'api_key' argument is required") unless api_key
19
25
 
20
26
  headers["api-key"] = api_key
21
- super(base_url, headers: headers, interval: interval, timeout: timeout)
27
+ super(base_url, headers: headers, pagination_interval: pagination_interval, timeout: timeout)
22
28
  end
23
29
 
24
30
  #
@@ -59,7 +65,7 @@ module Mihari
59
65
  total = res["total"].to_i
60
66
  break if total <= page * PAGE_SIZE
61
67
 
62
- sleep_interval
68
+ sleep_pagination_interval
63
69
  end
64
70
  end
65
71
  end
@@ -102,7 +108,7 @@ module Mihari
102
108
  total = res["total"].to_i
103
109
  break if total <= page * PAGE_SIZE
104
110
 
105
- sleep_interval
111
+ sleep_pagination_interval
106
112
  end
107
113
  end
108
114
  end
data/lib/mihari/config.rb CHANGED
@@ -90,6 +90,12 @@ module Mihari
90
90
  # @return [Integer]
91
91
  attr_reader :retry_times
92
92
 
93
+ # @return [Boolean]
94
+ attr_reader :retry_exponential_backoff
95
+
96
+ # @return [Integer]
97
+ attr_reader :pagination_interval
98
+
93
99
  # @return [Integer]
94
100
  attr_reader :pagination_limit
95
101
 
@@ -152,7 +158,9 @@ module Mihari
152
158
 
153
159
  @retry_times = ENV.fetch("RETRY_TIMES", 3).to_i
154
160
  @retry_interval = ENV.fetch("RETRY_INTERVAL", 5).to_i
161
+ @retry_exponential_backoff = ENV.fetch("RETRY_EXPONENTIAL_BACKOFF", true).to_s.downcase == "true"
155
162
 
163
+ @pagination_interval = ENV.fetch("PAGINATION_INTERVAL", 0).to_i
156
164
  @pagination_limit = ENV.fetch("PAGINATION_LIMIT", 100).to_i
157
165
 
158
166
  @ignore_error = ENV.fetch("IGNORE_ERROR", false)
@@ -14,39 +14,76 @@ module Mihari
14
14
  # @return [Mihari::Services::Rule]
15
15
  attr_reader :rule
16
16
 
17
+ # @return [Hash]
18
+ attr_reader :options
19
+
17
20
  #
18
21
  # @param [Array<Mihari::Artifact>] artifacts
19
22
  # @param [Mihari::Services::RuleProxy] rule
20
- # @param [Hash] **_options
23
+ # @param [Hash, nil] options
24
+ # @param [Hash] **_params
21
25
  #
22
- def initialize(artifacts:, rule:, **_options)
26
+ def initialize(artifacts:, rule:, options: nil, **_params)
23
27
  @artifacts = artifacts
24
28
  @rule = rule
29
+ @options = options || {}
25
30
  end
26
31
 
27
- class << self
28
- def inherited(child)
29
- super
30
- Mihari.emitters << child
31
- end
32
+ #
33
+ # @return [Integer]
34
+ #
35
+ def retry_interval
36
+ options[:retry_interval] || Mihari.config.retry_interval
32
37
  end
33
38
 
39
+ #
34
40
  # @return [Boolean]
35
- def valid?
36
- raise NotImplementedError, "You must implement #{self.class}##{__method__}"
41
+ #
42
+ def retry_exponential_backoff
43
+ options[:retry_exponential_backoff] || Mihari.config.retry_exponential_backoff
44
+ end
45
+
46
+ #
47
+ # @return [Integer]
48
+ #
49
+ def retry_times
50
+ options[:retry_times] || Mihari.config.retry_times
51
+ end
52
+
53
+ #
54
+ # @return [Integer, nil]
55
+ #
56
+ def timeout
57
+ options[:timeout]
37
58
  end
38
59
 
39
- def run
40
- retry_on_error { emit }
60
+ # @return [Boolean]
61
+ def valid?
62
+ raise NotImplementedError, "You must implement #{self.class}##{__method__}"
41
63
  end
42
64
 
43
65
  def result
44
- Try[StandardError] { run }.to_result
66
+ Try[StandardError] do
67
+ retry_on_error(
68
+ times: retry_times,
69
+ interval: retry_interval,
70
+ exponential_backoff: retry_exponential_backoff
71
+ ) do
72
+ emit
73
+ end
74
+ end.to_result
45
75
  end
46
76
 
47
77
  def emit
48
78
  raise NotImplementedError, "You must implement #{self.class}##{__method__}"
49
79
  end
80
+
81
+ class << self
82
+ def inherited(child)
83
+ super
84
+ Mihari.emitters << child
85
+ end
86
+ end
50
87
  end
51
88
  end
52
89
  end
@@ -18,13 +18,14 @@ module Mihari
18
18
  #
19
19
  # @param [Array<Mihari::Artifact>] artifacts
20
20
  # @param [Mihari::Services::Rule] rule
21
- # @param [Hash] **options
21
+ # @param [Hash, nil] options
22
+ # @param [Hash] **params
22
23
  #
23
- def initialize(artifacts:, rule:, **options)
24
- super(artifacts: artifacts, rule: rule, **options)
24
+ def initialize(artifacts:, rule:, options: nil, **params)
25
+ super(artifacts: artifacts, rule: rule, options: options)
25
26
 
26
- @url = options[:url] || Mihari.config.misp_url
27
- @api_key = options[:api_key] || Mihari.config.misp_api_key
27
+ @url = params[:url] || Mihari.config.misp_url
28
+ @api_key = params[:api_key] || Mihari.config.misp_api_key
28
29
  end
29
30
 
30
31
  # @return [Boolean]
@@ -70,7 +71,7 @@ module Mihari
70
71
  end
71
72
 
72
73
  def client
73
- @client ||= Clients::MISP.new(url, api_key: api_key)
74
+ @client ||= Clients::MISP.new(url, api_key: api_key, timeout: timeout)
74
75
  end
75
76
 
76
77
  #
@@ -134,13 +134,14 @@ module Mihari
134
134
  #
135
135
  # @param [Array<Mihari::Artifact>] artifacts
136
136
  # @param [Mihari::Services::Rule] rule
137
- # @param [Hash] **_options
137
+ # @param [Hash, nil] options
138
+ # @param [Hash] **params
138
139
  #
139
- def initialize(artifacts:, rule:, **options)
140
- super(artifacts: artifacts, rule: rule, **options)
140
+ def initialize(artifacts:, rule:, options: nil, **params)
141
+ super(artifacts: artifacts, rule: rule, options: options)
141
142
 
142
- @webhook_url = options[:webhook_url] || Mihari.config.slack_webhook_url
143
- @channel = options[:channel] || Mihari.config.slack_channel || DEFAULT_CHANNEL
143
+ @webhook_url = params[:webhook_url] || Mihari.config.slack_webhook_url
144
+ @channel = params[:channel] || Mihari.config.slack_channel || DEFAULT_CHANNEL
144
145
  @username = DEFAULT_USERNAME
145
146
  end
146
147
 
@@ -162,8 +163,25 @@ module Mihari
162
163
  webhook_url?
163
164
  end
164
165
 
166
+ #
167
+ # @return [::Slack::Notifier]
168
+ #
165
169
  def notifier
166
- @notifier ||= ::Slack::Notifier.new(webhook_url, channel: channel, username: username)
170
+ @notifier ||= [].tap do |out|
171
+ out << if timeout.nil?
172
+ ::Slack::Notifier.new(
173
+ webhook_url,
174
+ channel: channel, username: username
175
+ )
176
+ else
177
+ ::Slack::Notifier.new(
178
+ webhook_url,
179
+ channel: channel,
180
+ username: username,
181
+ http_options: { timeout: timeout }
182
+ )
183
+ end
184
+ end.first
167
185
  end
168
186
 
169
187
  #
@@ -15,14 +15,15 @@ module Mihari
15
15
  #
16
16
  # @param [Array<Mihari::Artifact>] artifacts
17
17
  # @param [Mihari::Services::Rule] rule
18
- # @param [Hash] **options
18
+ # @param [Hash, nil] options
19
+ # @param [Hash] **params
19
20
  #
20
- def initialize(artifacts:, rule:, **options)
21
- super(artifacts: artifacts, rule: rule, **options)
21
+ def initialize(artifacts:, rule:, options: nil, **params)
22
+ super(artifacts: artifacts, rule: rule, options: options)
22
23
 
23
- @url = options[:url] || Mihari.config.thehive_url
24
- @api_key = options[:api_key] || Mihari.config.thehive_api_key
25
- @api_version = options[:api_version] || Mihari.config.thehive_api_version
24
+ @url = params[:url] || Mihari.config.thehive_url
25
+ @api_key = params[:api_key] || Mihari.config.thehive_api_key
26
+ @api_version = params[:api_version] || Mihari.config.thehive_api_version
26
27
  end
27
28
 
28
29
  # @return [Boolean]
@@ -79,7 +80,7 @@ module Mihari
79
80
  end
80
81
 
81
82
  def client
82
- @client ||= Clients::TheHive.new(url, api_key: api_key, api_version: normalized_api_version)
83
+ @client ||= Clients::TheHive.new(url, api_key: api_key, api_version: normalized_api_version, timeout: timeout)
83
84
  end
84
85
 
85
86
  #
@@ -5,28 +5,30 @@ require "erb"
5
5
  module Mihari
6
6
  module Emitters
7
7
  class PayloadTemplate < ERB
8
- def self.template
9
- %{
10
- {
11
- "rule": {
12
- "id": "<%= @rule.id %>",
13
- "title": "<%= @rule.title %>",
14
- "description": "<%= @rule.description %>"
15
- },
16
- "artifacts": [
17
- <% @artifacts.each_with_index do |artifact, idx| %>
18
- "<%= artifact.data %>"
19
- <%= ',' if idx < (@artifacts.length - 1) %>
20
- <% end %>
21
- ],
22
- "tags": [
23
- <% @rule.tags.each_with_index do |tag, idx| %>
24
- "<%= tag %>"
25
- <%= ',' if idx < (@rule.tags.length - 1) %>
26
- <% end %>
27
- ]
28
- }
29
- }
8
+ class << self
9
+ def template
10
+ %{
11
+ {
12
+ "rule": {
13
+ "id": "<%= @rule.id %>",
14
+ "title": "<%= @rule.title %>",
15
+ "description": "<%= @rule.description %>"
16
+ },
17
+ "artifacts": [
18
+ <% @artifacts.each_with_index do |artifact, idx| %>
19
+ "<%= artifact.data %>"
20
+ <%= ',' if idx < (@artifacts.length - 1) %>
21
+ <% end %>
22
+ ],
23
+ "tags": [
24
+ <% @rule.tags.each_with_index do |tag, idx| %>
25
+ "<%= tag %>"
26
+ <%= ',' if idx < (@rule.tags.length - 1) %>
27
+ <% end %>
28
+ ]
29
+ }
30
+ }
31
+ end
30
32
  end
31
33
 
32
34
  def initialize(artifacts:, rule:, options: {})
@@ -60,13 +62,13 @@ module Mihari
60
62
  # @param [Mihari::Services::Rule] rule
61
63
  # @param [Hash] **options
62
64
  #
63
- def initialize(artifacts:, rule:, **options)
64
- super(artifacts: artifacts, rule: rule, **options)
65
+ def initialize(artifacts:, rule:, options: nil, **params)
66
+ super(artifacts: artifacts, rule: rule, options: options)
65
67
 
66
- @url = Addressable::URI.parse(options[:url])
67
- @headers = options[:headers] || {}
68
- @method = options[:method] || "POST"
69
- @template = options[:template]
68
+ @url = Addressable::URI.parse(params[:url])
69
+ @headers = params[:headers] || {}
70
+ @method = params[:method] || "POST"
71
+ @template = params[:template]
70
72
  end
71
73
 
72
74
  def emit
@@ -90,7 +92,7 @@ module Mihari
90
92
  private
91
93
 
92
94
  def http
93
- HTTP::Factory.build headers: headers
95
+ HTTP::Factory.build headers: headers, timeout: timeout
94
96
  end
95
97
 
96
98
  #
@@ -4,31 +4,68 @@ module Mihari
4
4
  module Enrichers
5
5
  class Base
6
6
  include Mixins::Configurable
7
+ include Mixins::Retriable
7
8
 
8
- class << self
9
- include Dry::Monads[:result, :try]
9
+ include Dry::Monads[:result, :try]
10
10
 
11
- def inherited(child)
12
- super
13
- Mihari.enrichers << child
14
- end
11
+ # @return [Hash]
12
+ attr_reader :options
15
13
 
16
- def query_result(value)
17
- Try[StandardError] { query(value) }.to_result
18
- end
14
+ def initialize(options: nil)
15
+ @options = options || {}
16
+ end
19
17
 
20
- #
21
- # @param [String] value
22
- #
23
- def query(value)
24
- raise NotImplementedError, "You must implement #{self.class}##{__method__}"
25
- end
18
+ #
19
+ # @return [Integer]
20
+ #
21
+ def retry_interval
22
+ options[:retry_interval] || Mihari.config.retry_interval
26
23
  end
27
24
 
25
+ #
28
26
  # @return [Boolean]
29
- def valid?
27
+ #
28
+ def retry_exponential_backoff
29
+ options[:retry_exponential_backoff] || Mihari.config.retry_exponential_backoff
30
+ end
31
+
32
+ #
33
+ # @return [Integer]
34
+ #
35
+ def retry_times
36
+ options[:retry_times] || Mihari.config.retry_times
37
+ end
38
+
39
+ #
40
+ # @return [Integer, nil]
41
+ #
42
+ def timeout
43
+ options[:timeout]
44
+ end
45
+
46
+ def query_result(value)
47
+ Try[StandardError] do
48
+ retry_on_error(
49
+ times: retry_times,
50
+ interval: retry_interval,
51
+ exponential_backoff: retry_exponential_backoff
52
+ ) { query value }
53
+ end.to_result
54
+ end
55
+
56
+ #
57
+ # @param [String] value
58
+ #
59
+ def query(value)
30
60
  raise NotImplementedError, "You must implement #{self.class}##{__method__}"
31
61
  end
62
+
63
+ class << self
64
+ def inherited(child)
65
+ super
66
+ Mihari.enrichers << child
67
+ end
68
+ end
32
69
  end
33
70
  end
34
71
  end
@@ -5,52 +5,43 @@ require "net/https"
5
5
  module Mihari
6
6
  module Enrichers
7
7
  class GooglePublicDNS < Base
8
- # @return [Boolean]
9
- def valid?
10
- true
11
- end
12
-
13
- class << self
14
- include Dry::Monads[:result]
15
-
16
- #
17
- # Query Google Public DNS
18
- #
19
- # @param [String] name
20
- #
21
- # @return [Array<Mihari::Structs::Shodan::GooglePublicDNS::Response>]
22
- #
23
- def query(name)
24
- %w[A AAAA CNAME TXT NS].filter_map do |resource_type|
25
- query_by_type(name, resource_type)
26
- end
8
+ #
9
+ # Query Google Public DNS
10
+ #
11
+ # @param [String] name
12
+ #
13
+ # @return [Array<Mihari::Structs::Shodan::GooglePublicDNS::Response>]
14
+ #
15
+ def query(name)
16
+ %w[A AAAA CNAME TXT NS].filter_map do |resource_type|
17
+ query_by_type(name, resource_type)
27
18
  end
19
+ end
28
20
 
29
- #
30
- # Query Google Public DNS by resource type
31
- #
32
- # @param [String] name
33
- # @param [String] resource_type
34
- #
35
- # @return [Mihari::Structs::Shodan::GooglePublicDNS::Response, nil]
36
- #
37
- def query_by_type(name, resource_type)
38
- url = "https://dns.google/resolve"
39
- params = { name: name, type: resource_type }
40
- res = http.get(url, params: params)
41
-
42
- data = JSON.parse(res.body.to_s)
43
-
44
- Structs::GooglePublicDNS::Response.from_dynamic! data
45
- rescue HTTPError
46
- nil
47
- end
21
+ #
22
+ # Query Google Public DNS by resource type
23
+ #
24
+ # @param [String] name
25
+ # @param [String] resource_type
26
+ #
27
+ # @return [Mihari::Structs::Shodan::GooglePublicDNS::Response, nil]
28
+ #
29
+ def query_by_type(name, resource_type)
30
+ url = "https://dns.google/resolve"
31
+ params = { name: name, type: resource_type }
32
+ res = http.get(url, params: params)
33
+
34
+ data = JSON.parse(res.body.to_s)
35
+
36
+ Structs::GooglePublicDNS::Response.from_dynamic! data
37
+ rescue HTTPError
38
+ nil
39
+ end
48
40
 
49
- private
41
+ private
50
42
 
51
- def http
52
- HTTP::Factory.build
53
- end
43
+ def http
44
+ HTTP::Factory.build timeout: timeout
54
45
  end
55
46
  end
56
47
  end
@@ -5,9 +5,15 @@ require "net/https"
5
5
  module Mihari
6
6
  module Enrichers
7
7
  class IPInfo < Base
8
- # @return [Boolean]
9
- def valid?
10
- Mihari.config.ipinfo_api_key.nil?
8
+ include Memist::Memoizable
9
+
10
+ # @return [String, nil]
11
+ attr_reader :api_key
12
+
13
+ def initialize(options: nil, api_key: nil)
14
+ @api_key = api_key || Mihari.config.ipinfo_api_key
15
+
16
+ super(options: options)
11
17
  end
12
18
 
13
19
  private
@@ -16,37 +22,29 @@ module Mihari
16
22
  %w[ipinfo_api_key]
17
23
  end
18
24
 
19
- class << self
20
- include Dry::Monads[:result]
21
- include Memist::Memoizable
22
-
23
- #
24
- # Query IPInfo
25
- #
26
- # @param [String] ip
27
- #
28
- # @return [Mihari::Structs::IPInfo::Response, nil]
29
- #
30
- def query(ip)
31
- url = "https://ipinfo.io/#{ip}/json"
32
- res = http.get(url)
33
- data = JSON.parse(res.body.to_s)
34
-
35
- Structs::IPInfo::Response.from_dynamic! data
36
- end
37
- memoize :query
38
-
39
- private
40
-
41
- def headers
42
- token = Mihari.config.ipinfo_api_key
43
- authorization = token.nil? ? nil : "Bearer #{token}"
44
- { authorization: authorization }.compact
45
- end
46
-
47
- def http
48
- HTTP::Factory.build headers: headers
49
- end
25
+ #
26
+ # Query IPInfo
27
+ #
28
+ # @param [String] ip
29
+ #
30
+ # @return [Mihari::Structs::IPInfo::Response, nil]
31
+ #
32
+ def query(ip)
33
+ url = "https://ipinfo.io/#{ip}/json"
34
+ res = http.get(url)
35
+ data = JSON.parse(res.body.to_s)
36
+
37
+ Structs::IPInfo::Response.from_dynamic! data
38
+ end
39
+ memoize :query
40
+
41
+ def headers
42
+ authorization = api_key.nil? ? nil : "Bearer #{api_key}"
43
+ { authorization: authorization }.compact
44
+ end
45
+
46
+ def http
47
+ HTTP::Factory.build headers: headers, timeout: timeout
50
48
  end
51
49
  end
52
50
  end
@@ -5,36 +5,28 @@ require "net/https"
5
5
  module Mihari
6
6
  module Enrichers
7
7
  class Shodan < Base
8
- # @return [Boolean]
9
- def valid?
10
- true
11
- end
12
-
13
- class << self
14
- include Dry::Monads[:result]
15
- include Memist::Memoizable
8
+ include Memist::Memoizable
16
9
 
17
- #
18
- # Query Shodan Internet DB
19
- #
20
- # @param [String] ip
21
- #
22
- # @return [Mihari::Structs::Shodan::InternetDBResponse, nil]
23
- #
24
- def query(ip)
25
- url = "https://internetdb.shodan.io/#{ip}"
26
- res = http.get(url)
27
- data = JSON.parse(res.body.to_s)
10
+ #
11
+ # Query Shodan Internet DB
12
+ #
13
+ # @param [String] ip
14
+ #
15
+ # @return [Mihari::Structs::Shodan::InternetDBResponse, nil]
16
+ #
17
+ def query(ip)
18
+ url = "https://internetdb.shodan.io/#{ip}"
19
+ res = http.get(url)
20
+ data = JSON.parse(res.body.to_s)
28
21
 
29
- Structs::Shodan::InternetDBResponse.from_dynamic! data
30
- end
31
- memoize :query
22
+ Structs::Shodan::InternetDBResponse.from_dynamic! data
23
+ end
24
+ memoize :query
32
25
 
33
- private
26
+ private
34
27
 
35
- def http
36
- HTTP::Factory.build
37
- end
28
+ def http
29
+ HTTP::Factory.build timeout: timeout
38
30
  end
39
31
  end
40
32
  end