mihari 5.1.0 → 5.1.2

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 (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
  #