mihari 3.4.0 → 3.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (173) hide show
  1. checksums.yaml +4 -4
  2. data/.gitmodules +3 -0
  3. data/README.md +2 -0
  4. data/Steepfile +32 -0
  5. data/config.ru +1 -0
  6. data/lib/mihari/analyzers/base.rb +39 -11
  7. data/lib/mihari/analyzers/binaryedge.rb +13 -0
  8. data/lib/mihari/analyzers/censys.rb +42 -9
  9. data/lib/mihari/analyzers/circl.rb +15 -0
  10. data/lib/mihari/analyzers/crtsh.rb +5 -0
  11. data/lib/mihari/analyzers/dnpedia.rb +5 -0
  12. data/lib/mihari/analyzers/dnstwister.rb +17 -0
  13. data/lib/mihari/analyzers/onyphe.rb +50 -9
  14. data/lib/mihari/analyzers/otx.rb +20 -0
  15. data/lib/mihari/analyzers/passivetotal.rb +25 -0
  16. data/lib/mihari/analyzers/pulsedive.rb +10 -0
  17. data/lib/mihari/analyzers/rule.rb +18 -0
  18. data/lib/mihari/analyzers/securitytrails.rb +25 -0
  19. data/lib/mihari/analyzers/shodan.rb +39 -5
  20. data/lib/mihari/analyzers/spyse.rb +20 -0
  21. data/lib/mihari/analyzers/urlscan.rb +10 -0
  22. data/lib/mihari/analyzers/virustotal.rb +20 -0
  23. data/lib/mihari/analyzers/zoomeye.rb +38 -0
  24. data/lib/mihari/cli/analyzer.rb +1 -0
  25. data/lib/mihari/cli/base.rb +0 -2
  26. data/lib/mihari/commands/init.rb +4 -4
  27. data/lib/mihari/commands/search.rb +1 -0
  28. data/lib/mihari/commands/web.rb +1 -0
  29. data/lib/mihari/{constraints.rb → constants.rb} +0 -0
  30. data/lib/mihari/database.rb +42 -3
  31. data/lib/mihari/emitters/base.rb +1 -1
  32. data/lib/mihari/emitters/misp.rb +38 -5
  33. data/lib/mihari/emitters/slack.rb +20 -2
  34. data/lib/mihari/emitters/the_hive.rb +16 -3
  35. data/lib/mihari/emitters/webhook.rb +18 -3
  36. data/lib/mihari/mixins/disallowed_data_value.rb +1 -1
  37. data/lib/mihari/models/alert.rb +28 -10
  38. data/lib/mihari/models/artifact.rb +55 -0
  39. data/lib/mihari/models/autonomous_system.rb +9 -0
  40. data/lib/mihari/models/dns.rb +53 -0
  41. data/lib/mihari/models/geolocation.rb +9 -0
  42. data/lib/mihari/models/reverse_dns.rb +24 -0
  43. data/lib/mihari/models/whois.rb +119 -0
  44. data/lib/mihari/schemas/configuration.rb +1 -0
  45. data/lib/mihari/schemas/rule.rb +2 -15
  46. data/lib/mihari/serializers/alert.rb +6 -4
  47. data/lib/mihari/serializers/artifact.rb +11 -2
  48. data/lib/mihari/serializers/autonomous_system.rb +9 -0
  49. data/lib/mihari/serializers/dns.rb +11 -0
  50. data/lib/mihari/serializers/geolocation.rb +11 -0
  51. data/lib/mihari/serializers/reverse_dns.rb +11 -0
  52. data/lib/mihari/serializers/tag.rb +4 -2
  53. data/lib/mihari/serializers/whois.rb +11 -0
  54. data/lib/mihari/structs/censys.rb +92 -0
  55. data/lib/mihari/structs/onyphe.rb +47 -0
  56. data/lib/mihari/structs/shodan.rb +53 -0
  57. data/lib/mihari/type_checker.rb +9 -9
  58. data/lib/mihari/types.rb +21 -0
  59. data/lib/mihari/version.rb +1 -1
  60. data/lib/mihari/web/app.rb +2 -0
  61. data/lib/mihari/web/controllers/alerts_controller.rb +3 -4
  62. data/lib/mihari/web/controllers/artifacts_controller.rb +46 -2
  63. data/lib/mihari/web/controllers/ip_address_controller.rb +36 -0
  64. data/lib/mihari/web/controllers/sources_controller.rb +2 -2
  65. data/lib/mihari/web/controllers/tags_controller.rb +3 -1
  66. data/lib/mihari/web/public/index.html +1 -1
  67. data/lib/mihari/web/public/redoc-static.html +12 -10
  68. data/lib/mihari/web/public/static/fonts/fa-brands-400.1a575a41.woff +0 -0
  69. data/lib/mihari/web/public/static/fonts/fa-brands-400.513aa607.ttf +0 -0
  70. data/lib/mihari/web/public/static/fonts/fa-brands-400.592643a8.eot +0 -0
  71. data/lib/mihari/web/public/static/fonts/fa-brands-400.ed311c7a.woff2 +0 -0
  72. data/lib/mihari/web/public/static/fonts/fa-regular-400.766913e6.ttf +0 -0
  73. data/lib/mihari/web/public/static/fonts/fa-regular-400.b0e2db3b.eot +0 -0
  74. data/lib/mihari/web/public/static/fonts/fa-regular-400.b91d376b.woff2 +0 -0
  75. data/lib/mihari/web/public/static/fonts/fa-regular-400.d1d7e3b4.woff +0 -0
  76. data/lib/mihari/web/public/static/fonts/fa-solid-900.0c6bfc66.eot +0 -0
  77. data/lib/mihari/web/public/static/fonts/fa-solid-900.b9625119.ttf +0 -0
  78. data/lib/mihari/web/public/static/fonts/fa-solid-900.d745348d.woff +0 -0
  79. data/lib/mihari/web/public/static/fonts/fa-solid-900.d824df7e.woff2 +0 -0
  80. data/lib/mihari/web/public/static/img/fa-brands-400.1d5619cd.svg +3717 -0
  81. data/lib/mihari/web/public/static/img/fa-regular-400.c5d109be.svg +801 -0
  82. data/lib/mihari/web/public/static/img/fa-solid-900.37bc7099.svg +5034 -0
  83. data/lib/mihari/web/public/static/js/app.8e3e5150.js +36 -0
  84. data/lib/mihari/web/public/static/js/app.8e3e5150.js.map +1 -0
  85. data/lib/mihari/web/public/static/js/app.b5914c39.js +36 -0
  86. data/lib/mihari/web/public/static/js/app.b5914c39.js.map +1 -0
  87. data/lib/mihari.rb +25 -4
  88. data/mihari.gemspec +10 -2
  89. data/sig/lib/mihari/analyzers/base.rbs +99 -0
  90. data/sig/lib/mihari/analyzers/basic.rbs +17 -0
  91. data/sig/lib/mihari/analyzers/binaryedge.rbs +25 -0
  92. data/sig/lib/mihari/analyzers/censys.rbs +38 -0
  93. data/sig/lib/mihari/analyzers/circl.rbs +29 -0
  94. data/sig/lib/mihari/analyzers/crtsh.rbs +19 -0
  95. data/sig/lib/mihari/analyzers/dnpedia.rbs +18 -0
  96. data/sig/lib/mihari/analyzers/dnstwister.rbs +27 -0
  97. data/sig/lib/mihari/analyzers/onyphe.rbs +33 -0
  98. data/sig/lib/mihari/analyzers/otx.rbs +33 -0
  99. data/sig/lib/mihari/analyzers/passivetotal.rbs +33 -0
  100. data/sig/lib/mihari/analyzers/pulsedive.rbs +27 -0
  101. data/sig/lib/mihari/analyzers/rule.rbs +68 -0
  102. data/sig/lib/mihari/analyzers/securitytrails.rbs +33 -0
  103. data/sig/lib/mihari/analyzers/shodan.rbs +33 -0
  104. data/sig/lib/mihari/analyzers/spyse.rbs +29 -0
  105. data/sig/lib/mihari/analyzers/urlscan.rbs +28 -0
  106. data/sig/lib/mihari/analyzers/virustotal.rbs +31 -0
  107. data/sig/lib/mihari/analyzers/zoomeye.rbs +33 -0
  108. data/sig/lib/mihari/cli/analyzer.rbs +39 -0
  109. data/sig/lib/mihari/cli/base.rbs +11 -0
  110. data/sig/lib/mihari/cli/init.rbs +7 -0
  111. data/sig/lib/mihari/cli/main.rbs +9 -0
  112. data/sig/lib/mihari/cli/mixins/utils.rbs +50 -0
  113. data/sig/lib/mihari/cli/validator.rbs +7 -0
  114. data/sig/lib/mihari/commands/binaryedge.rbs +7 -0
  115. data/sig/lib/mihari/commands/censys.rbs +7 -0
  116. data/sig/lib/mihari/commands/circl.rbs +7 -0
  117. data/sig/lib/mihari/commands/crtsh.rbs +7 -0
  118. data/sig/lib/mihari/commands/dnpedia.rbs +7 -0
  119. data/sig/lib/mihari/commands/dnstwister.rbs +7 -0
  120. data/sig/lib/mihari/commands/init.rbs +11 -0
  121. data/sig/lib/mihari/commands/json.rbs +7 -0
  122. data/sig/lib/mihari/commands/onyphe.rbs +7 -0
  123. data/sig/lib/mihari/commands/otx.rbs +7 -0
  124. data/sig/lib/mihari/commands/passivetotal.rbs +7 -0
  125. data/sig/lib/mihari/commands/pulsedive.rbs +7 -0
  126. data/sig/lib/mihari/commands/search.rbs +35 -0
  127. data/sig/lib/mihari/commands/securitytrails.rbs +7 -0
  128. data/sig/lib/mihari/commands/shodan.rbs +7 -0
  129. data/sig/lib/mihari/commands/spyse.rbs +7 -0
  130. data/sig/lib/mihari/commands/urlscan.rbs +7 -0
  131. data/sig/lib/mihari/commands/validator.rbs +11 -0
  132. data/sig/lib/mihari/commands/virustotal.rbs +7 -0
  133. data/sig/lib/mihari/commands/web.rbs +7 -0
  134. data/sig/lib/mihari/commands/zoomeye.rbs +7 -0
  135. data/sig/lib/mihari/constants.rbs +3 -0
  136. data/sig/lib/mihari/database.rbs +25 -0
  137. data/sig/lib/mihari/emitters/base.rbs +18 -0
  138. data/sig/lib/mihari/emitters/database.rbs +9 -0
  139. data/sig/lib/mihari/emitters/misp.rbs +28 -0
  140. data/sig/lib/mihari/emitters/slack.rbs +58 -0
  141. data/sig/lib/mihari/emitters/stdout.rbs +9 -0
  142. data/sig/lib/mihari/emitters/the_hive.rbs +24 -0
  143. data/sig/lib/mihari/emitters/webhook.rbs +20 -0
  144. data/sig/lib/mihari/errors.rbs +10 -0
  145. data/sig/lib/mihari/mixins/configurable.rbs +26 -0
  146. data/sig/lib/mihari/mixins/configuration.rbs +45 -0
  147. data/sig/lib/mihari/mixins/disallowed_data_value.rbs +25 -0
  148. data/sig/lib/mihari/mixins/hash.rbs +14 -0
  149. data/sig/lib/mihari/mixins/refang.rbs +14 -0
  150. data/sig/lib/mihari/mixins/retriable.rbs +15 -0
  151. data/sig/lib/mihari/mixins/rule.rbs +41 -0
  152. data/sig/lib/mihari/models/alert.rbs +46 -0
  153. data/sig/lib/mihari/models/artifact.rbs +54 -0
  154. data/sig/lib/mihari/models/autonomous_system.rbs +5 -0
  155. data/sig/lib/mihari/models/dns.rbs +19 -0
  156. data/sig/lib/mihari/models/geolocation.rbs +6 -0
  157. data/sig/lib/mihari/models/reverse_dns.rbs +14 -0
  158. data/sig/lib/mihari/models/tag.rbs +5 -0
  159. data/sig/lib/mihari/models/tagging.rbs +4 -0
  160. data/sig/lib/mihari/models/whois.rbs +66 -0
  161. data/sig/lib/mihari/notifiers/base.rbs +18 -0
  162. data/sig/lib/mihari/notifiers/exception_notifier.rbs +75 -0
  163. data/sig/lib/mihari/notifiers/slack.rbs +50 -0
  164. data/sig/lib/mihari/status.rbs +25 -0
  165. data/sig/lib/mihari/structs/censys.rbs +50 -0
  166. data/sig/lib/mihari/structs/onyphe.rbs +25 -0
  167. data/sig/lib/mihari/structs/shodan.rbs +28 -0
  168. data/sig/lib/mihari/type_checker.rbs +48 -0
  169. data/sig/lib/mihari/types.rbs +17 -0
  170. data/sig/lib/mihari/version.rbs +3 -0
  171. data/sig/lib/mihari/web/app.rbs +5 -0
  172. data/sig/lib/mihari.rbs +57 -0
  173. metadata +240 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 142df147927aee93e6c653b2eb29cca50f4ba11606d68f1302af21780d6f0dc5
