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
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "passivetotal"
4
-
5
3
  module Mihari
6
4
  module Analyzers
7
5
  class PassiveTotal < Base
@@ -18,6 +16,9 @@ module Mihari
18
16
  # @return [String, nil]
19
17
  attr_reader :api_key
20
18
 
19
+ # @return [String]
20
+ attr_reader :query
21
+
21
22
  def initialize(*args, **kwargs)
22
23
  super(*args, **kwargs)
23
24
 
@@ -42,8 +43,8 @@ module Mihari
42
43
  %w[passivetotal_username passivetotal_api_key]
43
44
  end
44
45
 
45
- def api
46
- @api ||= ::PassiveTotal::API.new(username: username, api_key: api_key)
46
+ def client
47
+ @client ||= Clients::PassiveTotal.new(username: username, api_key: api_key)
47
48
  end
48
49
 
49
50
  #
@@ -79,7 +80,7 @@ module Mihari
79
80
  # @return [Array<String>]
80
81
  #
81
82
  def passive_dns_search
82
- res = api.dns.passive_unique(query)
83
+ res = client.passive_dns_search(query)
83
84
  res["results"] || []
84
85
  end
85
86
 
@@ -89,7 +90,7 @@ module Mihari
89
90
  # @return [Array<Mihari::Artifact>]
90
91
  #
91
92
  def reverse_whois_search
92
- res = api.whois.search(query: query, field: "email")
93
+ res = client.reverse_whois_search(query: query, field: "email")
93
94
  results = res["results"] || []
94
95
  results.map do |result|
95
96
  data = result["domain"]
@@ -103,7 +104,7 @@ module Mihari
103
104
  # @return [Array<Mihari::Artifact>]
104
105
  #
105
106
  def ssl_search
106
- res = api.ssl.history(query)
107
+ res = client.ssl_search(query)
107
108
  results = res["results"] || []
108
109
  results.map do |result|
109
110
  data = result["ipAddresses"]
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "pulsedive"
4
-
5
3
  module Mihari
6
4
  module Analyzers
7
5
  class Pulsedive < Base
@@ -15,6 +13,9 @@ module Mihari
15
13
  # @return [String, nil]
16
14
  attr_reader :api_key
17
15
 
16
+ # @return [Integer]
17
+ attr_reader :query
18
+
18
19
  def initialize(*args, **kwargs)
19
20
  super
20
21
 
@@ -34,8 +35,8 @@ module Mihari
34
35
  %w[pulsedive_api_key]
35
36
  end
36
37
 
37
- def api
38
- @api ||= ::Pulsedive::API.new(api_key)
38
+ def client
39
+ @client ||= Clients::PulseDive.new(api_key: api_key)
39
40
  end
40
41
 
41
42
  #
@@ -55,12 +56,12 @@ module Mihari
55
56
  def search
56
57
  raise InvalidInputError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
57
58
 
58
- indicator = api.indicator.get_by_value(query)
59
+ indicator = client.get_indicator(query)
59
60
  iid = indicator["iid"]
60
61
 
61
- properties = api.indicator.get_properties_by_id(iid)
62
+ properties = client.get_properties(iid)
62
63
  (properties["dns"] || []).filter_map do |property|
63
- if ["A", "PTR"].include?(property["name"])
64
+ if %w[A PTR].include?(property["name"])
64
65
  nil
65
66
  else
66
67
  data = property["value"]
@@ -7,7 +7,6 @@ module Mihari
7
7
  "censys" => Censys,
8
8
  "circl" => CIRCL,
9
9
  "crtsh" => Crtsh,
10
- "dnpedia" => DNPedia,
11
10
  "dnstwister" => DNSTwister,
12
11
  "feed" => Feed,
13
12
  "greynoise" => GreyNoise,
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "securitytrails"
4
-
5
3
  module Mihari
6
4
  module Analyzers
7
5
  class SecurityTrails < 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
 
@@ -34,8 +35,8 @@ module Mihari
34
35
  %w[securitytrails_api_key]
35
36
  end
36
37
 
37
- def api
38
- @api ||= ::SecurityTrails::API.new(api_key)
38
+ def client
39
+ @client ||= Clients::SecurityTrails.new(api_key: api_key)
39
40
  end
40
41
 
41
42
  #
