mihari 5.1.0 → 5.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.gitmodules +0 -3
  3. data/.rubocop.yml +6 -0
  4. data/README.md +0 -1
  5. data/lib/mihari/analyzers/base.rb +32 -27
  6. data/lib/mihari/analyzers/binaryedge.rb +17 -9
  7. data/lib/mihari/analyzers/censys.rb +10 -54
  8. data/lib/mihari/analyzers/circl.rb +7 -6
  9. data/lib/mihari/analyzers/crtsh.rb +12 -7
  10. data/lib/mihari/analyzers/dnstwister.rb +7 -7
  11. data/lib/mihari/analyzers/feed.rb +33 -10
  12. data/lib/mihari/analyzers/greynoise.rb +8 -33
  13. data/lib/mihari/analyzers/onyphe.rb +10 -36
  14. data/lib/mihari/analyzers/otx.rb +4 -3
  15. data/lib/mihari/analyzers/passivetotal.rb +8 -7
  16. data/lib/mihari/analyzers/pulsedive.rb +8 -7
  17. data/lib/mihari/analyzers/rule.rb +0 -1
  18. data/lib/mihari/analyzers/securitytrails.rb +8 -10
  19. data/lib/mihari/analyzers/shodan.rb +16 -90
  20. data/lib/mihari/analyzers/urlscan.rb +16 -6
  21. data/lib/mihari/analyzers/virustotal.rb +8 -6
  22. data/lib/mihari/analyzers/virustotal_intelligence.rb +12 -7
  23. data/lib/mihari/analyzers/zoomeye.rb +13 -10
  24. data/lib/mihari/clients/base.rb +53 -0
  25. data/lib/mihari/clients/binaryedge.rb +38 -0
  26. data/lib/mihari/clients/censys.rb +42 -0
  27. data/lib/mihari/clients/circl.rb +59 -0
  28. data/lib/mihari/clients/crtsh.rb +31 -0
  29. data/lib/mihari/clients/dnstwister.rb +40 -0
  30. data/lib/mihari/clients/greynoise.rb +34 -0
  31. data/lib/mihari/clients/misp.rb +29 -0
  32. data/lib/mihari/clients/onyphe.rb +35 -0
  33. data/lib/mihari/clients/otx.rb +49 -0
  34. data/lib/mihari/clients/passivetotal.rb +69 -0
  35. data/lib/mihari/clients/publsedive.rb +56 -0
  36. data/lib/mihari/clients/securitytrails.rb +94 -0
  37. data/lib/mihari/clients/shodan.rb +41 -0
  38. data/lib/mihari/clients/the_hive.rb +33 -0
  39. data/lib/mihari/clients/urlscan.rb +33 -0
  40. data/lib/mihari/clients/virustotal.rb +62 -0
  41. data/lib/mihari/clients/zoomeye.rb +74 -0
  42. data/lib/mihari/commands/database.rb +1 -6
  43. data/lib/mihari/commands/searcher.rb +1 -2
  44. data/lib/mihari/database.rb +9 -0
  45. data/lib/mihari/emitters/misp.rb +13 -20
  46. data/lib/mihari/emitters/the_hive.rb +3 -5
  47. data/lib/mihari/emitters/webhook.rb +2 -2
  48. data/lib/mihari/feed/reader.rb +14 -11
  49. data/lib/mihari/http.rb +29 -21
  50. data/lib/mihari/mixins/retriable.rb +3 -1
  51. data/lib/mihari/schemas/analyzer.rb +5 -4
  52. data/lib/mihari/structs/censys.rb +62 -0
  53. data/lib/mihari/structs/greynoise.rb +43 -0
  54. data/lib/mihari/structs/onyphe.rb +45 -0
  55. data/lib/mihari/structs/shodan.rb +83 -0
  56. data/lib/mihari/version.rb +1 -1
  57. data/lib/mihari/web/middleware/connection_adapter.rb +1 -3
  58. data/lib/mihari/web/public/assets/{index-63900d73.js → index-7d0fb8c4.js} +2 -2
  59. data/lib/mihari/web/public/index.html +1 -1
  60. data/lib/mihari/web/public/redoc-static.html +2 -2
  61. data/lib/mihari.rb +21 -2
  62. data/mihari.gemspec +15 -23
  63. metadata +55 -264
  64. data/lib/mihari/analyzers/clients/otx.rb +0 -36
  65. data/lib/mihari/analyzers/dnpedia.rb +0 -37
  66. data/lib/mihari/mixins/database.rb +0 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9481229c15756515053bbc91453db90d914e40e5f536d0b5aaad0f2b8fff5868
