mihari 2.2.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +7 -0
- data/.overcommit.yml +12 -0
- data/README.md +7 -9
- data/exe/mihari +1 -1
- data/images/tines.png +0 -0
- data/lib/mihari.rb +89 -15
- data/lib/mihari/analyzers/base.rb +49 -8
- data/lib/mihari/analyzers/basic.rb +1 -2
- data/lib/mihari/analyzers/binaryedge.rb +7 -13
- data/lib/mihari/analyzers/censys.rb +26 -63
- data/lib/mihari/analyzers/circl.rb +20 -17
- data/lib/mihari/analyzers/crtsh.rb +6 -13
- data/lib/mihari/analyzers/dnpedia.rb +6 -12
- data/lib/mihari/analyzers/dnstwister.rb +13 -10
- data/lib/mihari/analyzers/onyphe.rb +6 -12
- data/lib/mihari/analyzers/otx.rb +22 -19
- data/lib/mihari/analyzers/passivetotal.rb +22 -21
- data/lib/mihari/analyzers/pulsedive.rb +16 -13
- data/lib/mihari/analyzers/rule.rb +99 -0
- data/lib/mihari/analyzers/securitytrails.rb +22 -19
- data/lib/mihari/analyzers/shodan.rb +7 -13
- data/lib/mihari/analyzers/spyse.rb +12 -19
- data/lib/mihari/analyzers/urlscan.rb +20 -30
- data/lib/mihari/analyzers/virustotal.rb +25 -22
- data/lib/mihari/analyzers/zoomeye.rb +16 -22
- data/lib/mihari/cli/analyzer.rb +44 -0
- data/lib/mihari/cli/base.rb +27 -0
- data/lib/mihari/cli/init.rb +13 -0
- data/lib/mihari/cli/main.rb +30 -0
- data/lib/mihari/cli/mixins/utils.rb +88 -0
- data/lib/mihari/cli/validator.rb +11 -0
- data/lib/mihari/commands/binaryedge.rb +1 -1
- data/lib/mihari/commands/censys.rb +1 -1
- data/lib/mihari/commands/circl.rb +2 -2
- data/lib/mihari/commands/crtsh.rb +1 -1
- data/lib/mihari/commands/dnpedia.rb +1 -1
- data/lib/mihari/commands/dnstwister.rb +2 -2
- data/lib/mihari/commands/init.rb +46 -0
- data/lib/mihari/commands/json.rb +1 -1
- data/lib/mihari/commands/onyphe.rb +1 -1
- data/lib/mihari/commands/otx.rb +2 -2
- data/lib/mihari/commands/passivetotal.rb +2 -2
- data/lib/mihari/commands/pulsedive.rb +2 -2
- data/lib/mihari/commands/search.rb +77 -0
- data/lib/mihari/commands/securitytrails.rb +2 -2
- data/lib/mihari/commands/shodan.rb +1 -1
- data/lib/mihari/commands/spyse.rb +1 -1
- data/lib/mihari/commands/urlscan.rb +2 -4
- data/lib/mihari/commands/validator.rb +38 -0
- data/lib/mihari/commands/virustotal.rb +2 -2
- data/lib/mihari/commands/zoomeye.rb +1 -1
- data/lib/mihari/constraints.rb +5 -0
- data/lib/mihari/database.rb +13 -2
- data/lib/mihari/emitters/base.rb +2 -2
- data/lib/mihari/emitters/database.rb +1 -1
- data/lib/mihari/emitters/misp.rb +1 -1
- data/lib/mihari/emitters/slack.rb +5 -9
- data/lib/mihari/emitters/the_hive.rb +1 -1
- data/lib/mihari/emitters/webhook.rb +53 -0
- data/lib/mihari/mixins/configurable.rb +38 -0
- data/lib/mihari/mixins/configuration.rb +85 -0
- data/lib/mihari/mixins/hash.rb +20 -0
- data/lib/mihari/mixins/refang.rb +21 -0
- data/lib/mihari/mixins/retriable.rb +27 -0
- data/lib/mihari/mixins/rule.rb +79 -0
- data/lib/mihari/models/alert.rb +28 -1
- data/lib/mihari/models/artifact.rb +10 -0
- data/lib/mihari/notifiers/base.rb +9 -1
- data/lib/mihari/notifiers/exception_notifier.rb +50 -0
- data/lib/mihari/notifiers/slack.rb +29 -1
- data/lib/mihari/schemas/configuration.rb +42 -0
- data/lib/mihari/schemas/macros.rb +17 -0
- data/lib/mihari/schemas/rule.rb +72 -0
- data/lib/mihari/serializers/artifact.rb +1 -1
- data/lib/mihari/status.rb +14 -0
- data/lib/mihari/templates/rule.yml.erb +19 -0
- data/lib/mihari/type_checker.rb +8 -3
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/app.rb +2 -0
- data/lib/mihari/web/controllers/alerts_controller.rb +12 -3
- data/lib/mihari/web/controllers/artifacts_controller.rb +1 -3
- data/lib/mihari/web/controllers/base_controller.rb +22 -0
- data/lib/mihari/web/controllers/command_controller.rb +3 -4
- data/lib/mihari/web/controllers/config_controller.rb +1 -3
- data/lib/mihari/web/controllers/sources_controller.rb +1 -3
- data/lib/mihari/web/controllers/tags_controller.rb +1 -3
- data/lib/mihari/web/helpers/json.rb +2 -0
- data/lib/mihari/web/public/index.html +1 -21
- data/lib/mihari/web/public/redoc-static.html +2 -2
- data/lib/mihari/web/public/static/js/app.ab213f7c.js +12 -0
- data/lib/mihari/web/public/static/js/app.ab213f7c.js.map +1 -0
- data/lib/mihari/web/public/static/js/{app.bcc595df.js → app.cccddb2b.js} +2 -2
- data/lib/mihari/web/public/static/js/app.cccddb2b.js.map +1 -0
- data/mihari.gemspec +20 -10
- metadata +180 -63
- data/.rubocop.yml +0 -161
- data/lib/mihari/analyzers/free_text.rb +0 -48
- data/lib/mihari/analyzers/http_hash.rb +0 -100
- data/lib/mihari/analyzers/passive_dns.rb +0 -59
- data/lib/mihari/analyzers/passive_ssl.rb +0 -55
- data/lib/mihari/analyzers/reverse_whois.rb +0 -55
- data/lib/mihari/analyzers/securitytrails_domain_feed.rb +0 -59
- data/lib/mihari/analyzers/ssh_fingerprint.rb +0 -58
- data/lib/mihari/cli.rb +0 -124
- data/lib/mihari/commands/config.rb +0 -27
- data/lib/mihari/commands/free_text.rb +0 -21
- data/lib/mihari/commands/http_hash.rb +0 -25
- data/lib/mihari/commands/passive_dns.rb +0 -21
- data/lib/mihari/commands/passive_ssl.rb +0 -21
- data/lib/mihari/commands/reverse_whois.rb +0 -21
- data/lib/mihari/commands/securitytrails_domain_feed.rb +0 -23
- data/lib/mihari/commands/ssh_fingerprint.rb +0 -21
- data/lib/mihari/config.rb +0 -84
- data/lib/mihari/configurable.rb +0 -21
- data/lib/mihari/html.rb +0 -43
- data/lib/mihari/retriable.rb +0 -17
- data/lib/mihari/slack_monkeypatch.rb +0 -16
- data/lib/mihari/web/public/static/js/app.bcc595df.js.map +0 -1
@@ -5,26 +5,29 @@ require "passive_circl"
|
|
5
5
|
module Mihari
|
6
6
|
module Analyzers
|
7
7
|
class CIRCL < Base
|
8
|
-
|
8
|
+
include Mixins::Refang
|
9
9
|
|
10
|
-
|
11
|
-
|
10
|
+
param :query
|
11
|
+
option :title, default: proc { "CIRCL passive DNS/SSL search" }
|
12
|
+
option :description, default: proc { "query = #{query}" }
|
13
|
+
option :tags, default: proc { [] }
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
+
attr_reader :type
|
16
|
+
|
17
|
+
def initialize(*args, **kwargs)
|
18
|
+
super
|
15
19
|
|
16
|
-
@
|
17
|
-
@
|
18
|
-
@tags = tags
|
20
|
+
@query = refang(query)
|
21
|
+
@type = TypeChecker.type(query)
|
19
22
|
end
|
20
23
|
|
21
24
|
def artifacts
|
22
|
-
|
25
|
+
search || []
|
23
26
|
end
|
24
27
|
|
25
28
|
private
|
26
29
|
|
27
|
-
def
|
30
|
+
def configuration_keys
|
28
31
|
%w[circl_passive_password circl_passive_username]
|
29
32
|
end
|
30
33
|
|
@@ -32,26 +35,26 @@ module Mihari
|
|
32
35
|
@api ||= ::PassiveCIRCL::API.new(username: Mihari.config.circl_passive_username, password: Mihari.config.circl_passive_password)
|
33
36
|
end
|
34
37
|
|
35
|
-
def
|
38
|
+
def search
|
36
39
|
case @type
|
37
40
|
when "domain"
|
38
|
-
|
41
|
+
passive_dns_search
|
39
42
|
when "hash"
|
40
|
-
|
43
|
+
passive_ssl_search
|
41
44
|
else
|
42
45
|
raise InvalidInputError, "#{@query}(type: #{@type || "unknown"}) is not supported."
|
43
46
|
end
|
44
47
|
end
|
45
48
|
|
46
|
-
def
|
49
|
+
def passive_dns_search
|
47
50
|
results = api.dns.query(@query)
|
48
|
-
results.
|
51
|
+
results.filter_map do |result|
|
49
52
|
type = result["rrtype"]
|
50
53
|
type == "A" ? result["rdata"] : nil
|
51
|
-
end.
|
54
|
+
end.uniq
|
52
55
|
end
|
53
56
|
|
54
|
-
def
|
57
|
+
def passive_ssl_search
|
55
58
|
result = api.ssl.cquery(@query)
|
56
59
|
seen = result["seen"] || []
|
57
60
|
seen.uniq
|
@@ -5,22 +5,15 @@ require "crtsh"
|
|
5
5
|
module Mihari
|
6
6
|
module Analyzers
|
7
7
|
class Crtsh < Base
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
@query = query
|
14
|
-
@title = title || "crt.sh lookup"
|
15
|
-
@description = description || "query = #{query}"
|
16
|
-
@tags = tags
|
17
|
-
|
18
|
-
@exclude_expired = exclude_expired.nil? ? true : exclude_expired
|
19
|
-
end
|
8
|
+
param :query
|
9
|
+
option :title, default: proc { "crt.sh search" }
|
10
|
+
option :description, default: proc { "query = #{query}" }
|
11
|
+
option :tags, default: proc { [] }
|
12
|
+
option :exclude_expired, default: proc { true }
|
20
13
|
|
21
14
|
def artifacts
|
22
15
|
results = search
|
23
|
-
name_values = results.
|
16
|
+
name_values = results.filter_map { |result| result["name_value"] }
|
24
17
|
name_values.map(&:lines).flatten.uniq.map(&:chomp)
|
25
18
|
end
|
26
19
|
|
@@ -5,19 +5,13 @@ require "dnpedia"
|
|
5
5
|
module Mihari
|
6
6
|
module Analyzers
|
7
7
|
class DNPedia < Base
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
@query = query
|
14
|
-
@title = title || "DNPedia domain lookup"
|
15
|
-
@description = description || "query = #{query}"
|
16
|
-
@tags = tags
|
17
|
-
end
|
8
|
+
param :query
|
9
|
+
option :title, default: proc { "DNPedia domain search" }
|
10
|
+
option :description, default: proc { "query = #{query}" }
|
11
|
+
option :tags, default: proc { [] }
|
18
12
|
|
19
13
|
def artifacts
|
20
|
-
|
14
|
+
search || []
|
21
15
|
end
|
22
16
|
|
23
17
|
private
|
@@ -26,7 +20,7 @@ module Mihari
|
|
26
20
|
@api ||= ::DNPedia::API.new
|
27
21
|
end
|
28
22
|
|
29
|
-
def
|
23
|
+
def search
|
30
24
|
res = api.search(query)
|
31
25
|
rows = res["rows"] || []
|
32
26
|
rows.map do |row|
|
@@ -7,21 +7,24 @@ require "parallel"
|
|
7
7
|
module Mihari
|
8
8
|
module Analyzers
|
9
9
|
class DNSTwister < Base
|
10
|
-
|
10
|
+
include Mixins::Refang
|
11
11
|
|
12
|
-
|
13
|
-
|
12
|
+
param :query
|
13
|
+
option :title, default: proc { "dnstwister domain search" }
|
14
|
+
option :description, default: proc { "query = #{query}" }
|
15
|
+
option :tags, default: proc { [] }
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
+
attr_reader :type
|
18
|
+
|
19
|
+
def initialize(*args, **kwargs)
|
20
|
+
super
|
17
21
|
|
18
|
-
@
|
19
|
-
@
|
20
|
-
@tags = tags
|
22
|
+
@query = refang(query)
|
23
|
+
@type = TypeChecker.type(query)
|
21
24
|
end
|
22
25
|
|
23
26
|
def artifacts
|
24
|
-
|
27
|
+
search || []
|
25
28
|
end
|
26
29
|
|
27
30
|
private
|
@@ -41,7 +44,7 @@ module Mihari
|
|
41
44
|
false
|
42
45
|
end
|
43
46
|
|
44
|
-
def
|
47
|
+
def search
|
45
48
|
raise InvalidInputError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
|
46
49
|
|
47
50
|
res = api.fuzz(query)
|
@@ -5,16 +5,10 @@ require "onyphe"
|
|
5
5
|
module Mihari
|
6
6
|
module Analyzers
|
7
7
|
class Onyphe < Base
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
@query = query
|
14
|
-
@title = title || "Onyphe lookup"
|
15
|
-
@description = description || "query = #{query}"
|
16
|
-
@tags = tags
|
17
|
-
end
|
8
|
+
param :query
|
9
|
+
option :title, default: proc { "Onyphe search" }
|
10
|
+
option :description, default: proc { "query = #{query}" }
|
11
|
+
option :tags, default: proc { [] }
|
18
12
|
|
19
13
|
def artifacts
|
20
14
|
results = search
|
@@ -24,14 +18,14 @@ module Mihari
|
|
24
18
|
result["results"]
|
25
19
|
end.flatten.compact
|
26
20
|
|
27
|
-
flat_results.
|
21
|
+
flat_results.filter_map { |result| result["ip"] }.uniq
|
28
22
|
end
|
29
23
|
|
30
24
|
private
|
31
25
|
|
32
26
|
PAGE_SIZE = 10
|
33
27
|
|
34
|
-
def
|
28
|
+
def configuration_keys
|
35
29
|
%w[onyphe_api_key]
|
36
30
|
end
|
37
31
|
|
data/lib/mihari/analyzers/otx.rb
CHANGED
@@ -5,26 +5,29 @@ require "otx_ruby"
|
|
5
5
|
module Mihari
|
6
6
|
module Analyzers
|
7
7
|
class OTX < Base
|
8
|
-
|
8
|
+
include Mixins::Refang
|
9
9
|
|
10
|
-
|
11
|
-
|
10
|
+
param :query
|
11
|
+
option :title, default: proc { "OTX search" }
|
12
|
+
option :description, default: proc { "query = #{query}" }
|
13
|
+
option :tags, default: proc { [] }
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
+
attr_reader :type
|
16
|
+
|
17
|
+
def initialize(*args, **kwargs)
|
18
|
+
super
|
15
19
|
|
16
|
-
@
|
17
|
-
@
|
18
|
-
@tags = tags
|
20
|
+
@query = refang(query)
|
21
|
+
@type = TypeChecker.type(query)
|
19
22
|
end
|
20
23
|
|
21
24
|
def artifacts
|
22
|
-
|
25
|
+
search || []
|
23
26
|
end
|
24
27
|
|
25
28
|
private
|
26
29
|
|
27
|
-
def
|
30
|
+
def configuration_keys
|
28
31
|
%w[otx_api_key]
|
29
32
|
end
|
30
33
|
|
@@ -40,29 +43,29 @@ module Mihari
|
|
40
43
|
%w[ip domain].include? type
|
41
44
|
end
|
42
45
|
|
43
|
-
def
|
46
|
+
def search
|
44
47
|
case type
|
45
48
|
when "domain"
|
46
|
-
|
49
|
+
domain_search
|
47
50
|
when "ip"
|
48
|
-
|
51
|
+
ip_search
|
49
52
|
else
|
50
53
|
raise InvalidInputError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
|
51
54
|
end
|
52
55
|
end
|
53
56
|
|
54
|
-
def
|
57
|
+
def domain_search
|
55
58
|
records = domain_client.get_passive_dns(query)
|
56
|
-
records.
|
59
|
+
records.filter_map do |record|
|
57
60
|
record.address if record.record_type == "A"
|
58
|
-
end.
|
61
|
+
end.uniq
|
59
62
|
end
|
60
63
|
|
61
|
-
def
|
64
|
+
def ip_search
|
62
65
|
records = ip_client.get_passive_dns(query)
|
63
|
-
records.
|
66
|
+
records.filter_map do |record|
|
64
67
|
record.hostname if record.record_type == "A"
|
65
|
-
end.
|
68
|
+
end.uniq
|
66
69
|
end
|
67
70
|
end
|
68
71
|
end
|
@@ -5,26 +5,29 @@ require "passivetotal"
|
|
5
5
|
module Mihari
|
6
6
|
module Analyzers
|
7
7
|
class PassiveTotal < Base
|
8
|
-
|
8
|
+
include Mixins::Refang
|
9
9
|
|
10
|
-
|
11
|
-
|
10
|
+
param :query
|
11
|
+
option :title, default: proc { "PassiveTotal search" }
|
12
|
+
option :description, default: proc { "query = #{query}" }
|
13
|
+
option :tags, default: proc { [] }
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
+
attr_reader :type
|
16
|
+
|
17
|
+
def initialize(*args, **kwargs)
|
18
|
+
super
|
15
19
|
|
16
|
-
@
|
17
|
-
@
|
18
|
-
@tags = tags
|
20
|
+
@query = refang(query)
|
21
|
+
@type = TypeChecker.type(query)
|
19
22
|
end
|
20
23
|
|
21
24
|
def artifacts
|
22
|
-
|
25
|
+
search || []
|
23
26
|
end
|
24
27
|
|
25
28
|
private
|
26
29
|
|
27
|
-
def
|
30
|
+
def configuration_keys
|
28
31
|
%w[passivetotal_username passivetotal_api_key]
|
29
32
|
end
|
30
33
|
|
@@ -33,30 +36,28 @@ module Mihari
|
|
33
36
|
end
|
34
37
|
|
35
38
|
def valid_type?
|
36
|
-
%w[ip domain mail].include? type
|
39
|
+
%w[ip domain mail hash].include? type
|
37
40
|
end
|
38
41
|
|
39
|
-
def
|
42
|
+
def search
|
40
43
|
case type
|
41
|
-
when "domain"
|
42
|
-
|
43
|
-
when "ip"
|
44
|
-
passive_dns_lookup
|
44
|
+
when "domain", "ip"
|
45
|
+
passive_dns_search
|
45
46
|
when "mail"
|
46
|
-
|
47
|
+
reverse_whois_search
|
47
48
|
when "hash"
|
48
|
-
|
49
|
+
ssl_search
|
49
50
|
else
|
50
51
|
raise InvalidInputError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
|
51
52
|
end
|
52
53
|
end
|
53
54
|
|
54
|
-
def
|
55
|
+
def passive_dns_search
|
55
56
|
res = api.dns.passive_unique(query)
|
56
57
|
res["results"] || []
|
57
58
|
end
|
58
59
|
|
59
|
-
def
|
60
|
+
def reverse_whois_search
|
60
61
|
res = api.whois.search(query: query, field: "email")
|
61
62
|
results = res["results"] || []
|
62
63
|
results.map do |result|
|
@@ -64,7 +65,7 @@ module Mihari
|
|
64
65
|
end.flatten.compact.uniq
|
65
66
|
end
|
66
67
|
|
67
|
-
def
|
68
|
+
def ssl_search
|
68
69
|
res = api.ssl.history(query)
|
69
70
|
results = res["results"] || []
|
70
71
|
results.map do |result|
|
@@ -5,26 +5,29 @@ require "pulsedive"
|
|
5
5
|
module Mihari
|
6
6
|
module Analyzers
|
7
7
|
class Pulsedive < Base
|
8
|
-
|
8
|
+
include Mixins::Refang
|
9
9
|
|
10
|
-
|
11
|
-
|
10
|
+
param :query
|
11
|
+
option :title, default: proc { "Pulsedive search" }
|
12
|
+
option :description, default: proc { "query = #{query}" }
|
13
|
+
option :tags, default: proc { [] }
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
+
attr_reader :type
|
16
|
+
|
17
|
+
def initialize(*args, **kwargs)
|
18
|
+
super
|
15
19
|
|
16
|
-
@
|
17
|
-
@
|
18
|
-
@tags = tags
|
20
|
+
@query = refang(query)
|
21
|
+
@type = TypeChecker.type(query)
|
19
22
|
end
|
20
23
|
|
21
24
|
def artifacts
|
22
|
-
|
25
|
+
search || []
|
23
26
|
end
|
24
27
|
|
25
28
|
private
|
26
29
|
|
27
|
-
def
|
30
|
+
def configuration_keys
|
28
31
|
%w[pulsedive_api_key]
|
29
32
|
end
|
30
33
|
|
@@ -36,16 +39,16 @@ module Mihari
|
|
36
39
|
%w[ip domain].include? type
|
37
40
|
end
|
38
41
|
|
39
|
-
def
|
42
|
+
def search
|
40
43
|
raise InvalidInputError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
|
41
44
|
|
42
45
|
indicator = api.indicator.get_by_value(query)
|
43
46
|
iid = indicator["iid"]
|
44
47
|
|
45
48
|
properties = api.indicator.get_properties_by_id(iid)
|
46
|
-
(properties["dns"] || []).
|
49
|
+
(properties["dns"] || []).filter_map do |property|
|
47
50
|
property["value"] if ["A", "PTR"].include?(property["name"])
|
48
|
-
end
|
51
|
+
end
|
49
52
|
end
|
50
53
|
end
|
51
54
|
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "uuidtools"
|
4
|
+
|
5
|
+
NIL = nil
|
6
|
+
|
7
|
+
module Mihari
|
8
|
+
module Analyzers
|
9
|
+
class Rule < Base
|
10
|
+
option :title
|
11
|
+
option :description
|
12
|
+
option :queries
|
13
|
+
|
14
|
+
option :id, default: proc {}
|
15
|
+
option :tags, default: proc { [] }
|
16
|
+
option :allowed_data_types, default: proc { ALLOWED_DATA_TYPES }
|
17
|
+
|
18
|
+
attr_reader :source
|
19
|
+
|
20
|
+
def initialize(**kwargs)
|
21
|
+
super(**kwargs)
|
22
|
+
|
23
|
+
@source = id || UUIDTools::UUID.md5_create(UUIDTools::UUID_URL_NAMESPACE, title + description).to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
ANALYZER_TO_CLASS = {
|
27
|
+
"binaryedge" => BinaryEdge,
|
28
|
+
"censys" => Censys,
|
29
|
+
"circl" => CIRCL,
|
30
|
+
"crtsh" => Crtsh,
|
31
|
+
"dnpedia" => DNPedia,
|
32
|
+
"dnstwister" => DNSTwister,
|
33
|
+
"onyphe" => Onyphe,
|
34
|
+
"otx" => OTX,
|
35
|
+
"passivetotal" => PassiveTotal,
|
36
|
+
"pulsedive" => Pulsedive,
|
37
|
+
"securitytrails" => SecurityTrails,
|
38
|
+
"shodan" => Shodan,
|
39
|
+
"spyse" => Spyse,
|
40
|
+
"urlscan" => Urlscan,
|
41
|
+
"virustotal" => VirusTotal,
|
42
|
+
"zoomeye" => ZoomEye
|
43
|
+
}.freeze
|
44
|
+
|
45
|
+
#
|
46
|
+
# Returns a list of artifacts matched with queries
|
47
|
+
#
|
48
|
+
# @return [Array<Mihari::Artifact>]
|
49
|
+
#
|
50
|
+
def artifacts
|
51
|
+
artifacts = []
|
52
|
+
|
53
|
+
queries.each do |params|
|
54
|
+
analyzer_name = params[:analyzer]
|
55
|
+
klass = get_analyzer_class(analyzer_name)
|
56
|
+
|
57
|
+
query = params[:query]
|
58
|
+
analyzer = klass.new(query, **params)
|
59
|
+
|
60
|
+
# Use #normalized_artifacts method to get atrifacts as Array<Mihari::Artifact>
|
61
|
+
# So Mihari::Artifact object has "source" attribute (e.g. "Shodan")
|
62
|
+
artifacts << analyzer.normalized_artifacts
|
63
|
+
end
|
64
|
+
|
65
|
+
artifacts.flatten
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# Normalize artifacts
|
70
|
+
# - Uniquefy artifacts by #uniq(&:data)
|
71
|
+
# - Reject an invalid artifact (for just in case)
|
72
|
+
# - Select artifacts with allowed data types
|
73
|
+
#
|
74
|
+
# @return [Array<Mihari::Artifact>]
|
75
|
+
#
|
76
|
+
def normalized_artifacts
|
77
|
+
@normalized_artifacts ||= artifacts.uniq(&:data).select(&:valid?).select do |artifact|
|
78
|
+
allowed_data_types.include? artifact.data_type
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
#
|
85
|
+
# Get analyzer class
|
86
|
+
#
|
87
|
+
# @param [String] analyzer_name
|
88
|
+
#
|
89
|
+
# @return [Class<Mihari::Analyzers::Base>] analyzer class
|
90
|
+
#
|
91
|
+
def get_analyzer_class(analyzer_name)
|
92
|
+
analyzer = ANALYZER_TO_CLASS[analyzer_name]
|
93
|
+
return analyzer if analyzer
|
94
|
+
|
95
|
+
raise ArgumentError, "#{analyzer_name} is not supported"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|