mihari 5.4.2 → 5.4.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/frontend/package-lock.json +2399 -1504
- data/frontend/package.json +22 -22
- data/lib/mihari/analyzers/base.rb +25 -14
- data/lib/mihari/analyzers/binaryedge.rb +2 -48
- data/lib/mihari/analyzers/censys.rb +4 -20
- data/lib/mihari/analyzers/circl.rb +3 -27
- data/lib/mihari/analyzers/crtsh.rb +2 -17
- data/lib/mihari/analyzers/dnstwister.rb +2 -4
- data/lib/mihari/analyzers/greynoise.rb +5 -4
- data/lib/mihari/analyzers/hunterhow.rb +8 -23
- data/lib/mihari/analyzers/onyphe.rb +5 -39
- data/lib/mihari/analyzers/otx.rb +3 -39
- data/lib/mihari/analyzers/passivetotal.rb +4 -42
- data/lib/mihari/analyzers/pulsedive.rb +1 -1
- data/lib/mihari/analyzers/rule.rb +18 -13
- data/lib/mihari/analyzers/securitytrails.rb +4 -42
- data/lib/mihari/analyzers/shodan.rb +7 -39
- data/lib/mihari/analyzers/urlscan.rb +3 -39
- data/lib/mihari/analyzers/virustotal.rb +1 -1
- data/lib/mihari/analyzers/virustotal_intelligence.rb +2 -25
- data/lib/mihari/analyzers/zoomeye.rb +18 -84
- data/lib/mihari/clients/base.rb +9 -1
- data/lib/mihari/clients/binaryedge.rb +26 -4
- data/lib/mihari/clients/censys.rb +32 -2
- data/lib/mihari/clients/circl.rb +28 -1
- data/lib/mihari/clients/crtsh.rb +7 -2
- data/lib/mihari/clients/dnstwister.rb +4 -2
- data/lib/mihari/clients/greynoise.rb +31 -4
- data/lib/mihari/clients/hunterhow.rb +41 -3
- data/lib/mihari/clients/onyphe.rb +25 -3
- data/lib/mihari/clients/otx.rb +40 -0
- data/lib/mihari/clients/passivetotal.rb +33 -15
- data/lib/mihari/clients/publsedive.rb +1 -1
- data/lib/mihari/clients/securitytrails.rb +44 -0
- data/lib/mihari/clients/shodan.rb +31 -3
- data/lib/mihari/clients/urlscan.rb +32 -6
- data/lib/mihari/clients/virustotal.rb +29 -4
- data/lib/mihari/clients/zoomeye.rb +53 -2
- data/lib/mihari/commands/alert.rb +42 -13
- data/lib/mihari/commands/rule.rb +11 -7
- data/lib/mihari/commands/search.rb +54 -22
- data/lib/mihari/commands/web.rb +1 -1
- data/lib/mihari/config.rb +6 -1
- data/lib/mihari/emitters/base.rb +9 -3
- data/lib/mihari/emitters/slack.rb +1 -1
- data/lib/mihari/enrichers/base.rb +13 -0
- data/lib/mihari/enrichers/google_public_dns.rb +16 -1
- data/lib/mihari/enrichers/ipinfo.rb +9 -13
- data/lib/mihari/enrichers/shodan.rb +1 -2
- data/lib/mihari/enrichers/whois.rb +2 -2
- data/lib/mihari/errors.rb +16 -10
- data/lib/mihari/feed/parser.rb +2 -2
- data/lib/mihari/models/artifact.rb +1 -1
- data/lib/mihari/models/autonomous_system.rb +11 -5
- data/lib/mihari/models/cpe.rb +10 -4
- data/lib/mihari/models/dns.rb +11 -16
- data/lib/mihari/models/geolocation.rb +11 -5
- data/lib/mihari/models/port.rb +10 -4
- data/lib/mihari/models/reverse_dns.rb +10 -4
- data/lib/mihari/models/whois.rb +4 -1
- data/lib/mihari/schemas/analyzer.rb +1 -0
- data/lib/mihari/services/alert_builder.rb +43 -0
- data/lib/mihari/services/alert_proxy.rb +7 -25
- data/lib/mihari/services/alert_runner.rb +9 -0
- data/lib/mihari/services/rule_builder.rb +47 -0
- data/lib/mihari/services/rule_proxy.rb +5 -61
- data/lib/mihari/services/rule_runner.rb +9 -4
- data/lib/mihari/structs/binaryedge.rb +89 -0
- data/lib/mihari/structs/censys.rb +11 -11
- data/lib/mihari/structs/greynoise.rb +17 -8
- data/lib/mihari/structs/onyphe.rb +7 -7
- data/lib/mihari/structs/shodan.rb +7 -6
- data/lib/mihari/structs/urlscan.rb +4 -6
- data/lib/mihari/structs/virustotal_intelligence.rb +4 -6
- data/lib/mihari/type_checker.rb +1 -1
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/endpoints/alerts.rb +33 -15
- data/lib/mihari/web/endpoints/artifacts.rb +53 -25
- data/lib/mihari/web/endpoints/configs.rb +2 -2
- data/lib/mihari/web/endpoints/ip_addresses.rb +3 -5
- data/lib/mihari/web/endpoints/rules.rb +97 -71
- data/lib/mihari/web/endpoints/tags.rb +15 -5
- data/lib/mihari/web/public/assets/index-ef33a6cd.js +1738 -0
- data/lib/mihari/web/public/index.html +1 -1
- data/lib/mihari/web/public/redoc-static.html +419 -382
- data/lib/mihari.rb +4 -0
- data/mihari.gemspec +10 -9
- metadata +38 -21
- data/lib/mihari/web/public/assets/index-4d7eda9f.js +0 -1738
@@ -3,32 +3,59 @@
|
|
3
3
|
module Mihari
|
4
4
|
module Clients
|
5
5
|
class GreyNoise < Base
|
6
|
+
PAGE_SIZE = 10_000
|
7
|
+
|
6
8
|
#
|
7
9
|
# @param [String] base_url
|
8
10
|
# @param [String, nil] api_key
|
9
11
|
# @param [Hash] headers
|
12
|
+
# @param [Integer, nil] interval
|
10
13
|
#
|
11
|
-
def initialize(base_url = "https://api.greynoise.io", api_key:, headers: {})
|
14
|
+
def initialize(base_url = "https://api.greynoise.io", api_key:, headers: {}, interval: nil)
|
12
15
|
raise(ArgumentError, "'api_key' argument is required") unless api_key
|
13
16
|
|
14
17
|
headers["key"] = api_key
|
15
|
-
super(base_url, headers: headers)
|
18
|
+
super(base_url, headers: headers, interval: interval)
|
16
19
|
end
|
17
20
|
|
18
21
|
#
|
19
22
|
# GNQL (GreyNoise Query Language) is a domain-specific query language that uses Lucene deep under the hood
|
20
23
|
#
|
21
24
|
# @param [String] query GNQL query string
|
22
|
-
# @param [Integer
|
25
|
+
# @param [Integer] size Maximum amount of results to grab
|
23
26
|
# @param [Integer, nil] scroll Scroll token to paginate through results
|
24
27
|
#
|
25
28
|
# @return [Hash]
|
26
29
|
#
|
27
|
-
def gnql_search(query, size:
|
30
|
+
def gnql_search(query, size: PAGE_SIZE, scroll: nil)
|
28
31
|
params = { query: query, size: size, scroll: scroll }.compact
|
29
32
|
res = get("/v2/experimental/gnql", params: params)
|
30
33
|
Structs::GreyNoise::Response.from_dynamic! JSON.parse(res.body.to_s)
|
31
34
|
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# @param [String] query
|
38
|
+
# @param [Integer] size
|
39
|
+
# @param [Integer] pagination_limit
|
40
|
+
#
|
41
|
+
# @return [Enumerable<Structs::GreyNoise::Response>]
|
42
|
+
#
|
43
|
+
def gnql_search_with_pagination(query, size: PAGE_SIZE, pagination_limit: Mihari.config.pagination_limit)
|
44
|
+
scroll = nil
|
45
|
+
|
46
|
+
Enumerator.new do |y|
|
47
|
+
pagination_limit.times do
|
48
|
+
res = gnql_search(query, size: size, scroll: scroll)
|
49
|
+
|
50
|
+
y.yield res
|
51
|
+
|
52
|
+
scroll = res.scroll
|
53
|
+
break if scroll.nil?
|
54
|
+
|
55
|
+
sleep_interval
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
32
59
|
end
|
33
60
|
end
|
34
61
|
end
|
@@ -5,6 +5,8 @@ require "base64"
|
|
5
5
|
module Mihari
|
6
6
|
module Clients
|
7
7
|
class HunterHow < Base
|
8
|
+
PAGE_SIZE = 100
|
9
|
+
|
8
10
|
# @return [String]
|
9
11
|
attr_reader :api_key
|
10
12
|
|
@@ -12,11 +14,12 @@ module Mihari
|
|
12
14
|
# @param [String] base_url
|
13
15
|
# @param [String, nil] api_key
|
14
16
|
# @param [Hash] headers
|
17
|
+
# @param [Integer, nil] interval
|
15
18
|
#
|
16
|
-
def initialize(base_url = "https://api.hunter.how/", api_key:, headers: {})
|
19
|
+
def initialize(base_url = "https://api.hunter.how/", api_key:, headers: {}, interval: nil)
|
17
20
|
raise(ArgumentError, "'api_key' argument is required") unless api_key
|
18
21
|
|
19
|
-
super(base_url, headers: headers)
|
22
|
+
super(base_url, headers: headers, interval: interval)
|
20
23
|
|
21
24
|
@api_key = api_key
|
22
25
|
end
|
@@ -30,7 +33,7 @@ module Mihari
|
|
30
33
|
#
|
31
34
|
# @return [Structs::HunterHow::Response]
|
32
35
|
#
|
33
|
-
def search(query, start_time:, end_time:, page: 1, page_size:
|
36
|
+
def search(query, start_time:, end_time:, page: 1, page_size: PAGE_SIZE)
|
34
37
|
params = {
|
35
38
|
query: Base64.urlsafe_encode64(query),
|
36
39
|
page: page,
|
@@ -42,6 +45,41 @@ module Mihari
|
|
42
45
|
res = get("/search", params: params)
|
43
46
|
Structs::HunterHow::Response.from_dynamic! JSON.parse(res.body.to_s)
|
44
47
|
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# @param [String] query String used to query our data
|
51
|
+
# @param [Integer] page_size Default 100, Maximum: 100
|
52
|
+
# @param [Integer] pagination_limit
|
53
|
+
# @param [String] start_time
|
54
|
+
# @param [String] end_time
|
55
|
+
#
|
56
|
+
# @return [Enumerable<Structs::HunterHow::Response>]
|
57
|
+
#
|
58
|
+
def search_with_pagination(
|
59
|
+
query,
|
60
|
+
start_time:,
|
61
|
+
end_time:,
|
62
|
+
page_size: PAGE_SIZE,
|
63
|
+
pagination_limit: Mihari.config.pagination_limit
|
64
|
+
)
|
65
|
+
Enumerator.new do |y|
|
66
|
+
(1..pagination_limit).each do |page|
|
67
|
+
res = search(
|
68
|
+
query,
|
69
|
+
start_time: start_time,
|
70
|
+
end_time: end_time,
|
71
|
+
page: page,
|
72
|
+
page_size: page_size
|
73
|
+
)
|
74
|
+
|
75
|
+
y.yield res
|
76
|
+
|
77
|
+
break if res.data.list.length < page_size
|
78
|
+
|
79
|
+
sleep_interval
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
45
83
|
end
|
46
84
|
end
|
47
85
|
end
|
@@ -3,6 +3,8 @@
|
|
3
3
|
module Mihari
|
4
4
|
module Clients
|
5
5
|
class Onyphe < Base
|
6
|
+
PAGE_SIZE = 10
|
7
|
+
|
6
8
|
# @return [String]
|
7
9
|
attr_reader :api_key
|
8
10
|
|
@@ -11,10 +13,10 @@ module Mihari
|
|
11
13
|
# @param [String, nil] api_key
|
12
14
|
# @param [Hash] headers
|
13
15
|
#
|
14
|
-
def initialize(base_url = "https://www.onyphe.io", api_key:, headers: {})
|
16
|
+
def initialize(base_url = "https://www.onyphe.io", api_key:, headers: {}, interval: nil)
|
15
17
|
raise(ArgumentError, "'api_key' argument is required") if api_key.nil?
|
16
18
|
|
17
|
-
super(base_url, headers: headers)
|
19
|
+
super(base_url, headers: headers, interval: interval)
|
18
20
|
|
19
21
|
@api_key = api_key
|
20
22
|
end
|
@@ -23,13 +25,33 @@ module Mihari
|
|
23
25
|
# @param [String] query
|
24
26
|
# @param [Integer] page
|
25
27
|
#
|
26
|
-
# @return [
|
28
|
+
# @return [Structs::Onyphe::Response]
|
27
29
|
#
|
28
30
|
def datascan(query, page: 1)
|
29
31
|
params = { page: page, apikey: api_key }
|
30
32
|
res = get("/api/v2/simple/datascan/#{query}", params: params)
|
31
33
|
Structs::Onyphe::Response.from_dynamic! JSON.parse(res.body.to_s)
|
32
34
|
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# @param [String] query
|
38
|
+
# @param [Integer] pagination_limit
|
39
|
+
#
|
40
|
+
# @return [Enumerable<Structs::Onyphe::Response>]
|
41
|
+
#
|
42
|
+
def datascan_with_pagination(query, pagination_limit: Mihari.config.pagination_limit)
|
43
|
+
Enumerator.new do |y|
|
44
|
+
(1..pagination_limit).each do |page|
|
45
|
+
res = datascan(query, page: page)
|
46
|
+
|
47
|
+
y.yield res
|
48
|
+
|
49
|
+
break if res.total <= page * PAGE_SIZE
|
50
|
+
|
51
|
+
sleep_interval
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
33
55
|
end
|
34
56
|
end
|
35
57
|
end
|
data/lib/mihari/clients/otx.rb
CHANGED
@@ -15,6 +15,46 @@ module Mihari
|
|
15
15
|
super(base_url, headers: headers)
|
16
16
|
end
|
17
17
|
|
18
|
+
#
|
19
|
+
# Domain search
|
20
|
+
#
|
21
|
+
# @param [String] query
|
22
|
+
#
|
23
|
+
# @return [Array<String>]
|
24
|
+
#
|
25
|
+
def domain_search(query)
|
26
|
+
res = query_by_domain(query)
|
27
|
+
return [] if res.nil?
|
28
|
+
|
29
|
+
records = res["passive_dns"] || []
|
30
|
+
records.filter_map do |record|
|
31
|
+
record_type = record["record_type"]
|
32
|
+
address = record["address"]
|
33
|
+
|
34
|
+
address if record_type == "A"
|
35
|
+
end.uniq
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
# IP search
|
40
|
+
#
|
41
|
+
# @param [String] query
|
42
|
+
#
|
43
|
+
# @return [Array<String>]
|
44
|
+
#
|
45
|
+
def ip_search(query)
|
46
|
+
res = query_by_ip(query)
|
47
|
+
return [] if res.nil?
|
48
|
+
|
49
|
+
records = res["passive_dns"] || []
|
50
|
+
records.filter_map do |record|
|
51
|
+
record_type = record["record_type"]
|
52
|
+
hostname = record["hostname"]
|
53
|
+
|
54
|
+
hostname if record_type == "A"
|
55
|
+
end.uniq
|
56
|
+
end
|
57
|
+
|
18
58
|
#
|
19
59
|
# @param [String] ip
|
20
60
|
#
|
@@ -21,35 +21,53 @@ module Mihari
|
|
21
21
|
end
|
22
22
|
|
23
23
|
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
def ssl_search(query)
|
27
|
-
params = { query: query }
|
28
|
-
_get("/v2/ssl-certificate/history", params: params)
|
29
|
-
end
|
30
|
-
|
24
|
+
# Passive DNS search
|
31
25
|
#
|
32
26
|
# @param [String] query
|
33
27
|
#
|
34
|
-
# @return [
|
28
|
+
# @return [Array<String>]
|
35
29
|
#
|
36
30
|
def passive_dns_search(query)
|
37
31
|
params = { query: query }
|
38
|
-
_get("/v2/dns/passive/unique", params: params)
|
32
|
+
res = _get("/v2/dns/passive/unique", params: params)
|
33
|
+
res["results"] || []
|
39
34
|
end
|
40
35
|
|
41
36
|
#
|
42
|
-
#
|
43
|
-
# @param [String] field whether to return historical results
|
37
|
+
# Reverse whois search
|
44
38
|
#
|
45
|
-
# @
|
39
|
+
# @param [String] query
|
40
|
+
#
|
41
|
+
# @return [Array<Mihari::Artifact>]
|
46
42
|
#
|
47
|
-
def reverse_whois_search(query
|
43
|
+
def reverse_whois_search(query)
|
48
44
|
params = {
|
49
45
|
query: query,
|
50
|
-
field:
|
46
|
+
field: "email"
|
51
47
|
}.compact
|
52
|
-
_get("/v2/whois/search", params: params)
|
48
|
+
res = _get("/v2/whois/search", params: params)
|
49
|
+
results = res["results"] || []
|
50
|
+
results.map do |result|
|
51
|
+
data = result["domain"]
|
52
|
+
Artifact.new(data: data, metadata: result)
|
53
|
+
end.flatten
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# Passive SSL search
|
58
|
+
#
|
59
|
+
# @param [String] query
|
60
|
+
#
|
61
|
+
# @return [Array<Mihari::Artifact>]
|
62
|
+
#
|
63
|
+
def ssl_search(query)
|
64
|
+
params = { query: query }
|
65
|
+
res = _get("/v2/ssl-certificate/history", params: params)
|
66
|
+
results = res["results"] || []
|
67
|
+
results.map do |result|
|
68
|
+
data = result["ipAddresses"]
|
69
|
+
data.map { |d| Artifact.new(data: d, metadata: result) }
|
70
|
+
end.flatten
|
53
71
|
end
|
54
72
|
|
55
73
|
private
|
@@ -15,6 +15,50 @@ module Mihari
|
|
15
15
|
super(base_url, headers: headers)
|
16
16
|
end
|
17
17
|
|
18
|
+
#
|
19
|
+
# Domain search
|
20
|
+
#
|
21
|
+
# @param [String] query
|
22
|
+
#
|
23
|
+
# @return [Array<String>]
|
24
|
+
#
|
25
|
+
def domain_search(query)
|
26
|
+
records = get_all_dns_history(query, type: "a")
|
27
|
+
records.map do |record|
|
28
|
+
(record["values"] || []).map { |value| value["ip"] }
|
29
|
+
end.flatten.compact.uniq
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# IP search
|
34
|
+
#
|
35
|
+
# @param [String] query
|
36
|
+
#
|
37
|
+
# @return [Array<Mihari::Artifact>]
|
38
|
+
#
|
39
|
+
def ip_search(query)
|
40
|
+
records = search_by_ip(query)
|
41
|
+
records.filter_map do |record|
|
42
|
+
data = record["hostname"]
|
43
|
+
Artifact.new(data: data, metadata: record)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# Mail search
|
49
|
+
#
|
50
|
+
# @param [String] query
|
51
|
+
#
|
52
|
+
# @return [Array<String>]
|
53
|
+
#
|
54
|
+
def mail_search(query)
|
55
|
+
records = search_by_mail(query)
|
56
|
+
records.filter_map do |record|
|
57
|
+
data = record["hostname"]
|
58
|
+
Artifact.new(data: data, metadata: record)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
18
62
|
#
|
19
63
|
# @param [String] mail
|
20
64
|
#
|
@@ -3,6 +3,8 @@
|
|
3
3
|
module Mihari
|
4
4
|
module Clients
|
5
5
|
class Shodan < Base
|
6
|
+
PAGE_SIZE = 100
|
7
|
+
|
6
8
|
# @return [String]
|
7
9
|
attr_reader :api_key
|
8
10
|
|
@@ -10,11 +12,12 @@ module Mihari
|
|
10
12
|
# @param [String] base_url
|
11
13
|
# @param [String, nil] api_key
|
12
14
|
# @param [Hash] headers
|
15
|
+
# @param [Integer, nil] interval
|
13
16
|
#
|
14
|
-
def initialize(base_url = "https://api.shodan.io", api_key:, headers: {})
|
17
|
+
def initialize(base_url = "https://api.shodan.io", api_key:, headers: {}, interval: nil)
|
15
18
|
raise(ArgumentError, "'api_key' argument is required") unless api_key
|
16
19
|
|
17
|
-
super(base_url, headers: headers)
|
20
|
+
super(base_url, headers: headers, interval: interval)
|
18
21
|
|
19
22
|
@api_key = api_key
|
20
23
|
end
|
@@ -34,7 +37,32 @@ module Mihari
|
|
34
37
|
key: api_key
|
35
38
|
}
|
36
39
|
res = get("/shodan/host/search", params: params)
|
37
|
-
Structs::Shodan::
|
40
|
+
Structs::Shodan::Response.from_dynamic! JSON.parse(res.body.to_s)
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# @param [String] query
|
45
|
+
# @param [Boolean] minify
|
46
|
+
# @param [Integer] pagination_limit
|
47
|
+
#
|
48
|
+
# @return [Enumerable<Structs::Shodan::Response>]
|
49
|
+
#
|
50
|
+
def search_with_pagination(query, minify: true, pagination_limit: Mihari.config.pagination_limit)
|
51
|
+
Enumerator.new do |y|
|
52
|
+
(1..pagination_limit).each do |page|
|
53
|
+
res = search(query, page: page, minify: minify)
|
54
|
+
|
55
|
+
y.yield res
|
56
|
+
|
57
|
+
break if res.total <= page * PAGE_SIZE
|
58
|
+
|
59
|
+
sleep_interval
|
60
|
+
rescue JSON::ParserError
|
61
|
+
# ignore JSON::ParserError
|
62
|
+
# ref. https://github.com/ninoseki/mihari/issues/197
|
63
|
+
next
|
64
|
+
end
|
65
|
+
end
|
38
66
|
end
|
39
67
|
end
|
40
68
|
end
|
@@ -7,26 +7,52 @@ module Mihari
|
|
7
7
|
# @param [String] base_url
|
8
8
|
# @param [String, nil] api_key
|
9
9
|
# @param [Hash] headers
|
10
|
+
# @param [Interval, nil] interval
|
10
11
|
#
|
11
|
-
def initialize(base_url = "https://urlscan.io", api_key:, headers: {})
|
12
|
+
def initialize(base_url = "https://urlscan.io", api_key:, headers: {}, interval: nil)
|
12
13
|
raise(ArgumentError, "'api_key' argument is required") if api_key.nil?
|
13
14
|
|
14
15
|
headers["api-key"] = api_key
|
15
16
|
|
16
|
-
super(base_url, headers: headers)
|
17
|
+
super(base_url, headers: headers, interval: interval)
|
17
18
|
end
|
18
19
|
|
19
20
|
#
|
20
21
|
# @param [String] q
|
21
|
-
# @param [Integer] size
|
22
|
+
# @param [Integer, nil] size
|
22
23
|
# @param [String, nil] search_after
|
23
24
|
#
|
24
|
-
# @return [
|
25
|
+
# @return [Structs::Urlscan::Response]
|
25
26
|
#
|
26
|
-
def search(q, size:
|
27
|
+
def search(q, size: nil, search_after: nil)
|
27
28
|
params = { q: q, size: size, search_after: search_after }.compact
|
28
29
|
res = get("/api/v1/search/", params: params)
|
29
|
-
JSON.parse
|
30
|
+
Structs::Urlscan::Response.from_dynamic! JSON.parse(res.body.to_s)
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# @param [String] q
|
35
|
+
# @param [Integer, nil] size
|
36
|
+
# @param [Integer] pagination_limit
|
37
|
+
#
|
38
|
+
# @return [Enumerable<Structs::Urlscan::Response>]
|
39
|
+
#
|
40
|
+
def search_with_pagination(q, size: nil, pagination_limit: Mihari.config.pagination_limit)
|
41
|
+
search_after = nil
|
42
|
+
|
43
|
+
Enumerator.new do |y|
|
44
|
+
pagination_limit.times do
|
45
|
+
res = search(q, size: size, search_after: search_after)
|
46
|
+
|
47
|
+
y.yield res
|
48
|
+
|
49
|
+
break unless res.has_more
|
50
|
+
|
51
|
+
search_after = res.results.last.sort.join(",")
|
52
|
+
|
53
|
+
sleep_interval
|
54
|
+
end
|
55
|
+
end
|
30
56
|
end
|
31
57
|
end
|
32
58
|
end
|
@@ -7,13 +7,14 @@ module Mihari
|
|
7
7
|
# @param [String] base_url
|
8
8
|
# @param [String, nil] api_key
|
9
9
|
# @param [Hash] headers
|
10
|
+
# @param [Integer, nil] interval
|
10
11
|
#
|
11
|
-
def initialize(base_url = "https://www.virustotal.com", api_key:, headers: {})
|
12
|
+
def initialize(base_url = "https://www.virustotal.com", api_key:, headers: {}, interval: nil)
|
12
13
|
raise(ArgumentError, "'api_key' argument is required") if api_key.nil?
|
13
14
|
|
14
15
|
headers["x-apikey"] = api_key
|
15
16
|
|
16
|
-
super(base_url, headers: headers)
|
17
|
+
super(base_url, headers: headers, interval: interval)
|
17
18
|
end
|
18
19
|
|
19
20
|
#
|
@@ -38,11 +39,35 @@ module Mihari
|
|
38
39
|
# @param [String] query
|
39
40
|
# @param [String, nil] cursor
|
40
41
|
#
|
41
|
-
# @return [
|
42
|
+
# @return [Structs::VirusTotalIntelligence::Response]
|
42
43
|
#
|
43
44
|
def intel_search(query, cursor: nil)
|
44
45
|
params = { query: query, cursor: cursor }.compact
|
45
|
-
_get("/api/v3/intelligence/search", params: params)
|
46
|
+
res = _get("/api/v3/intelligence/search", params: params)
|
47
|
+
Structs::VirusTotalIntelligence::Response.from_dynamic! res
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# @param [String] query
|
52
|
+
# @param [Integer] pagination_limit
|
53
|
+
#
|
54
|
+
# @return [Enumerable<Structs::VirusTotalIntelligence::Response>]
|
55
|
+
#
|
56
|
+
def intel_search_with_pagination(query, pagination_limit: Mihari.config.pagination_limit)
|
57
|
+
cursor = nil
|
58
|
+
|
59
|
+
Enumerator.new do |y|
|
60
|
+
pagination_limit.times do
|
61
|
+
res = intel_search(query, cursor: cursor)
|
62
|
+
|
63
|
+
y.yield res
|
64
|
+
|
65
|
+
cursor = res.meta.cursor
|
66
|
+
break if cursor.nil?
|
67
|
+
|
68
|
+
sleep_interval
|
69
|
+
end
|
70
|
+
end
|
46
71
|
end
|
47
72
|
|
48
73
|
private
|
@@ -3,18 +3,21 @@
|
|
3
3
|
module Mihari
|
4
4
|
module Clients
|
5
5
|
class ZoomEye < Base
|
6
|
+
PAGE_SIZE = 10
|
7
|
+
|
6
8
|
attr_reader :api_key
|
7
9
|
|
8
10
|
#
|
9
11
|
# @param [String] base_url
|
10
12
|
# @param [String, nil] api_key
|
11
13
|
# @param [Hash] headers
|
14
|
+
# @param [Integer, nil] interval
|
12
15
|
#
|
13
|
-
def initialize(base_url = "https://api.zoomeye.org", api_key:, headers: {})
|
16
|
+
def initialize(base_url = "https://api.zoomeye.org", api_key:, headers: {}, interval: nil)
|
14
17
|
raise(ArgumentError, "'api_key' argument is required") unless api_key
|
15
18
|
|
16
19
|
headers["api-key"] = api_key
|
17
|
-
super(base_url, headers: headers)
|
20
|
+
super(base_url, headers: headers, interval: interval)
|
18
21
|
end
|
19
22
|
|
20
23
|
#
|
@@ -36,6 +39,30 @@ module Mihari
|
|
36
39
|
_get("/host/search", params: params)
|
37
40
|
end
|
38
41
|
|
42
|
+
#
|
43
|
+
# @param [String] query
|
44
|
+
# @param [String, nil] facets
|
45
|
+
# @param [Integer] pagination_limit
|
46
|
+
#
|
47
|
+
# @return [Enumerable<Hash>]
|
48
|
+
#
|
49
|
+
def host_search_with_pagination(query, facets: nil, pagination_limit: Mihari.config.pagination_limit)
|
50
|
+
Enumerator.new do |y|
|
51
|
+
(1..pagination_limit).each do |page|
|
52
|
+
res = host_search(query, facets: facets, page: page)
|
53
|
+
|
54
|
+
break if res.nil?
|
55
|
+
|
56
|
+
y.yield res
|
57
|
+
|
58
|
+
total = res["total"].to_i
|
59
|
+
break if total <= page * PAGE_SIZE
|
60
|
+
|
61
|
+
sleep_interval
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
39
66
|
#
|
40
67
|
# Search the Web technologies
|
41
68
|
#
|
@@ -55,6 +82,30 @@ module Mihari
|
|
55
82
|
_get("/web/search", params: params)
|
56
83
|
end
|
57
84
|
|
85
|
+
#
|
86
|
+
# @param [String] query
|
87
|
+
# @param [String, nil] facets
|
88
|
+
# @param [Integer] pagination_limit
|
89
|
+
#
|
90
|
+
# @return [Enumerable<Hash>]
|
91
|
+
#
|
92
|
+
def web_search_with_pagination(query, facets: nil, pagination_limit: Mihari.config.pagination_limit)
|
93
|
+
Enumerator.new do |y|
|
94
|
+
(1..pagination_limit).each do |page|
|
95
|
+
res = web_search(query, facets: facets, page: page)
|
96
|
+
|
97
|
+
break if res.nil?
|
98
|
+
|
99
|
+
y.yield res
|
100
|
+
|
101
|
+
total = res["total"].to_i
|
102
|
+
break if total <= page * PAGE_SIZE
|
103
|
+
|
104
|
+
sleep_interval
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
58
109
|
private
|
59
110
|
|
60
111
|
#
|