@@ -71,8 +72,7 @@ module Mihari
71
72
  # @return [Array<String>]
72
73
  #
73
74
  def domain_search
74
- result = api.history.get_all_dns_history(query, type: "a")
75
- records = result["records"] || []
75
+ records = client.get_all_dns_history(query, type: "a")
76
76
  records.map do |record|
77
77
  (record["values"] || []).map { |value| value["ip"] }
78
78
  end.flatten.compact.uniq
@@ -84,8 +84,7 @@ module Mihari
84
84
  # @return [Array<Mihari::Artifact>]
85
85
  #
86
86
  def ip_search
87
- result = api.domains.search(filter: { ipv4: query })
88
- records = result["records"] || []
87
+ records = client.search_by_ip(query)
89
88
  records.filter_map do |record|
90
89
  data = record["hostname"]
91
90
  Artifact.new(data: data, source: source, metadata: record)
@@ -98,8 +97,7 @@ module Mihari
98
97
  # @return [Array<String>]
99
98
  #
100
99
  def mail_search
101
- result = api.domains.search(filter: { whois_email: query })
102
- records = result["records"] || []
100
+ records = client.search_by_mail(query)
103
101
  records.filter_map do |record|
104
102
  data = record["hostname"]
105
103
  Artifact.new(data: data, source: source, metadata: record)
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "shodan"
4
-
5
3
  module Mihari
6
4
  module Analyzers
7
5
  class Shodan < Base
@@ -12,6 +10,12 @@ module Mihari
12
10
  # @return [String, nil]
13
11
  attr_reader :api_key
14
12
 
13
+ # @return [Integer]
14
+ attr_reader :interval
15
+
16
+ # @return [String]
17
+ attr_reader :query
18
+
15
19
  def initialize(*args, **kwargs)
16
20
  super(*args, **kwargs)
17
21
 
@@ -20,13 +24,9 @@ module Mihari
20
24
 
21
25
  def artifacts
22
26
  results = search
23
- return [] unless results || results.empty?
27
+ return [] if results.empty?
24
28
 
25
- results = results.map { |result| Structs::Shodan::Result.from_dynamic!(result) }
26
- matches = results.map { |result| result.matches || [] }.flatten
27
-
28
- uniq_matches = matches.uniq(&:ip_str)
29
- uniq_matches.map { |match| build_artifact(match, matches) }
29
+ results.map { |result| result.to_artifacts(source) }.flatten.uniq(&:data)
30
30
  end
31
31
 
32
32
  private
@@ -37,40 +37,32 @@ module Mihari
37
37
  %w[shodan_api_key]
38
38
  end
39
39
 
40
- def api
41
- @api ||= ::Shodan::API.new(key: api_key)
40
+ def client
41
+ @client ||= Clients::Shodan.new(api_key: api_key)
42
42
  end
43
43
 
44
44
  #
45
45
  # Search with pagination
46
46
  #
47
- # @param [String] query
48
47
  # @param [Integer] page
49
48
  #
50
- # @return [Hash]
49
+ # @return [Structs::Shodan::Result]
51
50
  #
52
- def search_with_page(query, page: 1)
53
- api.host.search(query, page: page)
54
- rescue ::Shodan::Error => e
55
- raise RetryableError, e if e.message.include?("request timed out")
56
-
57
- raise e
51
+ def search_with_page(page: 1)
52
+ client.search(query, page: page)
58
53
  end
59
54
 
60
55
  #
61
56
  # Search
62
57
  #
63
- # @return [Array<Hash>]
58
+ # @return [Array<Structs::Shodan::Result>]
64
59
  #
65
60
  def search
66
61
  responses = []
67
62
  (1..Float::INFINITY).each do |page|
68
- res = search_with_page(query, page: page)
69
-
70
- break unless res
71
-
63
+ res = search_with_page(page: page)
72
64
  responses << res
73
- break if res["total"].to_i <= page * PAGE_SIZE
65
+ break if res.total <= page * PAGE_SIZE
74
66
 
75
67
  # sleep #{interval} seconds to avoid the rate limitation (if it is set)
76
68
  sleep interval
@@ -82,42 +74,6 @@ module Mihari
82
74
  responses
83
75
  end
84
76
 