4
- data.tar.gz: 1ea1f9caa3dbb1f945f4a3ba9f9af17c80c786594692de1564ca4f76692fa628
3
+ metadata.gz: 5415ee0f5bb820e8073383e4771589bbbaa39fc313af3aa434c5754f34bb9056
4
+ data.tar.gz: a4da4ce859fa718c900572b865518110f92649698a76ca64c55e38ebad2f9856
5
5
  SHA512:
6
- metadata.gz: f13bb894b69d4c92b03afc30fcce1be95ba1af1b0cb963c553a328fb85f771c852ee5526af751a098dd4f1b69c60592f021a0e3ee7e40004856d1d4eabc53aba
7
- data.tar.gz: 751ad7fc17f2db38961e835f8c9b2a325cbf82692df65ef29c638bcf3eec9cbc17905a39bcbcf5a53bcf477adbf6080a23aa40702b3fb5a55727038eefe9b09d
6
+ metadata.gz: c0b95e672f17d5ba0035624b035da278c863dc0b1a0860e3393cd8f001125964270233da7bf5ffb08c9057889b4a51698829de059089ed4d31a0f4fd7d0aa3e8
7
+ data.tar.gz: 30cbba0170104212e9244dfeac820a1a223468bae940719c242778d5bffd95b147f482548aebcc2386713fe43cc0c1b97bf2d71d08d318ad62a4b1a707f3e605
data/.gitmodules CHANGED
@@ -1,3 +0,0 @@
1
- [submodule "vendor/rbs/gem_rbs_collection"]
2
- path = vendor/rbs/gem_rbs_collection
3
- url = https://github.com/ruby/gem_rbs_collection.git
data/.rubocop.yml CHANGED
@@ -2,3 +2,9 @@ Style/HashSyntax:
2
2
  EnforcedShorthandSyntax: either
3
3
  Style/StringLiterals:
4
4
  EnforcedStyle: double_quotes
