mihari 5.6.1 → 5.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/frontend/package-lock.json +173 -176
- data/frontend/package.json +9 -9
- data/lib/mihari/{base.rb → actor.rb} +16 -2
- data/lib/mihari/analyzers/base.rb +5 -10
- data/lib/mihari/analyzers/censys.rb +1 -1
- data/lib/mihari/analyzers/hunterhow.rb +1 -1
- data/lib/mihari/analyzers/passivetotal.rb +1 -1
- data/lib/mihari/analyzers/pulsedive.rb +1 -1
- data/lib/mihari/analyzers/securitytrails.rb +1 -1
- data/lib/mihari/analyzers/urlscan.rb +1 -1
- data/lib/mihari/analyzers/virustotal.rb +5 -5
- data/lib/mihari/analyzers/zoomeye.rb +3 -3
- data/lib/mihari/clients/crtsh.rb +2 -2
- data/lib/mihari/clients/passivetotal.rb +4 -4
- data/lib/mihari/clients/securitytrails.rb +3 -3
- data/lib/mihari/commands/rule.rb +2 -11
- data/lib/mihari/commands/search.rb +1 -1
- data/lib/mihari/emitters/base.rb +13 -24
- data/lib/mihari/emitters/database.rb +7 -9
- data/lib/mihari/emitters/misp.rb +14 -38
- data/lib/mihari/emitters/slack.rb +14 -11
- data/lib/mihari/emitters/the_hive.rb +16 -44
- data/lib/mihari/emitters/webhook.rb +31 -21
- data/lib/mihari/enrichers/base.rb +1 -6
- data/lib/mihari/enrichers/whois.rb +1 -1
- data/lib/mihari/models/alert.rb +75 -73
- data/lib/mihari/models/artifact.rb +182 -180
- data/lib/mihari/models/autonomous_system.rb +22 -20
- data/lib/mihari/models/cpe.rb +21 -19
- data/lib/mihari/models/dns.rb +24 -22
- data/lib/mihari/models/geolocation.rb +22 -20
- data/lib/mihari/models/port.rb +21 -19
- data/lib/mihari/models/reverse_dns.rb +21 -19
- data/lib/mihari/models/rule.rb +67 -65
- data/lib/mihari/models/tag.rb +5 -3
- data/lib/mihari/models/tagging.rb +5 -3
- data/lib/mihari/models/whois.rb +18 -16
- data/lib/mihari/rule.rb +352 -0
- data/lib/mihari/schemas/analyzer.rb +94 -87
- data/lib/mihari/schemas/emitter.rb +9 -5
- data/lib/mihari/schemas/enricher.rb +8 -4
- data/lib/mihari/schemas/mixins.rb +15 -0
- data/lib/mihari/schemas/rule.rb +3 -10
- data/lib/mihari/services/alert_builder.rb +1 -1
- data/lib/mihari/services/alert_proxy.rb +10 -6
- data/lib/mihari/services/alert_runner.rb +4 -4
- data/lib/mihari/services/rule_builder.rb +3 -3
- data/lib/mihari/services/rule_runner.rb +5 -5
- data/lib/mihari/structs/binaryedge.rb +1 -1
- data/lib/mihari/structs/censys.rb +6 -6
- data/lib/mihari/structs/config.rb +1 -1
- data/lib/mihari/structs/greynoise.rb +5 -5
- data/lib/mihari/structs/hunterhow.rb +3 -3
- data/lib/mihari/structs/onyphe.rb +5 -5
- data/lib/mihari/structs/shodan.rb +6 -6
- 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 +4 -4
- data/lib/mihari/web/endpoints/artifacts.rb +6 -6
- data/lib/mihari/web/endpoints/rules.rb +10 -17
- data/lib/mihari/web/endpoints/tags.rb +2 -2
- data/lib/mihari/web/public/assets/{index-9cc489e6.js → index-28d4c79d.js} +48 -48
- data/lib/mihari/web/public/index.html +1 -1
- data/lib/mihari.rb +6 -8
- data/mihari.gemspec +1 -2
- data/requirements.txt +1 -1
- metadata +8 -22
- data/lib/mihari/analyzers/rule.rb +0 -232
- data/lib/mihari/services/rule_proxy.rb +0 -182
|
@@ -12,45 +12,42 @@ module Mihari
|
|
|
12
12
|
# @return [String, nil]
|
|
13
13
|
attr_reader :api_version
|
|
14
14
|
|
|
15
|
+
# @return [Array<Mihari::Models::Artifact>]
|
|
16
|
+
attr_accessor :artifacts
|
|
17
|
+
|
|
15
18
|
#
|
|
16
|
-
# @param [Array<Mihari::Artifact>] artifacts
|
|
17
19
|
# @param [Mihari::Services::Rule] rule
|
|
18
20
|
# @param [Hash, nil] options
|
|
19
21
|
# @param [Hash] **params
|
|
20
22
|
#
|
|
21
|
-
def initialize(
|
|
22
|
-
super(
|
|
23
|
+
def initialize(rule:, options: nil, **params)
|
|
24
|
+
super(rule: rule, options: options)
|
|
23
25
|
|
|
24
26
|
@url = params[:url] || Mihari.config.thehive_url
|
|
25
27
|
@api_key = params[:api_key] || Mihari.config.thehive_api_key
|
|
26
28
|
@api_version = params[:api_version] || Mihari.config.thehive_api_version
|
|
29
|
+
|
|
30
|
+
@artifacts = []
|
|
27
31
|
end
|
|
28
32
|
|
|
33
|
+
#
|
|
29
34
|
# @return [Boolean]
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
Mihari.logger.info("TheHive API key is not set") unless api_key?
|
|
34
|
-
return false
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
unless ping?
|
|
38
|
-
Mihari.logger.info("TheHive URL (#{url}) is not reachable")
|
|
39
|
-
return false
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
true
|
|
35
|
+
#
|
|
36
|
+
def configured?
|
|
37
|
+
api_key? && url?
|
|
43
38
|
end
|
|
44
39
|
|
|
45
40
|
#
|
|
46
41
|
# Create a Hive alert
|
|
47
42
|
#
|
|
48
|
-
# @
|
|
43
|
+
# @param [Array<Mihari::Models::Artifact>] artifacts
|
|
49
44
|
#
|
|
50
|
-
def emit
|
|
45
|
+
def emit(artifacts)
|
|
51
46
|
return if artifacts.empty?
|
|
52
47
|
|
|
53
|
-
|
|
48
|
+
@artifacts = artifacts
|
|
49
|
+
|
|
50
|
+
client.alert payload
|
|
54
51
|
end
|
|
55
52
|
|
|
56
53
|
#
|
|
@@ -146,31 +143,6 @@ module Mihari
|
|
|
146
143
|
source_ref: "1"
|
|
147
144
|
}
|
|
148
145
|
end
|
|
149
|
-
|
|
150
|
-
#
|
|
151
|
-
# Check whether a URL is reachable or not
|
|
152
|
-
#
|
|
153
|
-
# @return [Boolean]
|
|
154
|
-
#
|
|
155
|
-
def ping?
|
|
156
|
-
base_url = url.end_with?("/") ? url[0..-2] : url
|
|
157
|
-
|
|
158
|
-
if normalized_api_version.nil?
|
|
159
|
-
# for v4
|
|
160
|
-
base_url = url.end_with?("/") ? url[0..-2] : url
|
|
161
|
-
public_url = "#{base_url}/index.html"
|
|
162
|
-
else
|
|
163
|
-
# for v5
|
|
164
|
-
public_url = "#{base_url}/api/v1/status/public"
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
http = Net::Ping::HTTP.new(public_url)
|
|
168
|
-
|
|
169
|
-
# use GET for v5
|
|
170
|
-
http.get_request = true if normalized_api_version
|
|
171
|
-
|
|
172
|
-
http.ping?
|
|
173
|
-
end
|
|
174
146
|
end
|
|
175
147
|
end
|
|
176
148
|
end
|
|
@@ -57,23 +57,41 @@ module Mihari
|
|
|
57
57
|
# @return [String, nil]
|
|
58
58
|
attr_reader :template
|
|
59
59
|
|
|
60
|
+
# @return [Array<Mihari::Models::Artifact>]
|
|
61
|
+
attr_accessor :artifacts
|
|
62
|
+
|
|
60
63
|
#
|
|
61
|
-
# @param [Array<Mihari::Artifact>] artifacts
|
|
62
64
|
# @param [Mihari::Services::Rule] rule
|
|
63
65
|
# @param [Hash] **options
|
|
64
66
|
#
|
|
65
|
-
def initialize(
|
|
66
|
-
super(
|
|
67
|
+
def initialize(rule:, options: nil, **params)
|
|
68
|
+
super(rule: rule, options: options)
|
|
67
69
|
|
|
68
70
|
@url = Addressable::URI.parse(params[:url])
|
|
69
71
|
@headers = params[:headers] || {}
|
|
70
72
|
@method = params[:method] || "POST"
|
|
71
73
|
@template = params[:template]
|
|
74
|
+
|
|
75
|
+
@artifacts = []
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
#
|
|
79
|
+
# @return [Boolean]
|
|
80
|
+
#
|
|
81
|
+
def configured?
|
|
82
|
+
return false if url.nil?
|
|
83
|
+
|
|
84
|
+
%w[http https].include? url.scheme.downcase
|
|
72
85
|
end
|
|
73
86
|
|
|
74
|
-
|
|
87
|
+
#
|
|
88
|
+
# @param [Array<Mihari::Models::Artifact>] artifacts
|
|
89
|
+
#
|
|
90
|
+
def emit(artifacts)
|
|
75
91
|
return if artifacts.empty?
|
|
76
92
|
|
|
93
|
+
@artifacts = artifacts
|
|
94
|
+
|
|
77
95
|
# returns body to prevent Parallel issue (Parallel fails to handle HTTP:Response object)
|
|
78
96
|
case method
|
|
79
97
|
when "GET"
|
|
@@ -83,12 +101,6 @@ module Mihari
|
|
|
83
101
|
end
|
|
84
102
|
end
|
|
85
103
|
|
|
86
|
-
def valid?
|
|
87
|
-
return false if url.nil?
|
|
88
|
-
|
|
89
|
-
%w[http https].include? url.scheme.downcase
|
|
90
|
-
end
|
|
91
|
-
|
|
92
104
|
private
|
|
93
105
|
|
|
94
106
|
def http
|
|
@@ -101,17 +113,15 @@ module Mihari
|
|
|
101
113
|
# @return [String]
|
|
102
114
|
#
|
|
103
115
|
def rendered_template
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
out << payload_template.result
|
|
114
|
-
end.first
|
|
116
|
+
options = {}
|
|
117
|
+
options[:template] = File.read(template) unless template.nil?
|
|
118
|
+
|
|
119
|
+
payload_template = PayloadTemplate.new(
|
|
120
|
+
artifacts: artifacts,
|
|
121
|
+
rule: rule,
|
|
122
|
+
options: options
|
|
123
|
+
)
|
|
124
|
+
payload_template.result
|
|
115
125
|
end
|
|
116
126
|
|
|
117
127
|
#
|
data/lib/mihari/models/alert.rb
CHANGED
|
@@ -1,88 +1,90 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Mihari
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
#
|
|
35
|
-
# Count alerts
|
|
36
|
-
#
|
|
37
|
-
# @param [String, nil] artifact_data
|
|
38
|
-
#
|
|
39
|
-
# @return [Integer]
|
|
40
|
-
#
|
|
41
|
-
def count(filter)
|
|
42
|
-
relation = build_relation(filter)
|
|
43
|
-
relation.distinct("alerts.id").count
|
|
44
|
-
end
|
|
4
|
+
module Models
|
|
5
|
+
class Alert < ActiveRecord::Base
|
|
6
|
+
has_many :taggings, dependent: :destroy
|
|
7
|
+
has_many :artifacts, dependent: :destroy
|
|
8
|
+
has_many :tags, through: :taggings
|
|
9
|
+
|
|
10
|
+
belongs_to :rule
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
#
|
|
14
|
+
# Search alerts
|
|
15
|
+
#
|
|
16
|
+
# @param [Structs::Filters::Alert::SearchFilterWithPagination] filter
|
|
17
|
+
#
|
|
18
|
+
# @return [Array<Alert>]
|
|
19
|
+
#
|
|
20
|
+
def search(filter)
|
|
21
|
+
limit = filter.limit.to_i
|
|
22
|
+
raise ArgumentError, "limit should be bigger than zero" unless limit.positive?
|
|
23
|
+
|
|
24
|
+
page = filter.page.to_i
|
|
25
|
+
raise ArgumentError, "page should be bigger than zero" unless page.positive?
|
|
26
|
+
|
|
27
|
+
offset = (page - 1) * limit
|
|
28
|
+
|
|
29
|
+
relation = build_relation(filter.without_pagination)
|
|
30
|
+
|
|
31
|
+
alert_ids = relation.limit(limit).offset(offset).order(id: :desc).pluck(:id).uniq
|
|
32
|
+
eager_load(:artifacts, :tags).where(id: [alert_ids]).order(id: :desc)
|
|
33
|
+
end
|
|
45
34
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if filter.artifact_data
|
|
57
|
-
artifact = Artifact.where(data: filter.artifact_data)
|
|
58
|
-
artifact_ids = artifact.pluck(:id)
|
|
59
|
-
# set invalid ID if nothing is matched with the filters
|
|
60
|
-
artifact_ids = [-1] if artifact_ids.empty?
|
|
35
|
+
#
|
|
36
|
+
# Count alerts
|
|
37
|
+
#
|
|
38
|
+
# @param [String, nil] artifact_data
|
|
39
|
+
#
|
|
40
|
+
# @return [Integer]
|
|
41
|
+
#
|
|
42
|
+
def count(filter)
|
|
43
|
+
relation = build_relation(filter)
|
|
44
|
+
relation.distinct("alerts.id").count
|
|
61
45
|
end
|
|
62
46
|
|
|
63
|
-
|
|
64
|
-
end
|
|
47
|
+
private
|
|
65
48
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
49
|
+
#
|
|
50
|
+
# @param [Structs::Filters::Alert::SearchFilter] filter
|
|
51
|
+
#
|
|
52
|
+
# @return [Array<Integer>]
|
|
53
|
+
#
|
|
54
|
+
def get_artifact_ids_by_filter(filter)
|
|
55
|
+
artifact_ids = []
|
|
73
56
|
|
|
74
|
-
|
|
75
|
-
|
|
57
|
+
if filter.artifact_data
|
|
58
|
+
artifact = Artifact.where(data: filter.artifact_data)
|
|
59
|
+
artifact_ids = artifact.pluck(:id)
|
|
60
|
+
# set invalid ID if nothing is matched with the filters
|
|
61
|
+
artifact_ids = [-1] if artifact_ids.empty?
|
|
62
|
+
end
|
|
76
63
|
|
|
77
|
-
|
|
78
|
-
|
|
64
|
+
artifact_ids
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
#
|
|
68
|
+
# @param [Structs::Filters::Alert::SearchFilter] filter
|
|
69
|
+
#
|
|
70
|
+
# @return [Mihari::Models::Alert]
|
|
71
|
+
#
|
|
72
|
+
def build_relation(filter)
|
|
73
|
+
artifact_ids = get_artifact_ids_by_filter(filter)
|
|
74
|
+
|
|
75
|
+
relation = self
|
|
76
|
+
relation = relation.includes(:artifacts, :tags)
|
|
79
77
|
|
|
80
|
-
|
|
78
|
+
relation = relation.where(artifacts: { id: artifact_ids }) unless artifact_ids.empty?
|
|
79
|
+
relation = relation.where(tags: { name: filter.tag_name }) if filter.tag_name
|
|
81
80
|
|
|
82
|
-
|
|
83
|
-
relation = relation.where("alerts.created_at <= ?", filter.to_at) if filter.to_at
|
|
81
|
+
relation = relation.where(rule_id: filter.rule_id) if filter.rule_id
|
|
84
82
|
|
|
85
|
-
|
|
83
|
+
relation = relation.where("alerts.created_at >= ?", filter.from_at) if filter.from_at
|
|
84
|
+
relation = relation.where("alerts.created_at <= ?", filter.to_at) if filter.to_at
|
|
85
|
+
|
|
86
|
+
relation
|
|
87
|
+
end
|
|
86
88
|
end
|
|
87
89
|
end
|
|
88
90
|
end
|