85
- #
86
- # Collect metadata from matches
87
- #
88
- # @param [Array<Structs::Shodan::Match>] matches
89
- # @param [String] ip
90
- #
91
- # @return [Array<Hash>]
92
- #
93
- def collect_metadata_by_ip(matches, ip)
94
- matches.select { |match| match.ip_str == ip }.map(&:metadata)
95
- end
96
-
97
- #
98
- # Collect ports from matches
99
- #
100
- # @param [Array<Structs::Shodan::Match>] matches
101
- # @param [String] ip
102
- #
103
- # @return [Array<String>]
104
- #
105
- def collect_ports_by_ip(matches, ip)
106
- matches.select { |match| match.ip_str == ip }.map(&:port)
107
- end
108
-
109
- #
110
- # Collect hostnames from matches
111
- #
112
- # @param [Array<Structs::Shodan::Match>] matches
113
- # @param [String] ip
114
- #
115
- # @return [Array<String>]
116
- #
117
- def collect_hostnames_by_ip(matches, ip)
118
- matches.select { |match| match.ip_str == ip }.map(&:hostnames).flatten.uniq
119
- end
120
-
121
77
  #
122
78
  # Build an artifact from a Shodan search API response
123
79
  #
@@ -127,36 +83,6 @@ module Mihari
127
83
  # @return [Artifact]
128
84
  #
129
85
  def build_artifact(match, matches)
130
- as = nil
131
- as = AutonomousSystem.new(asn: normalize_asn(match.asn)) unless match.asn.nil?
132
-
133
- geolocation = nil
134
- if !match.location.country_name.nil? && !match.location.country_code.nil?
135
- geolocation = Geolocation.new(
136
- country: match.location.country_name,
137
- country_code: match.location.country_code
138
- )
139
- end
140
-
141
- metadata = collect_metadata_by_ip(matches, match.ip_str)
142
-
143
- ports = collect_ports_by_ip(matches, match.ip_str).map do |port|
144
- Port.new(port: port)
145
- end
146
-
147
- reverse_dns_names = collect_hostnames_by_ip(matches, match.ip_str).map do |name|
148
- ReverseDnsName.new(name: name)
149
- end
150
-
151
- Artifact.new(
152
- data: match.ip_str,
153
- source: source,
154
- metadata: metadata,
155
- autonomous_system: as,
156
- geolocation: geolocation,
157
- ports: ports,
158
- reverse_dns_names: reverse_dns_names
159
- )
160
86
  end
161
87
  end
162
88
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "urlscan"
4
-
5
3
  module Mihari
6
4
  module Analyzers
7
5
  class Urlscan < Base
@@ -17,10 +15,22 @@ module Mihari
17
15
  # @return [String, nil]
18
16
  attr_reader :api_key
19
17
 
18
+ # @return [String]
19
+ attr_reader :query
20
+
21
+ # @return [Integer]
22
+ attr_reader :interval
23
+
24
+ # @return [String]
25
+ attr_reader :allowed_data_types
26
+
20
27
  def initialize(*args, **kwargs)
21
28
  super
22
29
 
23
- raise InvalidInputError, "allowed_data_types should be any of url, domain and ip." unless valid_alllowed_data_types?
30
+ unless valid_alllowed_data_types?
31
+ raise InvalidInputError,
32
+ "allowed_data_types should be any of url, domain and ip."
33
+ end
24
34
 
25
35
  @api_key = kwargs[:api_key] || Mihari.config.urlscan_api_key
26
36
  end
@@ -44,8 +54,8 @@ module Mihari
44
54
  %w[urlscan_api_key]
45
55
  end
46
56
 
47
- def api
48
- @api ||= ::UrlScan::API.new(api_key)
57
+ def client
58
+ @client ||= Clients::UrlScan.new(api_key: api_key)
49
59
  end
50
60
 
51
61
  #
@@ -54,7 +64,7 @@ module Mihari
54
64
  # @return [Structs::Urlscan::Response]
55
65
  #
56
66
  def search_with_search_after(search_after: nil)
57
- res = api.search(query, size: SIZE, search_after: search_after)
67
+ res = client.search(query, size: SIZE, search_after: search_after)
58
68
  Structs::Urlscan::Response.from_dynamic! res
59
69
  end
60
70
 
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "virustotal"
4
-
5
3
  module Mihari
6
4
  module Analyzers
7
5
  class VirusTotal < Base
@@ -9,11 +7,15 @@ module Mihari
9
7
 