5
+ Metrics/BlockLength:
6
+ Exclude:
7
+ - "spec/**/*"
8
+ - "*.gemspec"
9
+ Metrics/ClassLength:
10
+ Enabled: false
data/README.md CHANGED
@@ -45,7 +45,6 @@ Mihari supports the following services by default.
45
45
  - [Censys](http://censys.io)
46
46
  - [CIRCL passive DNS](https://www.circl.lu/services/passive-dns/) / [passive SSL](https://www.circl.lu/services/passive-ssl/)
47
47
  - [crt.sh](https://crt.sh/)
48
- - [DN Pedia](https://dnpedia.com/)
49
48
  - [dnstwister](https://dnstwister.report/)
50
49
  - [GreyNoise](https://www.greynoise.io/)
51
50
  - [Onyphe](https://onyphe.io)
@@ -7,7 +7,6 @@ module Mihari
7
7
 
8
8
  option :rule, default: proc {}
9
9
 
10
- include Mixins::AutonomousSystem
11
10
  include Mixins::Configurable
12
11
  include Mixins::Retriable
13
12
 
@@ -20,6 +19,15 @@ module Mihari
20
19
  @base_time = Time.now.utc
21
20
  end
22
21
 
22
+ #
23
+ # Load/overwrite rule
24
+ #
25
+ # @param [String] path_or_id
26
+ #
27
+ def load_rule(path_or_id)
28
+ @rule = Structs::Rule.from_path_or_id path_or_id
29
+ end
30
+
23
31
  # @return [Array<String>, Array<Mihari::Artifact>]
24
32
  def artifacts
25
33
  raise NotImplementedError, "You must implement #{self.class}##{__method__}"
@@ -30,35 +38,41 @@ module Mihari
30
38
  self.class.to_s.split("::").last.to_s
31
39
  end
32
40
 
41
+ # @return [String]
42
+ def class_name
43
+ self.class.to_s.split("::").last
44
+ end
45
+
33
46
  #
34
47
  # Set artifacts & run emitters in parallel
35
48
  #
36
49
  # @return [Mihari::Alert, nil]
37
50
  #
38
51
  def run
39
- unless configured?
40
- class_name = self.class.to_s.split("::").last
41
- raise ConfigurationError, "#{class_name} is not configured correctly"
42
- end
43
-
44
- set_enriched_artifacts
45
-
46
- responses = Parallel.map(valid_emitters) do |emitter|
47
- run_emitter emitter
48
- end
52
+ raise ConfigurationError, "#{class_name} is not configured correctly" unless configured?
49
53
 
54
+ alert_or_something = bulk_emit
50
55
  # returns Mihari::Alert created by the database emitter
51
- responses.find { |res| res.is_a?(Mihari::Alert) }
56
+ alert_or_something.find { |res| res.is_a?(Mihari::Alert) }
52
57
  end
53
58
 
54
59
  #
55
- # Run emitter
60
+ # Bulk emit
61
+ #
62
+ # @return [Array<Mihari::Alert>]
63
+ #
64
+ def bulk_emit
65
+ Parallel.map(valid_emitters) { |emitter| emit emitter }.compact
66
+ end
67
+
68
+ #
69
+ # Emit an alert
56
70
  #
57
71
  # @param [Mihari::Emitters::Base] emitter
58
72
  #
59
73
  # @return [Mihari::Alert, nil]
60
74
  #
61
- def run_emitter(emitter)
75
+ def emit(emitter)
62
76
  return if enriched_artifacts.empty?
63
77
 
64
78
  alert_or_something = emitter.run(artifacts: enriched_artifacts, rule: rule)
@@ -80,6 +94,7 @@ module Mihari
80
94
  #
81
95
  # Normalize artifacts
82
96
  # - Convert data (string) into an artifact
97
+ # - Set rule ID
83
98
  # - Reject an invalid artifact
84
99
  # - Uniquefy artifacts by data
85
100
  #
@@ -89,17 +104,16 @@ module Mihari
89
104
  @normalized_artifacts ||= artifacts.compact.sort.map do |artifact|
90
105
  # No need to set data_type manually
91
106
  # It is set automatically in #initialize
92
- artifact.is_a?(Artifact) ? artifact : Artifact.new(data: artifact, source: source)
93
- end.select(&:valid?).uniq(&:data).map do |artifact|
107
+ artifact = artifact.is_a?(Artifact) ? artifact : Artifact.new(data: artifact, source: source)
94
108
  artifact.rule_id = rule&.id
95
109
  artifact
96
- end
110
+ end.select(&:valid?).uniq(&:data)
97
111
  end
98
112
 
99
113
  private
100
114
 
101
115
  #
102
- # Uniquefy artifacts
116
+ # Uniquefy artifacts (assure rule level uniqueness)
103
117
  #
104
118
  # @return [Array<Mihari::Artifact>]
105
119
  #
@@ -121,15 +135,6 @@ module Mihari
121
135
  end
122
136
  end
123
137
 
124
- #
125
- # Set enriched artifacts
126
- #
127
- # @return [nil]
128
- #
129
- def set_enriched_artifacts
130
- retry_on_error { enriched_artifacts }
131
- end
132
-
133
138
  #
134
139
  # Select valid emitters
135
140
  #
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "binaryedge"
4
-
5
3
  module Mihari
6
4
  module Analyzers
7
5
  class BinaryEdge < Base
@@ -12,6 +10,12 @@ module Mihari
12
10
  # @return [String, nil]
13
11
  attr_reader :api_key
14
12
 
13
+ # @return [String]
14
+ attr_reader :query
15
+
16
+ # @return [Integer]
17
+ attr_reader :interval
18
+
15
19
  def initialize(*args, **kwargs)
16
20
  super(*args, **kwargs)
17
21
 
@@ -43,9 +47,9 @@ module Mihari
43
47
  #
44
48
  # @return [Hash]
45
49
  #
46
- def search_with_page(query, page: 1)
47
- api.host.search(query, page: page)
48
- rescue ::BinaryEdge::Error => e
50
+ def search_with_page(page: 1)
51
+ client.search(query, page: page)
52
+ rescue UnsuccessfulStatusCodeError => e
49
53
  raise RetryableError, e if e.message.include?("Request time limit exceeded")
50
54
 
51
55
  raise e
@@ -58,8 +62,8 @@ module Mihari
58
62
  #
59
63
  def search
60
64
  responses = []
61
- (1..Float::INFINITY).each do |page|
62
- res = search_with_page(query, page: page)
65
+ (1..500).each do |page|
66
+ res = search_with_page(page: page)
63
67
  total = res["total"].to_i
64
68
 
65
69
  responses << res
@@ -75,8 +79,12 @@ module Mihari
75
79
  %w[binaryedge_api_key]
76
80
  end
77
81
 
78
- def api
79
- @api ||= ::BinaryEdge::API.new(api_key)
82
+ #
83
+ #
84
+ # @return [Mihari::Clients::BinaryEdge]
85
+ #
86
+ def client
87
+ @client ||= Clients::BinaryEdge.new(api_key: api_key)
80
88
  end
81
89
  end
82
90
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "censysx"
4
-
5
3
  module Mihari
6
4
  module Analyzers
7
5
  class Censys < Base
@@ -15,6 +13,12 @@ module Mihari
15
13
  # @return [String, nil]
16
14
  attr_reader :secret
17
15
 
16
+ # @return [Integer]
17
+ attr_reader :interval
18
+
19
+ # @return [String]
20
+ attr_reader :query
21
+
18
22
  def initialize(*args, **kwargs)
19
23
  super(*args, **kwargs)
20
24
 
@@ -42,11 +46,8 @@ module Mihari
42
46
 
43
47
  cursor = nil
44
48
  loop do
45
- response = api.search(query, cursor: cursor)
46
- response = Structs::Censys::Response.from_dynamic!(response)
47
-
48
- artifacts << response_to_artifacts(response)
49
-
49
+ response = client.search(query, cursor: cursor)
50
+ artifacts << response.result.to_artifacts(source)
50
51
  cursor = response.result.links.next
51
52
  break if cursor == ""
52
53
 
@@ -57,57 +58,12 @@ module Mihari
57
58
  artifacts.flatten.uniq(&:data)
58
59
  end
59
60
 
60
- #
61
- # Extract IPv4s from Censys search API response
62
- #
63
- # @param [Structs::Censys::Response] response
64
- #
65
- # @return [Array<String>]
66
- #
67
- def response_to_artifacts(response)
68
- response.result.hits.map { |hit| build_artifact(hit) }
69
- end
70
-
71
- #
72
- # Build an artifact from a Shodan search API response
73
- #
74
- # @param [Structs::Censys::Hit] hit
75
- #
76
- # @return [Artifact]
77
- #
78
- def build_artifact(hit)
79
- as = AutonomousSystem.new(asn: normalize_asn(hit.autonomous_system.asn))
80
-
81
- # sometimes Censys overlooks country
82
- # then set geolocation as nil
83
- geolocation = nil
84
- unless hit.location.country.nil?
85
- geolocation = Geolocation.new(
86
- country: hit.location.country,
87
- country_code: hit.location.country_code
88
- )
89
- end
90
-
91
- ports = hit.services.map(&:port).map do |port|
92
- Port.new(port: port)
93
- end
94
-
95
- Artifact.new(
96
- data: hit.ip,
97
- source: source,
98
- metadata: hit.metadata,
99
- autonomous_system: as,
100
- geolocation: geolocation,
101
- ports: ports
102
- )
103
- end
104
-
105
61
  def configuration_keys
106
62
  %w[censys_id censys_secret]
107
63
  end
108
64
 
109
- def api
110
- @api ||= ::Censys::API.new(id, secret)
65
+ def client
66
+ @client ||= Clients::Censys.new(id: id, secret: secret)
111
67
  end
112
68
 
113
69
  def id?
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "passive_circl"
4
-
5
3
  module Mihari
6
4
  module Analyzers
7
5
  class CIRCL < Base
@@ -18,6 +16,9 @@ module Mihari
18
16
  # @return [String, nil]
19
17
  attr_reader :password
20
18
 
19
+ # @return [String]
20
+ attr_reader :query
21
+
21
22
  def initialize(*args, **kwargs)
22
23
  super
23
24
 
@@ -42,8 +43,8 @@ module Mihari
42
43
  %w[circl_passive_password circl_passive_username]
43
44
  end
44
45
 
45
- def api
46
- @api ||= ::PassiveCIRCL::API.new(username: username, password: password)
46
+ def client
47
+ @client ||= Clients::CIRCL.new(username: username, password: password)
47
48
  end
48
49
 
49
50
  #
@@ -68,7 +69,7 @@ module Mihari
68
69
  # @return [Array<String>]
69
70
  #
70
71
  def passive_dns_search
71
- results = api.dns.query(@query)
72
+ results = client.dns_query(query)
72
73
  results.filter_map do |result|
73
74
  type = result["rrtype"]
74
75
  (type == "A") ? result["rdata"] : nil
@@ -81,7 +82,7 @@ module Mihari
81
82
  # @return [Array<String>]
82
83
  #
83
84
  def passive_ssl_search
84
- result = api.ssl.cquery(@query)
85
+ result = client.ssl_cquery(query)
85
86
  seen = result["seen"] || []
86
87
  seen.uniq
87
88
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "crtsh"
4
-
5
3
  module Mihari
6
4
  module Analyzers
7
5
  class Crtsh < Base
@@ -9,6 +7,12 @@ module Mihari
9
7
 
10
8
  option :exclude_expired, default: proc { true }
11
9
 
10
+ # @return [Boolean]
11
+ attr_reader :exclude_expired
12
+
13
+ # @return [String]
14
+ attr_reader :query
15
+
12
16
  def artifacts
13
17
  results = search
14
18
  results.map do |result|
@@ -21,8 +25,11 @@ module Mihari
21
25
 
22
26
  private
23
27
 
24
- def api
25
- @api ||= ::Crtsh::API.new
28
+ #
29
+ # @return [Mihari::Clients::Crtsh]
30
+ #
31
+ def client
32
+ @client ||= Mihari::Clients::Crtsh.new
26
33
  end
27
34
 
28
35
  #
@@ -32,9 +39,7 @@ module Mihari
32
39
  #
33
40
  def search
34
41
  exclude = exclude_expired ? "expired" : nil
35
- api.search(query, exclude: exclude)
36
- rescue ::Crtsh::Error => _e
37
- []
42
+ client.search(query, exclude: exclude)
38
43
  end
39
44
  end
40
45
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dnstwister"
4
-
5
3
  module Mihari
6
4
  module Analyzers
7
5
  class DNSTwister < Base
@@ -9,10 +7,12 @@ module Mihari
9
7
 
10
8
  param :query
11
9
 
12
- option :tags, default: proc { [] }
13
-
10
+ # @return [String]
14
11
  attr_reader :type
15
12
 
13
+ # @return [String]
14
+ attr_reader :query
15
+
16
16
  def initialize(*args, **kwargs)
17
17
  super
18
18
 
@@ -35,8 +35,8 @@ module Mihari
35
35
  type == "domain"
36
36
  end
37
37
 
38
- def api
39
- @api ||= ::DNSTwister::API.new
38
+ def client
39
+ @client ||= Clients::DNSTwister.new
40
40
  end
41
41
 
42
42
  #
@@ -61,7 +61,7 @@ module Mihari
61
61
  def search
62
62
  raise InvalidInputError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
63
63
 
64
- res = api.fuzz(query)
64
+ res = client.fuzz(query)
65
65
  fuzzy_domains = res["fuzzy_domains"] || []
66
66
  domains = fuzzy_domains.map { |domain| domain["domain"] }
67
67
  Parallel.map(domains) do |domain|
@@ -8,26 +8,49 @@ module Mihari
8
8
  class Feed < Base
9
9
  param :query
10
10
 
11
- option :http_request_method, default: proc { "GET" }
12
- option :http_request_headers, default: proc { {} }
13
- option :http_request_payload, default: proc { {} }
14
- option :http_request_payload_type, default: proc {}
11
+ option :method, default: proc { "GET" }
12
+ option :headers, default: proc { {} }
13
+ option :params, default: proc {}
14
+ option :json, default: proc {}
15
+ option :data, default: proc {}
15
16
 
16
17
  option :selector, default: proc { "" }
17
18
 
19
+ # @return [Hash, nil]
20
+ attr_reader :data
21
+
22
+ # @return [Hash, nil]
23
+ attr_reader :json
24
+
25
+ # @return [Hash, nil]
26
+ attr_reader :params
27
+
28
+ # @return [Hash, nil]
29
+ attr_reader :headers
30
+
31
+ # @return [String]
32
+ attr_reader :method
33
+
34
+ # @return [String]
35
+ attr_reader :selector
36
+
37
+ # @return [String]
38
+ attr_reader :query
39
+
18
40
  def artifacts
19
- Mihari::Feed::Parser.new(data).parse selector
41
+ Mihari::Feed::Parser.new(results).parse selector
20
42
  end
21
43
 
22
44
  private
23
45
 
24
- def data
46
+ def results
25
47
  reader = Mihari::Feed::Reader.new(
26
48
  query,
27
- http_request_method: http_request_method,
28
- http_request_headers: http_request_headers,
29
- http_request_payload: http_request_payload,
30
- http_request_payload_type: http_request_payload_type
49
+ method: method,
50
+ headers: headers,
51
+ params: params,
52
+ json: json,
53
+ data: data
31
54
  )
32
55
  reader.read
33
56
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "greynoise"
4
-
5
3
  module Mihari
6
4
  module Analyzers
7
5
  class GreyNoise < Base
@@ -10,6 +8,9 @@ module Mihari
10
8
  # @return [String, nil]
11
9
  attr_reader :api_key
12
10
 
11
+ # @return [String]
12
+ attr_reader :query
13
+
13
14
  def initialize(*args, **kwargs)
14
15
  super(*args, **kwargs)
15
16
 
@@ -17,10 +18,8 @@ module Mihari
17
18
  end
18
19
 
19
20
  def artifacts
20
- res = Structs::GreyNoise::Response.from_dynamic!(search)
21
- res.data.map do |datum|
22
- build_artifact datum
23
- end
21
+ res = search
22
+ res.to_artifacts
24
23
  end
25
24
 
26
25
  private
@@ -31,8 +30,8 @@ module Mihari
31
30
  %w[greynoise_api_key]
32
31
  end
33
32
 
34
- def api
35
- @api ||= ::GreyNoise::API.new(key: api_key)
33
+ def client
34
+ @client ||= Clients::GreyNoise.new(api_key: api_key)
36
35
  end
37
36
 
38
37
  #
@@ -41,31 +40,7 @@ module Mihari
41
40
  # @return [Hash]
42
41
  #
43
42
  def search
44
- api.experimental.gnql(query, size: PAGE_SIZE)
45
- end
46
-
47
- #
48
- # Build an artifact from a GreyNoise search API response
49
- #
50
- # @param [Structs::GreyNoise::Datum] datum
51
- #
52
- # @return [Artifact]
53
- #
54
- def build_artifact(datum)
55
- as = AutonomousSystem.new(asn: normalize_asn(datum.metadata.asn))
56
-
57
- geolocation = Geolocation.new(
58
- country: datum.metadata.country,
59
- country_code: datum.metadata.country_code
60
- )
61
-
62
- Artifact.new(
63
- data: datum.ip,
64
- source: source,
65
- metadata: datum.metadata_,
66
- autonomous_system: as,
67
- geolocation: geolocation
68
- )
43
+ client.gnql_search(query, size: PAGE_SIZE)
69
44
  end
70
45
  end
71
46
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "onyphe"
4
3
  require "normalize_country"
5
4
 
6
5
  module Mihari
@@ -13,6 +12,12 @@ module Mihari
13
12
  # @return [String, nil]
14
13
  attr_reader :api_key
15
14
 
15
+ # @return [String]
16
+ attr_reader :query
17
+
18
+ # @return [Integer]
19
+ attr_reader :interval
20
+
16
21
  def initialize(*args, **kwargs)
17
22
  super(*args, **kwargs)
18
23
 
@@ -23,10 +28,7 @@ module Mihari
23
28
  responses = search
24
29
  return [] unless responses
25
30
 
26
- results = responses.map(&:results).flatten
27
- results.map do |result|
28
- build_artifact result
29
- end
31
+ responses.map { |response| response.to_artifacts(source) }.flatten
30
32
  end
31
33
 
32
34
  private
@@ -37,8 +39,8 @@ module Mihari
37
39
  %w[onyphe_api_key]
38
40
  end
39
41
 
40
- def api
41
- @api ||= ::Onyphe::API.new(api_key)
42
+ def client
43
+ @client ||= Clients::Onyphe.new(api_key: api_key)
42
44
  end
43
45
 
44
46
  #
@@ -50,8 +52,7 @@ module Mihari
50
52
  # @return [Structs::Onyphe::Response]
51
53
  #
52
54
  def search_with_page(query, page: 1)
53
- res = api.simple.datascan(query, page: page)
54
- Structs::Onyphe::Response.from_dynamic!(res)
55
+ client.datascan(query, page: page)
55
56
  end
56
57
 
57
58
  #
@@ -73,33 +74,6 @@ module Mihari
73
74
  end
74
75
  responses
75
76
  end
76
-
77
- #
78
- # Build an artifact from an Onyphe search API result
79
- #
80
- # @param [Structs::Onyphe::Result] result
81
- #
82
- # @return [Artifact]
83
- #
84
- def build_artifact(result)
85
- as = AutonomousSystem.new(asn: normalize_asn(result.asn))
86
-
87
- geolocation = nil
88
- unless result.country_code.nil?
89
- geolocation = Geolocation.new(
90
- country: NormalizeCountry(result.country_code, to: :short),
91
- country_code: result.country_code
92
- )
93
- end
94
-
95
- Artifact.new(
96
- data: result.ip,
97
- source: source,
98
- metadata: result.metadata,
99
- autonomous_system: as,
100
- geolocation: geolocation
101
- )
102
- end
103
77
  end
104
78
  end
105
79
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "mihari/analyzers/clients/otx"
4
-
5
3
  module Mihari
6
4
  module Analyzers
7
5
  class OTX < Base
@@ -15,6 +13,9 @@ module Mihari
15
13
  # @return [String, nil]
16
14
  attr_reader :api_key
17
15
 
16
+ # @return [String]
17
+ attr_reader :query
18
+
18
19
  def initialize(*args, **kwargs)
19
20
  super
20
21
 
@@ -35,7 +36,7 @@ module Mihari
35
36
  end
36
37
 
37
38
  def client
38
- @client ||= Mihari::Analyzers::Clients::OTX.new(api_key)
39
+ @client ||= Mihari::Clients::OTX.new(api_key: api_key)
39
40
  end
40
41
 
41
42
  #