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
@@ -6,32 +6,61 @@ module Mihari
|
|
6
6
|
class << self
|
7
7
|
def included(thor)
|
8
8
|
thor.class_eval do
|
9
|
+
include Dry::Monads[:result, :try]
|
10
|
+
|
9
11
|
desc "add [PATH]", "Add an alert"
|
10
12
|
#
|
11
13
|
# @param [String] path
|
12
14
|
#
|
13
15
|
def add(path)
|
14
16
|
Mihari::Database.with_db_connection do
|
15
|
-
|
16
|
-
|
17
|
+
builder = Mihari::Services::AlertBuilder.new(path)
|
18
|
+
|
19
|
+
run_proxy_l = ->(proxy) { run_proxy proxy }
|
20
|
+
check_nil_l = ->(alert_or_nil) { check_nil alert_or_nil }
|
17
21
|
|
18
|
-
|
22
|
+
result = builder.result.bind(run_proxy_l).bind(check_nil_l)
|
19
23
|
|
20
|
-
|
21
|
-
alert =
|
22
|
-
|
23
|
-
|
24
|
-
Mihari.logger.error e.to_s
|
24
|
+
if result.success?
|
25
|
+
alert = result.value!
|
26
|
+
data = Mihari::Entities::Alert.represent(alert)
|
27
|
+
puts JSON.pretty_generate(data.as_json)
|
25
28
|
return
|
26
29
|
end
|
27
30
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
+
failure = result.failure
|
32
|
+
case failure
|
33
|
+
when ValidationError
|
34
|
+
Mihari.logger.error "Failed to parse the input as an alert:"
|
35
|
+
Mihari.logger.error JSON.pretty_generate(failure.errors.to_h)
|
36
|
+
when StandardError
|
37
|
+
raise failure
|
38
|
+
else
|
39
|
+
Mihari.logger.info failure
|
31
40
|
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
no_commands do
|
45
|
+
#
|
46
|
+
# @param [Mihari::Services::AlertProxy] proxy
|
47
|
+
#
|
48
|
+
def run_proxy(proxy)
|
49
|
+
Dry::Monads::Try[StandardError] do
|
50
|
+
runner = Mihari::Services::AlertRunner.new(proxy)
|
51
|
+
runner.run
|
52
|
+
end.to_result
|
53
|
+
end
|
32
54
|
|
33
|
-
|
34
|
-
|
55
|
+
#
|
56
|
+
# @param [Mihari::Alert, nil] alert_or_nil
|
57
|
+
#
|
58
|
+
def check_nil(alert_or_nil)
|
59
|
+
if alert_or_nil.nil?
|
60
|
+
Failure "There is no new artifact found"
|
61
|
+
else
|
62
|
+
Success alert_or_nil
|
63
|
+
end
|
35
64
|
end
|
36
65
|
end
|
37
66
|
end
|
data/lib/mihari/commands/rule.rb
CHANGED
@@ -8,6 +8,8 @@ module Mihari
|
|
8
8
|
class << self
|
9
9
|
def included(thor)
|
10
10
|
thor.class_eval do
|
11
|
+
include Dry::Monads[:result, :try]
|
12
|
+
|
11
13
|
desc "validate [PATH]", "Validate a rule file"
|
12
14
|
#
|
13
15
|
# Validate format of a rule
|
@@ -15,15 +17,17 @@ module Mihari
|
|
15
17
|
# @param [String] path
|
16
18
|
#
|
17
19
|
def validate(path)
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
rule.validate!
|
20
|
+
res = Dry::Monads::Try[ValidationError] do
|
21
|
+
Services::RuleProxy.from_yaml(File.read(path))
|
22
|
+
end.fmap do |rule|
|
22
23
|
Mihari.logger.info "Valid format. The input is parsed as the following:"
|
23
24
|
Mihari.logger.info rule.data.to_yaml
|
24
|
-
rescue RuleValidationError
|
25
|
-
nil
|
26
25
|
end
|
26
|
+
|
27
|
+
return unless res.error?
|
28
|
+
|
29
|
+
Mihari.logger.error "Failed to parse the input as a rule:"
|
30
|
+
Mihari.logger.error JSON.pretty_generate(res.exception.errors.to_h)
|
27
31
|
end
|
28
32
|
|
29
33
|
desc "init [PATH]", "Initialize a new rule file"
|
@@ -47,7 +51,7 @@ module Mihari
|
|
47
51
|
# @return [Mihari::Services::Rule]
|
48
52
|
#
|
49
53
|
def rule_template
|
50
|
-
Services::RuleProxy.
|
54
|
+
Services::RuleProxy.from_yaml File.read(File.expand_path("../templates/rule.yml.erb", __dir__))
|
51
55
|
end
|
52
56
|
|
53
57
|
#
|
@@ -6,7 +6,9 @@ module Mihari
|
|
6
6
|
class << self
|
7
7
|
def included(thor)
|
8
8
|
thor.class_eval do
|
9
|
-
|
9
|
+
include Dry::Monads[:result, :try]
|
10
|
+
|
11
|
+
desc "search [PATH_OR_ID]", "Search by a rule"
|
10
12
|
method_option :force_overwrite, type: :boolean, aliases: "-f", desc: "Force an overwrite the rule"
|
11
13
|
#
|
12
14
|
# Search by a rule
|
@@ -15,39 +17,69 @@ module Mihari
|
|
15
17
|
#
|
16
18
|
def search(path_or_id)
|
17
19
|
Mihari::Database.with_db_connection do
|
18
|
-
|
20
|
+
builder = Services::RuleBuilder.new(path_or_id)
|
21
|
+
|
22
|
+
check_diff_l = ->(rule) { check_diff rule }
|
23
|
+
update_and_run_l = ->(runner) { update_and_run runner }
|
24
|
+
check_nil_l = ->(alert_or_nil) { check_nil alert_or_nil }
|
19
25
|
|
20
|
-
|
21
|
-
|
22
|
-
|
26
|
+
result = builder.result.bind(check_diff_l).bind(update_and_run_l).bind(check_nil_l)
|
27
|
+
|
28
|
+
if result.success?
|
29
|
+
alert = result.value!
|
30
|
+
data = Mihari::Entities::Alert.represent(alert)
|
31
|
+
puts JSON.pretty_generate(data.as_json)
|
23
32
|
return
|
24
33
|
end
|
25
34
|
|
35
|
+
failure = result.failure
|
36
|
+
case failure
|
37
|
+
when ValidationError
|
38
|
+
Mihari.logger.error "Failed to parse the input as a rule:"
|
39
|
+
Mihari.logger.error JSON.pretty_generate(failure.errors.to_h)
|
40
|
+
when StandardError
|
41
|
+
raise failure
|
42
|
+
else
|
43
|
+
Mihari.logger.info failure
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
no_commands do
|
49
|
+
#
|
50
|
+
# @param [Mihari::Services::RuleProxy] rule
|
51
|
+
#
|
52
|
+
def check_diff(rule)
|
26
53
|
force_overwrite = options["force_overwrite"] || false
|
27
54
|
runner = Services::RuleRunner.new(rule, force_overwrite: force_overwrite)
|
55
|
+
message = "There is a diff in the rule (#{rule.id}). Are you sure you want to overwrite the rule? (y/n)"
|
28
56
|
|
29
|
-
if runner.diff? && !force_overwrite
|
30
|
-
|
31
|
-
return unless yes?(message)
|
57
|
+
if runner.diff? && !force_overwrite && !yes?(message)
|
58
|
+
return Failure("Stop overwriting the rule (#{rule.id})")
|
32
59
|
end
|
33
60
|
|
34
|
-
runner
|
35
|
-
|
36
|
-
begin
|
37
|
-
alert = runner.run
|
38
|
-
rescue ConfigurationError => e
|
39
|
-
# if there is a configuration error, output that error without the stack trace
|
40
|
-
Mihari.logger.error e.to_s
|
41
|
-
return
|
42
|
-
end
|
61
|
+
Success runner
|
62
|
+
end
|
43
63
|
|
44
|
-
|
45
|
-
|
46
|
-
|
64
|
+
#
|
65
|
+
# @param [Mihari::Alert, nil] alert_or_nil
|
66
|
+
#
|
67
|
+
def check_nil(alert_or_nil)
|
68
|
+
if alert_or_nil.nil?
|
69
|
+
Failure "There is no new artifact found"
|
70
|
+
else
|
71
|
+
Success alert_or_nil
|
47
72
|
end
|
73
|
+
end
|
48
74
|
|
49
|
-
|
50
|
-
|
75
|
+
#
|
76
|
+
# @param [Mihari::Services::RuleRunner] runner
|
77
|
+
#
|
78
|
+
def update_and_run(runner)
|
79
|
+
Dry::Monads::Try[StandardError] do
|
80
|
+
runner.update_or_create
|
81
|
+
runner.run
|
82
|
+
end.to_result
|
51
83
|
end
|
52
84
|
end
|
53
85
|
end
|
data/lib/mihari/commands/web.rb
CHANGED
@@ -12,7 +12,7 @@ module Mihari
|
|
12
12
|
method_option :threads, type: :string, default: "0:5", desc: "min:max threads to use"
|
13
13
|
method_option :verbose, type: :boolean, default: true, desc: "Report each request"
|
14
14
|
method_option :worker_timeout, type: :numeric, default: 60, desc: "Worker timeout value (in seconds)"
|
15
|
-
method_option :hide_config_values, type: :boolean, default:
|
15
|
+
method_option :hide_config_values, type: :boolean, default: true,
|
16
16
|
desc: "Whether to hide config values or not"
|
17
17
|
method_option :open, type: :boolean, default: true, desc: "Whether to open the app in browser or not"
|
18
18
|
method_option :rack_env, type: :string, default: "production", desc: "Rack environment"
|
data/lib/mihari/config.rb
CHANGED
@@ -93,6 +93,9 @@ module Mihari
|
|
93
93
|
# @return [Integer]
|
94
94
|
attr_reader :pagination_limit
|
95
95
|
|
96
|
+
# @return [Boolean]
|
97
|
+
attr_reader :ignore_error
|
98
|
+
|
96
99
|
def initialize
|
97
100
|
@binaryedge_api_key = ENV.fetch("BINARYEDGE_API_KEY", nil)
|
98
101
|
|
@@ -141,12 +144,14 @@ module Mihari
|
|
141
144
|
|
142
145
|
@sentry_dsn = ENV.fetch("SENTRY_DSN", nil)
|
143
146
|
|
144
|
-
@hide_config_values = ENV.fetch("HIDE_CONFIG_VALUES",
|
147
|
+
@hide_config_values = ENV.fetch("HIDE_CONFIG_VALUES", true)
|
145
148
|
|
146
149
|
@retry_times = ENV.fetch("RETRY_TIMES", 3).to_i
|
147
150
|
@retry_interval = ENV.fetch("RETRY_INTERVAL", 5).to_i
|
148
151
|
|
149
152
|
@pagination_limit = ENV.fetch("PAGINATION_LIMIT", 100).to_i
|
153
|
+
|
154
|
+
@ignore_error = ENV.fetch("IGNORE_ERROR", false)
|
150
155
|
end
|
151
156
|
end
|
152
157
|
end
|
data/lib/mihari/emitters/base.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
module Mihari
|
4
4
|
module Emitters
|
5
5
|
class Base
|
6
|
+
include Dry::Monads[:result, :try]
|
7
|
+
|
6
8
|
include Mixins::Configurable
|
7
9
|
include Mixins::Retriable
|
8
10
|
|
@@ -34,11 +36,15 @@ module Mihari
|
|
34
36
|
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
35
37
|
end
|
36
38
|
|
37
|
-
def run
|
38
|
-
retry_on_error { emit
|
39
|
+
def run
|
40
|
+
retry_on_error { emit }
|
41
|
+
end
|
42
|
+
|
43
|
+
def result
|
44
|
+
Try[StandardError] { run }.to_result
|
39
45
|
end
|
40
46
|
|
41
|
-
def emit
|
47
|
+
def emit
|
42
48
|
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
43
49
|
end
|
44
50
|
end
|
@@ -6,10 +6,23 @@ module Mihari
|
|
6
6
|
include Mixins::Configurable
|
7
7
|
|
8
8
|
class << self
|
9
|
+
include Dry::Monads[:result, :try]
|
10
|
+
|
9
11
|
def inherited(child)
|
10
12
|
super
|
11
13
|
Mihari.enrichers << child
|
12
14
|
end
|
15
|
+
|
16
|
+
def query_result(value)
|
17
|
+
Try[StandardError] { query(value) }.to_result
|
18
|
+
end
|
19
|
+
|
20
|
+
#
|
21
|
+
# @param [String] value
|
22
|
+
#
|
23
|
+
def query(value)
|
24
|
+
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
25
|
+
end
|
13
26
|
end
|
14
27
|
|
15
28
|
# @return [Boolean]
|
@@ -11,15 +11,30 @@ module Mihari
|
|
11
11
|
end
|
12
12
|
|
13
13
|
class << self
|
14
|
+
include Dry::Monads[:result]
|
15
|
+
|
14
16
|
#
|
15
17
|
# Query Google Public DNS
|
16
18
|
#
|
17
19
|
# @param [String] name
|
20
|
+
#
|
21
|
+
# @return [Array<Mihari::Structs::Shodan::GooglePublicDNS::Response>]
|
22
|
+
#
|
23
|
+
def query(name)
|
24
|
+
%w[A AAAA CNAME TXT NS].filter_map do |resource_type|
|
25
|
+
query_by_type(name, resource_type)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
# Query Google Public DNS by resource type
|
31
|
+
#
|
32
|
+
# @param [String] name
|
18
33
|
# @param [String] resource_type
|
19
34
|
#
|
20
35
|
# @return [Mihari::Structs::Shodan::GooglePublicDNS::Response, nil]
|
21
36
|
#
|
22
|
-
def
|
37
|
+
def query_by_type(name, resource_type)
|
23
38
|
url = "https://dns.google/resolve"
|
24
39
|
params = { name: name, type: resource_type }
|
25
40
|
res = HTTP.get(url, params: params)
|
@@ -17,6 +17,7 @@ module Mihari
|
|
17
17
|
end
|
18
18
|
|
19
19
|
class << self
|
20
|
+
include Dry::Monads[:result]
|
20
21
|
include Memist::Memoizable
|
21
22
|
|
22
23
|
#
|
@@ -28,20 +29,15 @@ module Mihari
|
|
28
29
|
#
|
29
30
|
def query(ip)
|
30
31
|
headers = {}
|
32
|
+
|
31
33
|
token = Mihari.config.ipinfo_api_key
|
32
|
-
unless token.nil?
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
data = JSON.parse(res.body.to_s)
|
40
|
-
|
41
|
-
Structs::IPInfo::Response.from_dynamic! data
|
42
|
-
rescue HTTPError
|
43
|
-
nil
|
44
|
-
end
|
34
|
+
headers[:authorization] = "Bearer #{token}" unless token.nil?
|
35
|
+
|
36
|
+
url = "https://ipinfo.io/#{ip}/json"
|
37
|
+
res = HTTP.get(url, headers: headers)
|
38
|
+
data = JSON.parse(res.body.to_s)
|
39
|
+
|
40
|
+
Structs::IPInfo::Response.from_dynamic! data
|
45
41
|
end
|
46
42
|
memoize :query
|
47
43
|
end
|
@@ -11,6 +11,7 @@ module Mihari
|
|
11
11
|
end
|
12
12
|
|
13
13
|
class << self
|
14
|
+
include Dry::Monads[:result]
|
14
15
|
include Memist::Memoizable
|
15
16
|
|
16
17
|
#
|
@@ -26,8 +27,6 @@ module Mihari
|
|
26
27
|
data = JSON.parse(res.body.to_s)
|
27
28
|
|
28
29
|
Structs::Shodan::InternetDBResponse.from_dynamic! data
|
29
|
-
rescue HTTPError
|
30
|
-
nil
|
31
30
|
end
|
32
31
|
memoize :query
|
33
32
|
end
|
@@ -14,6 +14,8 @@ module Mihari
|
|
14
14
|
end
|
15
15
|
|
16
16
|
class << self
|
17
|
+
include Dry::Monads[:result]
|
18
|
+
|
17
19
|
#
|
18
20
|
# Query IAIA Whois API
|
19
21
|
#
|
@@ -47,8 +49,6 @@ module Mihari
|
|
47
49
|
# set memo
|
48
50
|
@memo[domain] = whois_record
|
49
51
|
whois_record
|
50
|
-
rescue ::Whois::Error, ::Whois::ParserError, Timeout::Error
|
51
|
-
nil
|
52
52
|
end
|
53
53
|
|
54
54
|
def reset_cache
|
data/lib/mihari/errors.rb
CHANGED
@@ -3,22 +3,14 @@
|
|
3
3
|
module Mihari
|
4
4
|
class Error < StandardError; end
|
5
5
|
|
6
|
-
class
|
6
|
+
class ValueError < Error; end
|
7
7
|
|
8
|
-
class
|
8
|
+
class TypeError < Error; end
|
9
9
|
|
10
10
|
class RetryableError < Error; end
|
11
11
|
|
12
12
|
class FileNotFoundError < Error; end
|
13
13
|
|
14
|
-
class FeedParseError < Error; end
|
15
|
-
|
16
|
-
class RuleValidationError < Error; end
|
17
|
-
|
18
|
-
class AlertValidationError < Error; end
|
19
|
-
|
20
|
-
class YAMLSyntaxError < Error; end
|
21
|
-
|
22
14
|
class ConfigurationError < Error; end
|
23
15
|
|
24
16
|
# errors for HTTP interactions
|
@@ -49,4 +41,18 @@ module Mihari
|
|
49
41
|
@body = body
|
50
42
|
end
|
51
43
|
end
|
44
|
+
|
45
|
+
class ValidationError < Error
|
46
|
+
attr_reader :errors
|
47
|
+
|
48
|
+
#
|
49
|
+
# @param [String] msg
|
50
|
+
# @param [Dry::Schema::MessageSet] errors
|
51
|
+
#
|
52
|
+
def initialize(msg, errors)
|
53
|
+
super(msg)
|
54
|
+
|
55
|
+
@errors = errors
|
56
|
+
end
|
57
|
+
end
|
52
58
|
end
|
data/lib/mihari/feed/parser.rb
CHANGED
@@ -25,8 +25,8 @@ module Mihari
|
|
25
25
|
def parse(selector)
|
26
26
|
parsed = data.instance_eval(selector)
|
27
27
|
|
28
|
-
raise
|
29
|
-
raise
|
28
|
+
raise TypeError unless parsed.is_a?(Array) || parsed.is_a?(Enumerator)
|
29
|
+
raise TypeError unless parsed.all?(String)
|
30
30
|
|
31
31
|
parsed.to_a
|
32
32
|
end
|
@@ -5,6 +5,8 @@ module Mihari
|
|
5
5
|
belongs_to :artifact
|
6
6
|
|
7
7
|
class << self
|
8
|
+
include Dry::Monads[:result]
|
9
|
+
|
8
10
|
#
|
9
11
|
# Build AS
|
10
12
|
#
|
@@ -13,11 +15,15 @@ module Mihari
|
|
13
15
|
# @return [Mihari::AutonomousSystem, nil]
|
14
16
|
#
|
15
17
|
def build_by_ip(ip)
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
result = Enrichers::IPInfo.query_result(ip).bind do |res|
|
19
|
+
value = res&.asn
|
20
|
+
if value.nil?
|
21
|
+
Success nil
|
22
|
+
else
|
23
|
+
Success new(asn: value)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
result.value_or nil
|
21
27
|
end
|
22
28
|
end
|
23
29
|
end
|
data/lib/mihari/models/cpe.rb
CHANGED
@@ -5,6 +5,8 @@ module Mihari
|
|
5
5
|
belongs_to :artifact
|
6
6
|
|
7
7
|
class << self
|
8
|
+
include Dry::Monads[:result]
|
9
|
+
|
8
10
|
#
|
9
11
|
# Build CPEs
|
10
12
|
#
|
@@ -13,10 +15,14 @@ module Mihari
|
|
13
15
|
# @return [Array<Mihari::CPE>]
|
14
16
|
#
|
15
17
|
def build_by_ip(ip)
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
18
|
+
result = Enrichers::Shodan.query_result(ip).bind do |res|
|
19
|
+
if res.nil?
|
20
|
+
Success []
|
21
|
+
else
|
22
|
+
Success(res.cpes.map { |cpe| new(cpe: cpe) })
|
23
|
+
end
|
24
|
+
end
|
25
|
+
result.value_or []
|
20
26
|
end
|
21
27
|
end
|
22
28
|
end
|
data/lib/mihari/models/dns.rb
CHANGED
@@ -5,6 +5,8 @@ module Mihari
|
|
5
5
|
belongs_to :artifact
|
6
6
|
|
7
7
|
class << self
|
8
|
+
include Dry::Monads[:result]
|
9
|
+
|
8
10
|
#
|
9
11
|
# Build DNS records
|
10
12
|
#
|
@@ -13,23 +15,16 @@ module Mihari
|
|
13
15
|
# @return [Array<Mihari::DnsRecord>]
|
14
16
|
#
|
15
17
|
def build_by_domain(domain)
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
private
|
25
|
-
|
26
|
-
def get_values(domain, resource_type)
|
27
|
-
response = Enrichers::GooglePublicDNS.query(domain, resource_type)
|
28
|
-
answers = response.answers || []
|
29
|
-
|
30
|
-
answers.filter_map do |answer|
|
31
|
-
new(resource: answer.resource_type, value: answer.data)
|
18
|
+
result = Enrichers::GooglePublicDNS.query_result(domain).bind do |responses|
|
19
|
+
Success(
|
20
|
+
responses.map do |res|
|
21
|
+
res.answers.map do |answer|
|
22
|
+
new(resource: answer.resource_type, value: answer.data)
|
23
|
+
end
|
24
|
+
end.flatten
|
25
|
+
)
|
32
26
|
end
|
27
|
+
result.value_or []
|
33
28
|
end
|
34
29
|
end
|
35
30
|
end
|
@@ -7,6 +7,8 @@ module Mihari
|
|
7
7
|
belongs_to :artifact
|
8
8
|
|
9
9
|
class << self
|
10
|
+
include Dry::Monads[:result]
|
11
|
+
|
10
12
|
#
|
11
13
|
# Build Geolocation
|
12
14
|
#
|
@@ -15,11 +17,15 @@ module Mihari
|
|
15
17
|
# @return [Mihari::Geolocation, nil]
|
16
18
|
#
|
17
19
|
def build_by_ip(ip)
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
result = Enrichers::IPInfo.query_result(ip).bind do |res|
|
21
|
+
value = res&.country_code
|
22
|
+
if value.nil?
|
23
|
+
Success nil
|
24
|
+
else
|
25
|
+
Success new(country: NormalizeCountry(value, to: :short), country_code: value)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
result.value_or nil
|
23
29
|
end
|
24
30
|
end
|
25
31
|
end
|
data/lib/mihari/models/port.rb
CHANGED
@@ -5,6 +5,8 @@ module Mihari
|
|
5
5
|
belongs_to :artifact
|
6
6
|
|
7
7
|
class << self
|
8
|
+
include Dry::Monads[:result]
|
9
|
+
|
8
10
|
#
|
9
11
|
# Build ports
|
10
12
|
#
|
@@ -13,10 +15,14 @@ module Mihari
|
|
13
15
|
# @return [Array<Mihari::Port>]
|
14
16
|
#
|
15
17
|
def build_by_ip(ip)
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
18
|
+
result = Enrichers::Shodan.query_result(ip).bind do |res|
|
19
|
+
if res.nil?
|
20
|
+
Success []
|
21
|
+
else
|
22
|
+
Success(res.ports.map { |port| new(port: port) })
|
23
|
+
end
|
24
|
+
end
|
25
|
+
result.value_or []
|
20
26
|
end
|
21
27
|
end
|
22
28
|
end
|
@@ -5,6 +5,8 @@ module Mihari
|
|
5
5
|
belongs_to :artifact
|
6
6
|
|
7
7
|
class << self
|
8
|
+
include Dry::Monads[:result]
|
9
|
+
|
8
10
|
#
|
9
11
|
# Build reverse DNS names
|
10
12
|
#
|
@@ -13,10 +15,14 @@ module Mihari
|
|
13
15
|
# @return [Array<Mihari::ReverseDnsName>]
|
14
16
|
#
|
15
17
|
def build_by_ip(ip)
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
18
|
+
result = Enrichers::Shodan.query_result(ip).bind do |res|
|
19
|
+
if res.nil?
|
20
|
+
Success []
|
21
|
+
else
|
22
|
+
Success(res.hostnames.map { |name| new(name: name) })
|
23
|
+
end
|
24
|
+
end
|
25
|
+
result.value_or []
|
20
26
|
end
|
21
27
|
end
|
22
28
|
end
|