10
8
  param :query
11
9
 
10
+ # @return [String]
12
11
  attr_reader :type
13
12
 
14
13
  # @return [String, nil]
15
14
  attr_reader :api_key
16
15
 
16
+ # @return [String]
17
+ attr_reader :query
18
+
17
19
  def initialize(*args, **kwargs)
18
20
  super(*args, **kwargs)
19
21
 
@@ -33,8 +35,8 @@ module Mihari
33
35
  %w[virustotal_api_key]
34
36
  end
35
37
 
36
- def api
37
- @api = ::VirusTotal::API.new(key: api_key)
38
+ def client
39
+ @client = Clients::VirusTotal.new(api_key: api_key)
38
40
  end
39
41
 
40
42
  #
@@ -68,7 +70,7 @@ module Mihari
68
70
  # @return [Array<Mihari::Artifact>]
69
71
  #
70
72
  def domain_search
71
- res = api.domain.resolutions(query)
73
+ res = client.domain_search(query)
72
74
 
73
75
  data = res["data"] || []
74
76
  data.filter_map do |item|
@@ -83,7 +85,7 @@ module Mihari
83
85
  # @return [Array<Mihari::Artifact>]
84
86
  #
85
87
  def ip_search
86
- res = api.ip_address.resolutions(query)
88
+ res = client.ip_search(query)
87
89
 
88
90
  data = res["data"] || []
89
91
  data.filter_map do |item|
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "virustotal"
4
-
5
3
  module Mihari
6
4
  module Analyzers
7
5
  class VirusTotalIntelligence < 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
17
21
 
@@ -21,7 +25,7 @@ module Mihari
21
25
  end
22
26
 
23
27
  def artifacts
24
- responses = search_witgh_cursor
28
+ responses = search_with_cursor
25
29
  responses.map do |response|
26
30
  response.data.map do |datum|
27
31
  Artifact.new(data: datum.value, source: source, metadata: datum.metadata)
@@ -40,8 +44,8 @@ module Mihari
40
44
  #
41
45
  # @return [::VirusTotal::API]
42
46
  #
43
- def api
44
- @api = ::VirusTotal::API.new(key: api_key)
47
+ def client
48
+ @client = Clients::VirusTotal.new(api_key: api_key)
45
49
  end
46
50
 
47
51
  #
@@ -49,12 +53,13 @@ module Mihari
49
53
  #
50
54
  # @return [Array<Structs::VirusTotalIntelligence::Response>]
51
55
  #
52
- def search_witgh_cursor
56
+ def search_with_cursor
53
57
  cursor = nil
54
58
  responses = []
55
59
 
56
60
  loop do
57
- response = Structs::VirusTotalIntelligence::Response.from_dynamic!(api.intelligence.search(query, cursor: cursor))
61
+ response = Structs::VirusTotalIntelligence::Response.from_dynamic!(client.intel_search(query,
62
+ cursor: cursor))
58
63
  responses << response
59
64
 
60
65
  break if response.meta.cursor.nil?
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "zoomeye"
4
-
5
3
  module Mihari
6
4
  module Analyzers
7
5
  class ZoomEye < Base
@@ -14,6 +12,15 @@ module Mihari
14
12
  # @return [String, nil]
15
13
  attr_reader :api_key
16
14
 
15
+ # @return [String]
16
+ attr_reader :query
17
+
18
+ # @return [String]
19
+ attr_reader :type
20
+
21
+ # @return [Integer]
22
+ attr_reader :interval
23
+
17
24
  def initialize(*args, **kwargs)
18
25
  super(*args, **kwargs)
19
26
 
@@ -48,8 +55,8 @@ module Mihari
48
55
  %w[zoomeye_api_key]
49
56
  end
50
57
 
51
- def api
52
- @api ||= ::ZoomEye::API.new(api_key: api_key)
58
+ def client
59
+ @client ||= Clients::ZoomEye.new(api_key: api_key)
53
60
  end
54
61
 
55
62
  #
@@ -83,9 +90,7 @@ module Mihari
83
90
  # @return [Hash, nil]
84
91
  #
85
92
  def _host_search(query, page: 1)
86
- api.host.search(query, page: page)
87
- rescue ::ZoomEye::Error => _e
88
- nil
93
+ client.host_search(query, page: page)
89
94
  end