4
- data.tar.gz: 594f762c94e361cc53cab08b39abad5e3503555b51d93add3cbce744fcbff711
3
+ metadata.gz: 14d0c74e85fbf6ef624afefe7e948595586d6c00fa8bc32f211e60caee581fc3
4
+ data.tar.gz: 9d5fde6d69f664efac0d6c56e6a0ba60adcad0edcfe45c69f285ffcaba8d11f0
5
5
  SHA512:
6
- metadata.gz: 8483d2d125e30f04bdaf74243877dd9a261511d7d857f6dba42c6ea54af5de95f63c23759d1937badfdb5ae75819b99e70270edb0fb2f466601b8eaf6912dc8e
7
- data.tar.gz: 8ffca15aadc0bc783086dd11df9fd051933af31e6778fcd4a67ddf51af5532b452603526a0303eb7c1dfdb530636a6e91df8440210189af3dc0f2806a4e64218
6
+ metadata.gz: 30597743e91f124388fbdf426199d97a00f92db9e525fe5725643d529d319ca670d1e9aa6eccff86c0844802157ca3d5c7db9b9afd925d6f0ac4d8b881c44949
7
+ data.tar.gz: 9d199a3c2f6c7794214730de7c8db812e939c5ddd11ab06d1c6f28c3b8764b2881958b0684adb59e8516d0ac4b6a1dc66b19c5a593efaac9695cb6e317ce8105
data/.gitmodules ADDED
@@ -0,0 +1,3 @@
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/README.md CHANGED
@@ -64,3 +64,5 @@ The gem is available as open source under the terms of the [MIT License](https:/
64
64
  ## Acknowledgement
65
65
 
66
66
  Mihari is proudly supported by [Tines.io](https://tines.io?utm_source=github&utm_medium=sponsorship&utm_campaign=ninoseki), The SOAR Platform for Enterprise Security Teams.
67
+
68
+ $ bundle exec rbs -rpathname --repo=gem_rbs/gems -ractivesupport -ractionpack -ractivejob -ractivemodel -ractionview -ractiverecord -rrailties -I sig validate
data/Steepfile ADDED
@@ -0,0 +1,32 @@
1
+ target :lib do
2
+ signature "sig"
3
+ check "lib"
4
+
5
+ repo_path "vendor/rbs/gem_rbs_collection/gems"
6
+
7
+ library "date"
8
+ library "json"
9
+ library "logger"
10
+ library "monitor"
11
+ library "mutex_m"
12
+ library "pathname"
13
+ library "securerandom"
14
+ library "singleton"
15
+ library "time"
16
+ library "tsort"
17
+ library "uri"
18
+ library "resolv"
19
+ library "timeout"
20
+ library "socket"
21
+
22
+ library "rack"
23
+
24
+ library "actionpack"
25
+ library "actionview"
26
+ library "activejob"
27
+ library "activemodel"
28
+ library "activerecord"
29
+ library "activesupport"
30
+ library "parallel"
31
+ library "railties"
32
+ end
data/config.ru CHANGED
@@ -1,3 +1,4 @@
1
+ # bundle exec rerun -- rackup config.ru
1
2
  require "./lib/mihari"
2
3
 
3
4
  # set rack env as development
@@ -27,7 +27,7 @@ module Mihari
27
27
 
28
28
  # @return [String]
29
29
  def title
30
- self.class.to_s.split("::").last
30
+ self.class.to_s.split("::").last.to_s
31
31
  end
32
32
 
33
33
  # @return [String]
@@ -37,7 +37,7 @@ module Mihari
37
37
 
38
38
  # @return [String]
39
39
  def source
40
- self.class.to_s.split("::").last
40
+ self.class.to_s.split("::").last.to_s
41
41
  end
42
42
 
43
43
  # @return [Array<String>]
@@ -51,7 +51,7 @@ module Mihari
51
51
  # @return [nil]
52
52
  #
53
53
  def run
54
- set_unique_artifacts
54
+ set_enriched_artifacts
55
55
 
56
56
  Parallel.each(valid_emitters) do |emitter|
57
57
  run_emitter emitter
@@ -66,7 +66,7 @@ module Mihari
66
66
  # @return [nil]
67
67
  #
68
68
  def run_emitter(emitter)
69
- emitter.run(title: title, description: description, artifacts: unique_artifacts, source: source, tags: tags)
69
+ emitter.run(title: title, description: description, artifacts: enriched_artifacts, source: source, tags: tags)
70
70
  rescue StandardError => e
71
71
  puts "Emission by #{emitter.class} is failed: #{e}"
72
72
  end
@@ -88,7 +88,7 @@ module Mihari
88
88
  # No need to set data_type manually
89
89
  # It is set automatically in #initialize
90
90
  artifact.is_a?(Artifact) ? artifact : Artifact.new(data: artifact, source: source)
91
- end.select(&:valid?)
91
+ end.select(&:valid?).uniq(&:data)
92
92
  end
93
93
 
94
94
  private
@@ -105,15 +105,29 @@ module Mihari
105
105
  end
106
106
 
107
107
  #
108
- # Set unique artifacts
108
+ # Enriched artifacts
109
+ #
110
+ # @return [Array<Mihari::Artifact>]
111
+ #
112
+ def enriched_artifacts
113
+ @enriched_artifacts ||= unique_artifacts.map do |artifact|
114
+ artifact.enrich_whois
115
+ artifact.enrich_dns
116
+ artifact.enrich_reverse_dns
117
+ artifact
118
+ end
119
+ end
120
+
121
+ #
122
+ # Set enriched artifacts
109
123
  #
110
124
  # @return [nil]
111
125
  #
112
- def set_unique_artifacts
113
- retry_on_error { unique_artifacts }
114
- rescue ArgumentError => _e
126
+ def set_enriched_artifacts
127
+ retry_on_error { enriched_artifacts }
128
+ rescue ArgumentError => e
115
129
  klass = self.class.to_s.split("::").last.to_s
116
- raise Error, "Please configure #{klass} API settings properly"
130
+ raise Error, "Please configure #{klass} settings properly. (#{e})"
117
131
  end
118
132
 
119
133
  #
@@ -125,7 +139,21 @@ module Mihari
125
139
  @valid_emitters ||= Mihari.emitters.filter_map do |klass|
126
140
  emitter = klass.new
127
141
  emitter.valid? ? emitter : nil
128
- end
142
+ end.compact
143
+ end
144
+
145
+ #
146
+ # Normalize ASN value
147
+ #
148
+ # @param [String, Integer] asn
149
+ #
150
+ # @return [Integer]
151
+ #
152
+ def normalize_asn(asn)
153
+ return asn if asn.is_a?(Integer)
154
+ return asn.to_i unless asn.start_with?("AS")
155
+
156
+ asn.delete_prefix("AS").to_i
129
157
  end
130
158
  end
131
159
  end
@@ -26,6 +26,14 @@ module Mihari
26
26
 
27
27
  PAGE_SIZE = 20
28
28
 
29
+ #
30
+ # Search with pagination
31
+ #
32
+ # @param [String] query
33
+ # @param [Integer] page
34
+ #
35
+ # @return [Hash]
36
+ #
29
37
  def search_with_page(query, page: 1)
30
38
  api.host.search(query, page: page)
31
39
  rescue ::BinaryEdge::Error => e
@@ -34,6 +42,11 @@ module Mihari
34
42
  raise e
35
43
  end
36
44
 
45
+ #
46
+ # Search
47
+ #
48
+ # @return [Array<Hash>]
49
+ #
37
50
  def search
38
51
  responses = []
39
52
  (1..Float::INFINITY).each do |page|
@@ -16,32 +16,65 @@ module Mihari
16
16
 
17
17
  private
18
18
 
19
+ #
20
+ # Search
21
+ #
22
+ # @return [Array<String>]
23
+ #
19
24
  def search
20
- ipv4s = []
25
+ artifacts = []
21
26
 
22
27
  cursor = nil
23
28
  loop do
24
29
  response = api.search(query, cursor: cursor)
25
- ipv4s << response_to_ipv4s(response)
30
+ response = Structs::Censys::Response.from_dynamic!(response)
26
31
 
27
- links = response.dig("result", "links")
28
- cursor = links["next"]
32
+ artifacts << response_to_artifacts(response)
33
+
34
+ cursor = response.result.links.next
29
35
  break if cursor == ""
30
36
  end
31
37
 
32
- ipv4s.flatten
38
+ artifacts.flatten.uniq(&:data)
33
39
  end
34
40
 
35
41
  #
36
42
  # Extract IPv4s from Censys search API response
37
43
  #
38
- # @param [Hash] response
44
+ # @param [Structs::Censys::Response] response
39
45
  #
40
46
  # @return [Array<String>]
41
47
  #
42
- def response_to_ipv4s(response)
43
- hits = response.dig("result", "hits") || []
44
- hits.map { |hit| hit["ip"] }
48
+ def response_to_artifacts(response)
49
+ response.result.hits.map { |hit| build_artifact(hit) }
50
+ end
51
+
52
+ #
53
+ # Build an artifact from a Shodan search API response
54
+ #
55
+ # @param [Structs::Censys::Hit] hit
56
+ #
57
+ # @return [Artifact]
58
+ #
59
+ def build_artifact(hit)
60
+ as = AutonomousSystem.new(asn: normalize_asn(hit.autonomous_system.asn))
61
+
62
+ # sometimes Censys overlooks country
63
+ # then set geolocation as nil
64
+ geolocation = nil
65
+ unless hit.location.country.nil?
66
+ geolocation = Geolocation.new(
67
+ country: hit.location.country,
68
+ country_code: hit.location.country_code
69
+ )
70
+ end
71
+
72
+ Artifact.new(
73
+ data: hit.ip,
74
+ source: source,
75
+ autonomous_system: as,
76
+ geolocation: geolocation
77
+ )
45
78
  end
46
79
 
47
80
  def configuration_keys
@@ -35,6 +35,11 @@ module Mihari
35
35
  @api ||= ::PassiveCIRCL::API.new(username: Mihari.config.circl_passive_username, password: Mihari.config.circl_passive_password)
36
36
  end
37
37
 
38
+ #
39
+ # Passive DNS/SSL search
40
+ #
41
+ # @return [Array<String>]
42
+ #
38
43
  def search
39
44
  case @type
40
45
  when "domain"
@@ -46,6 +51,11 @@ module Mihari
46
51
  end
47
52
  end
48
53
 
54
+ #
55
+ # Passive DNS search
56
+ #
57
+ # @return [Array<String>]
58
+ #
49
59
  def passive_dns_search
50
60
  results = api.dns.query(@query)
51
61
  results.filter_map do |result|
@@ -54,6 +64,11 @@ module Mihari
54
64
  end.uniq
55
65
  end
56
66
 
67
+ #
68
+ # Passive SSL search
69
+ #
70
+ # @return [Array<String>]
71
+ #
57
72
  def passive_ssl_search
58
73
  result = api.ssl.cquery(@query)
59
74
  seen = result["seen"] || []
@@ -23,6 +23,11 @@ module Mihari
23
23
  @api ||= ::Crtsh::API.new
24
24
  end
25
25
 
26
+ #
27
+ # Search
28
+ #
29
+ # @return [Array<Hash>]
30
+ #
26
31
  def search
27
32
  exclude = exclude_expired ? "expired" : nil
28
33
  api.search(query, exclude: exclude)
@@ -20,6 +20,11 @@ module Mihari
20
20
  @api ||= ::DNPedia::API.new
21
21
  end
22
22
 
23
+ #
24
+ # Search
25
+ #
26
+ # @return [Array<String>]
27
+ #
23
28
  def search
24
29
  res = api.search(query)
25
30
  rows = res["rows"] || []
@@ -29,6 +29,11 @@ module Mihari
29
29
 
30
30
  private
31
31
 
32
+ #
33
+ # Check whether a type is valid or not
34
+ #
35
+ # @return [Boolean]
36
+ #
32
37
  def valid_type?
33
38
  type == "domain"
34
39
  end
@@ -37,6 +42,13 @@ module Mihari
37
42
  @api ||= ::DNSTwister::API.new
38
43
  end
39
44
 
45
+ #
46
+ # Check whether a domain is resolvable or not
47
+ #
48
+ # @param [String] domain
49
+ #
50
+ # @return [Boolean]
51
+ #
40
52
  def resolvable?(domain)
41
53
  Resolv.getaddress domain
42
54
  true
@@ -44,6 +56,11 @@ module Mihari
44
56
  false
45
57
  end
46
58
 
59
+ #
60
+ # Search
61
+ #
62
+ # @return [Array<String>]
63
+ #
47
64
  def search
48
65
  raise InvalidInputError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
49
66
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "onyphe"
4
+ require "normalize_country"
4
5
 
5
6
  module Mihari
6
7
  module Analyzers
@@ -11,14 +12,13 @@ module Mihari
11
12
  option :tags, default: proc { [] }
12
13
 
13
14
  def artifacts
14
- results = search
15
- return [] unless results
15
+ responses = search
16
+ return [] unless responses
16
17
 
17
- flat_results = results.map do |result|
18
- result["results"]
19
- end.flatten.compact
20
-
21
- flat_results.filter_map { |result| result["ip"] }.uniq
18
+ results = responses.map(&:results).flatten
19
+ results.map do |result|
20
+ build_artifact result
21
+ end
22
22
  end
23
23
 
24
24
  private
@@ -33,20 +33,61 @@ module Mihari
33
33
  @api ||= ::Onyphe::API.new(Mihari.config.onyphe_api_key)
34
34
  end
35
35
 
36
+ #
37
+ # Search with pagination
38
+ #
39
+ # @param [String] query
40
+ # @param [Integer] page
41
+ #
42
+ # @return [Structs::Onyphe::Response]
43
+ #
36
44
  def search_with_page(query, page: 1)
37
- api.simple.datascan(query, page: page)
45
+ res = api.simple.datascan(query, page: page)
46
+ Structs::Onyphe::Response.from_dynamic!(res)
38
47
  end
39
48
 
49
+ #
50
+ # Search
51
+ #
52
+ # @return [Array<Structs::Onyphe::Response>]
53
+ #
40
54
  def search
41
55
  responses = []
42
56
  (1..Float::INFINITY).each do |page|
43
57
  res = search_with_page(query, page: page)
44
58
  responses << res
45
- total = res["total"].to_i
59
+
60
+ total = res.total
46
61
  break if total <= page * PAGE_SIZE
47
62
  end
48
63
  responses
49
64
  end
65
+
66
+ #
67
+ # Build an artifact from an Onyphe search API result
68
+ #
69
+ # @param [Structs::Onyphe::Result] result
70
+ #
71
+ # @return [Artifact]
72
+ #
73
+ def build_artifact(result)
74
+ as = AutonomousSystem.new(asn: normalize_asn(result.asn))
75
+
76
+ geolocation = nil
77
+ unless result.country_code.nil?
78
+ geolocation = Geolocation.new(
79
+ country: NormalizeCountry(result.country_code, to: :short),
80
+ country_code: result.country_code
81
+ )
82
+ end
83
+
84
+ Artifact.new(
85
+ data: result.ip,
86
+ source: source,
87
+ autonomous_system: as,
88
+ geolocation: geolocation
89
+ )
90
+ end
50
91
  end
51
92
  end
52
93
  end