mihari 5.4.1 → 5.4.3
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 +145 -146
- data/frontend/package.json +8 -8
- data/frontend/src/swagger.yaml +306 -272
- data/lib/mihari/analyzers/base.rb +0 -4
- data/lib/mihari/analyzers/binaryedge.rb +4 -44
- data/lib/mihari/analyzers/censys.rb +4 -20
- data/lib/mihari/analyzers/circl.rb +2 -26
- data/lib/mihari/analyzers/crtsh.rb +2 -17
- data/lib/mihari/analyzers/dnstwister.rb +1 -3
- 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 +2 -38
- data/lib/mihari/analyzers/passivetotal.rb +3 -41
- data/lib/mihari/analyzers/securitytrails.rb +3 -41
- data/lib/mihari/analyzers/shodan.rb +7 -39
- data/lib/mihari/analyzers/urlscan.rb +2 -38
- data/lib/mihari/analyzers/virustotal_intelligence.rb +2 -25
- data/lib/mihari/analyzers/zoomeye.rb +17 -83
- data/lib/mihari/cli/alert.rb +11 -0
- data/lib/mihari/cli/main.rb +6 -1
- data/lib/mihari/clients/base.rb +9 -1
- data/lib/mihari/clients/binaryedge.rb +27 -2
- data/lib/mihari/clients/censys.rb +32 -2
- data/lib/mihari/clients/circl.rb +28 -1
- data/lib/mihari/clients/crtsh.rb +9 -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/securitytrails.rb +44 -0
- data/lib/mihari/clients/shodan.rb +30 -2
- 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 -0
- data/lib/mihari/commands/rule.rb +2 -2
- data/lib/mihari/commands/search.rb +20 -59
- data/lib/mihari/commands/web.rb +1 -1
- data/lib/mihari/config.rb +2 -2
- data/lib/mihari/emitters/base.rb +1 -1
- data/lib/mihari/emitters/database.rb +2 -2
- data/lib/mihari/errors.rb +23 -2
- data/lib/mihari/http.rb +7 -1
- data/lib/mihari/schemas/alert.rb +14 -0
- data/lib/mihari/services/alert_proxy.rb +106 -0
- data/lib/mihari/services/alert_runner.rb +22 -0
- data/lib/mihari/services/{rule.rb → rule_proxy.rb} +10 -6
- data/lib/mihari/services/rule_runner.rb +49 -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 +5 -5
- data/lib/mihari/structs/urlscan.rb +3 -3
- data/lib/mihari/structs/virustotal_intelligence.rb +3 -3
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/endpoints/alerts.rb +22 -0
- data/lib/mihari/web/endpoints/rules.rb +8 -8
- data/lib/mihari/web/public/assets/{index-61dc587c.js → index-4d7eda9f.js} +1 -1
- data/lib/mihari/web/public/index.html +1 -1
- data/lib/mihari/web/public/redoc-static.html +29 -27
- data/lib/mihari.rb +6 -1
- data/mihari.gemspec +9 -10
- metadata +28 -37
- data/Steepfile +0 -31
@@ -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
|
@@ -36,6 +39,31 @@ module Mihari
|
|
36
39
|
res = get("/shodan/host/search", params: params)
|
37
40
|
Structs::Shodan::Result.from_dynamic! JSON.parse(res.body.to_s)
|
38
41
|
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# @param [String] query
|
45
|
+
# @param [Boolean] minify
|
46
|
+
# @param [Integer] pagination_limit
|
47
|
+
#
|
48
|
+
# @return [Enumerable<Structs::Shodan::Result>]
|
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
|
66
|
+
end
|
39
67
|
end
|
40
68
|
end
|
41
69
|
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
|
#
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mihari
|
4
|
+
module Commands
|
5
|
+
module Alert
|
6
|
+
class << self
|
7
|
+
def included(thor)
|
8
|
+
thor.class_eval do
|
9
|
+
desc "add [PATH]", "Add an alert"
|
10
|
+
#
|
11
|
+
# @param [String] path
|
12
|
+
#
|
13
|
+
def add(path)
|
14
|
+
Mihari::Database.with_db_connection do
|
15
|
+
proxy = Mihari::Services::AlertProxy.from_path(path)
|
16
|
+
proxy.validate!
|
17
|
+
|
18
|
+
runner = Mihari::Services::AlertRunner.new(proxy)
|
19
|
+
|
20
|
+
begin
|
21
|
+
alert = runner.run
|
22
|
+
rescue ActiveRecord::RecordNotFound => e
|
23
|
+
# if there is a ActiveRecord::RecordNotFound, output that error without the stack trace
|
24
|
+
Mihari.logger.error e.to_s
|
25
|
+
return
|
26
|
+
end
|
27
|
+
|
28
|
+
if alert.nil?
|
29
|
+
Mihari.logger.info "There is no new artifact found"
|
30
|
+
return
|
31
|
+
end
|
32
|
+
|
33
|
+
data = Mihari::Entities::Alert.represent(alert)
|
34
|
+
puts JSON.pretty_generate(data.as_json)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/mihari/commands/rule.rb
CHANGED
@@ -15,7 +15,7 @@ module Mihari
|
|
15
15
|
# @param [String] path
|
16
16
|
#
|
17
17
|
def validate(path)
|
18
|
-
rule = Services::
|
18
|
+
rule = Services::RuleProxy.from_path_or_id(path)
|
19
19
|
|
20
20
|
begin
|
21
21
|
rule.validate!
|
@@ -47,7 +47,7 @@ module Mihari
|
|
47
47
|
# @return [Mihari::Services::Rule]
|
48
48
|
#
|
49
49
|
def rule_template
|
50
|
-
Services::
|
50
|
+
Services::RuleProxy.from_path File.expand_path("../templates/rule.yml.erb", __dir__)
|
51
51
|
end
|
52
52
|
|
53
53
|
#
|
@@ -4,60 +4,6 @@ module Mihari
|
|
4
4
|
module Commands
|
5
5
|
module Search
|
6
6
|
class << self
|
7
|
-
class RuleWrapper
|
8
|
-
include Mixins::ErrorNotification
|
9
|
-
|
10
|
-
# @return [Nihari::Structs::Rule]
|
11
|
-
attr_reader :rule
|
12
|
-
|
13
|
-
# @return [Boolean]
|
14
|
-
attr_reader :force_overwrite
|
15
|
-
|
16
|
-
def initialize(rule, force_overwrite:)
|
17
|
-
@rule = rule
|
18
|
-
@force_overwrite = force_overwrite
|
19
|
-
end
|
20
|
-
|
21
|
-
def force_overwrite?
|
22
|
-
force_overwrite
|
23
|
-
end
|
24
|
-
|
25
|
-
#
|
26
|
-
# @return [Boolean]
|
27
|
-
#
|
28
|
-
def diff?
|
29
|
-
model = Mihari::Rule.find(rule.id)
|
30
|
-
model.data != rule.data.deep_stringify_keys
|
31
|
-
rescue ActiveRecord::RecordNotFound
|
32
|
-
false
|
33
|
-
end
|
34
|
-
|
35
|
-
def update_or_create
|
36
|
-
rule.to_model.save
|
37
|
-
end
|
38
|
-
|
39
|
-
def run
|
40
|
-
begin
|
41
|
-
analyzer = rule.to_analyzer
|
42
|
-
rescue ConfigurationError => e
|
43
|
-
# if there is a configuration error, output that error without the stack trace
|
44
|
-
Mihari.logger.error e.to_s
|
45
|
-
return
|
46
|
-
end
|
47
|
-
|
48
|
-
with_error_notification do
|
49
|
-
alert = analyzer.run
|
50
|
-
if alert.nil?
|
51
|
-
Mihari.logger.info "There is no new artifact found"
|
52
|
-
return
|
53
|
-
end
|
54
|
-
|
55
|
-
data = Mihari::Entities::Alert.represent(alert)
|
56
|
-
puts JSON.pretty_generate(data.as_json)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
7
|
def included(thor)
|
62
8
|
thor.class_eval do
|
63
9
|
desc "search [PATH]", "Search by a rule"
|
@@ -69,7 +15,7 @@ module Mihari
|
|
69
15
|
#
|
70
16
|
def search(path_or_id)
|
71
17
|
Mihari::Database.with_db_connection do
|
72
|
-
rule = Services::
|
18
|
+
rule = Services::RuleProxy.from_path_or_id path_or_id
|
73
19
|
|
74
20
|
begin
|
75
21
|
rule.validate!
|
@@ -78,15 +24,30 @@ module Mihari
|
|
78
24
|
end
|
79
25
|
|
80
26
|
force_overwrite = options["force_overwrite"] || false
|
81
|
-
|
27
|
+
runner = Services::RuleRunner.new(rule, force_overwrite: force_overwrite)
|
82
28
|
|
83
|
-
if
|
29
|
+
if runner.diff? && !force_overwrite
|
84
30
|
message = "There is diff in the rule (#{rule.id}). Are you sure you want to overwrite the rule? (y/n)"
|
85
31
|
return unless yes?(message)
|
86
32
|
end
|
87
33
|
|
88
|
-
|
89
|
-
|
34
|
+
runner.update_or_create
|
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
|
43
|
+
|
44
|
+
if alert.nil?
|
45
|
+
Mihari.logger.info "There is no new artifact found"
|
46
|
+
return
|
47
|
+
end
|
48
|
+
|
49
|
+
data = Mihari::Entities::Alert.represent(alert)
|
50
|
+
puts JSON.pretty_generate(data.as_json)
|
90
51
|
end
|
91
52
|
end
|
92
53
|
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
@@ -141,12 +141,12 @@ module Mihari
|
|
141
141
|
|
142
142
|
@sentry_dsn = ENV.fetch("SENTRY_DSN", nil)
|
143
143
|
|
144
|
-
@hide_config_values = ENV.fetch("HIDE_CONFIG_VALUES",
|
144
|
+
@hide_config_values = ENV.fetch("HIDE_CONFIG_VALUES", true)
|
145
145
|
|
146
146
|
@retry_times = ENV.fetch("RETRY_TIMES", 3).to_i
|
147
147
|
@retry_interval = ENV.fetch("RETRY_INTERVAL", 5).to_i
|
148
148
|
|
149
|
-
@pagination_limit = ENV.fetch("PAGINATION_LIMIT",
|
149
|
+
@pagination_limit = ENV.fetch("PAGINATION_LIMIT", 100).to_i
|
150
150
|
end
|
151
151
|
end
|
152
152
|
end
|
data/lib/mihari/emitters/base.rb
CHANGED
@@ -10,10 +10,10 @@ module Mihari
|
|
10
10
|
#
|
11
11
|
# Create an alert
|
12
12
|
#
|
13
|
-
# @return [Mihari::Alert]
|
13
|
+
# @return [Mihari::Alert, nil]
|
14
14
|
#
|
15
15
|
def emit
|
16
|
-
return if artifacts.empty?
|
16
|
+
return nil if artifacts.empty?
|
17
17
|
|
18
18
|
tags = rule.tags.filter_map { |name| Tag.find_or_create_by(name: name) }.uniq
|
19
19
|
taggings = tags.map { |tag| Tagging.new(tag_id: tag.id) }
|
data/lib/mihari/errors.rb
CHANGED
@@ -15,17 +15,38 @@ module Mihari
|
|
15
15
|
|
16
16
|
class RuleValidationError < Error; end
|
17
17
|
|
18
|
+
class AlertValidationError < Error; end
|
19
|
+
|
18
20
|
class YAMLSyntaxError < Error; end
|
19
21
|
|
20
22
|
class ConfigurationError < Error; end
|
21
23
|
|
24
|
+
# errors for HTTP interactions
|
22
25
|
class HTTPError < Error; end
|
23
26
|
|
24
|
-
class StatusCodeError < HTTPError; end
|
25
|
-
|
26
27
|
class NetworkError < HTTPError; end
|
27
28
|
|
28
29
|
class TimeoutError < HTTPError; end
|
29
30
|
|
30
31
|
class SSLError < HTTPError; end
|
32
|
+
|
33
|
+
class StatusCodeError < HTTPError
|
34
|
+
# @return [Integer]
|
35
|
+
attr_reader :status_code
|
36
|
+
|
37
|
+
# @return [String, nil]
|
38
|
+
attr_reader :body
|
39
|
+
|
40
|
+
#
|
41
|
+
# @param [String] msg
|
42
|
+
# @param [Integer] status_code
|
43
|
+
# @param [String, nil] body
|
44
|
+
#
|
45
|
+
def initialize(msg, status_code, body)
|
46
|
+
super(msg)
|
47
|
+
|
48
|
+
@status_code = status_code
|
49
|
+
@body = body
|
50
|
+
end
|
51
|
+
end
|
31
52
|
end
|
data/lib/mihari/http.rb
CHANGED
@@ -94,7 +94,13 @@ module Mihari
|
|
94
94
|
Net::HTTP.start(url.host, url.port, https_options) do |http|
|
95
95
|
res = http.request(req)
|
96
96
|
|
97
|
-
|
97
|
+
unless res.is_a?(Net::HTTPSuccess)
|
98
|
+
raise StatusCodeError.new(
|
99
|
+
"Unsuccessful response code returned: #{res.code}",
|
100
|
+
res.code.to_i,
|
101
|
+
res.body
|
102
|
+
)
|
103
|
+
end
|
98
104
|
|
99
105
|
res
|
100
106
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mihari
|
4
|
+
module Schemas
|
5
|
+
Alert = Dry::Schema.Params do
|
6
|
+
required(:rule_id).value(:string)
|
7
|
+
required(:artifacts).value(array[:string])
|
8
|
+
end
|
9
|
+
|
10
|
+
class AlertContract < Dry::Validation::Contract
|
11
|
+
params(Alert)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|