90
95
 
91
96
  #
@@ -118,9 +123,7 @@ module Mihari
118
123
  # @return [Hash, nil]
119
124
  #
120
125
  def _web_search(query, page: 1)
121
- api.web.search(query, page: page)
122
- rescue ::ZoomEye::Error => _e
123
- nil
126
+ client.web_search(query, page: page)
124
127
  end
125
128
 
126
129
  #
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mihari
4
+ module Clients
5
+ class Base
6
+ # @return [String]
7
+ attr_reader :base_url
8
+
9
+ # @return [Hash]
10
+ attr_reader :headers
11
+
12
+ #
13
+ # @param [String] base_url
14
+ # @param [Hash] headers
15
+ #
16
+ def initialize(base_url, headers: {})
17
+ @base_url = base_url
18
+ @headers = headers || {}
19
+ end
20
+
21
+ private
22
+
23
+ #
24
+ # @param [String] path
25
+ #
26
+ # @return [String]
27
+ #
28
+ def url_for(path)
29
+ base_url + path
30
+ end
31
+
32
+ #
33
+ # @param [String] path
34
+ # @param [Hashk, nil] params
35
+ #
36
+ # @return [String] <description>
37
+ #
38
+ def get(path, params: nil)
39
+ HTTP.get(url_for(path), headers: headers, params: params)
40
+ end
41
+
42
+ #
43
+ # @param [String] path
44
+ # @param [Hash, nil] json
45
+ #
46
+ # @return [String] <description>
47
+ #
48
+ def post(path, json: {})
49
+ HTTP.post(url_for(path), headers: headers, json: json)
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mihari
4
+ module Clients
5
+ class BinaryEdge < Base
6
+ #
7
+ # @param [String] base_url
8
+ # @param [String, nil] api_key
9
+ # @param [Hash] headers
10
+ #
11
+ def initialize(base_url = "https://api.binaryedge.io/v2", api_key:, headers: {})
12
+ raise(ArgumentError, "'api_key' argument is required") unless api_key
13
+
14
+ headers["x-key"] = api_key
15
+
16
+ super(base_url, headers: headers)
17
+ end
18
+
19
+ #
20
+ # @param [String] query String used to query our data
21
+ # @param [Integer] page Default 1, Maximum: 500
22
+ # @param [Integer, nil] only_ips If selected, only output IP addresses, ports and protocols.
23
+ #
24
+ # @return [Hash]
25
+ #
26
+ def search(query, page: 1, only_ips: nil)
27
+ params = {
28
+ query: query,
29
+ page: page,
30
+ only_ips: only_ips
31
+ }.compact
32
+
33
+ res = get("/query/search", params: params)
34
+ JSON.parse(res.body.to_s)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "base64"
4
+
5
+ module Mihari
6
+ module Clients
7
+ class Censys < Base
8
+ #
9
+ # @param [String] base_url
10
+ # @param [String, nil] id
11
+ # @param [String, nil] secret
12
+ # @param [Hash] headers
13
+ #
14
+ def initialize(base_url = "https://search.censys.io", id:, secret:, headers: {})
15
+ raise(ArgumentError, "'id' argument is required") if id.nil?
16
+ raise(ArgumentError, "'secret' argument is required") if secret.nil?
17
+
18
+ headers["authorization"] = "Basic #{Base64.strict_encode64("#{id}:#{secret}")}"
19
+
20
+ super(base_url, headers: headers)
21
+ end
22
+
23
+ #
24
+ # Search current index.
25
+ #
26
+ # Searches the given index for all records that match the given query.
27
+ # For more details, see our documentation: https://search.censys.io/api/v2/docs
28
+ #
29
+ # @param [String] query the query to be executed.
30
+ # @params [Integer, nil] per_page the number of results to be returned for each page.
31
+ # @params [Integer, nil] cursor the cursor of the desired result set.
32
+ #
33
+ # @return [Structs::Censys::Response]
34
+ #
35
+ def search(query, per_page: nil, cursor: nil)
36
+ params = { q: query, per_page: per_page, cursor: cursor }.compact
37
+ res = get("/api/v2/hosts/search", params: params)
38
+ Structs::Censys::Response.from_dynamic! JSON.parse(res.body.to_s)
39
+ end
40
+ end
41
+ end
42
+ end