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
data/frontend/package.json
CHANGED
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
"@fortawesome/vue-fontawesome": "^3.0.3",
|
|
20
20
|
"@vueuse/core": "^10.5.0",
|
|
21
21
|
"@vueuse/router": "^10.5.0",
|
|
22
|
-
"ace-builds": "^1.
|
|
23
|
-
"axios": "^1.
|
|
22
|
+
"ace-builds": "^1.31.0",
|
|
23
|
+
"axios": "^1.6.0",
|
|
24
24
|
"bulma": "^0.9.4",
|
|
25
25
|
"bulma-helpers": "^0.4.3",
|
|
26
26
|
"dayjs": "^1.11.10",
|
|
@@ -30,21 +30,21 @@
|
|
|
30
30
|
"ts-dedent": "^2.2.0",
|
|
31
31
|
"url-parse": "^1.5.10",
|
|
32
32
|
"uuidv4": "^6.2.13",
|
|
33
|
-
"vue": "^3.3.
|
|
33
|
+
"vue": "^3.3.7",
|
|
34
34
|
"vue-concurrency": "4.0.1",
|
|
35
35
|
"vue-json-pretty": "^2.2.4",
|
|
36
36
|
"vue-router": "^4.2.5",
|
|
37
37
|
"vue3-ace-editor": "^2.2.3"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@redocly/cli": "1.
|
|
40
|
+
"@redocly/cli": "1.4.0",
|
|
41
41
|
"@rushstack/eslint-patch": "^1.5.1",
|
|
42
42
|
"@tsconfig/node20": "^20.1.2",
|
|
43
43
|
"@types/jsdom": "^21.1.4",
|
|
44
|
-
"@types/node": "^20.8.
|
|
44
|
+
"@types/node": "^20.8.9",
|
|
45
45
|
"@types/url-parse": "^1.4.10",
|
|
46
|
-
"@typescript-eslint/eslint-plugin": "^6.
|
|
47
|
-
"@typescript-eslint/parser": "^6.
|
|
46
|
+
"@typescript-eslint/eslint-plugin": "^6.9.0",
|
|
47
|
+
"@typescript-eslint/parser": "^6.9.0",
|
|
48
48
|
"@vitejs/plugin-vue": "^4.4.0",
|
|
49
49
|
"@vue/eslint-config-prettier": "^8.0.0",
|
|
50
50
|
"@vue/eslint-config-typescript": "^12.0.0",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"eslint-config-prettier": "^9.0.0",
|
|
55
55
|
"eslint-plugin-prettier": "^5.0.1",
|
|
56
56
|
"eslint-plugin-simple-import-sort": "^10.0.0",
|
|
57
|
-
"eslint-plugin-vue": "^9.
|
|
57
|
+
"eslint-plugin-vue": "^9.18.1",
|
|
58
58
|
"husky": "^8.0.3",
|
|
59
59
|
"jsdom": "^22.1.0",
|
|
60
60
|
"npm-run-all": "^4.1.5",
|
|
@@ -62,6 +62,6 @@
|
|
|
62
62
|
"typescript": "~5.2.2",
|
|
63
63
|
"vite": "^4.5.0",
|
|
64
64
|
"vitest": "^0.34.6",
|
|
65
|
-
"vue-tsc": "^1.8.
|
|
65
|
+
"vue-tsc": "^1.8.22"
|
|
66
66
|
}
|
|
67
67
|
}
|
|
@@ -4,7 +4,12 @@ module Mihari
|
|
|
4
4
|
#
|
|
5
5
|
# Base class for Analyzer, Emitter and Enricher
|
|
6
6
|
#
|
|
7
|
-
class
|
|
7
|
+
class Actor
|
|
8
|
+
include Dry::Monads[:result, :try]
|
|
9
|
+
|
|
10
|
+
include Mixins::Configurable
|
|
11
|
+
include Mixins::Retriable
|
|
12
|
+
|
|
8
13
|
# @return [Hash]
|
|
9
14
|
attr_reader :options
|
|
10
15
|
|
|
@@ -43,6 +48,15 @@ module Mihari
|
|
|
43
48
|
options[:timeout]
|
|
44
49
|
end
|
|
45
50
|
|
|
51
|
+
def validate_configuration!
|
|
52
|
+
return if configured?
|
|
53
|
+
|
|
54
|
+
joined = configuration_keys.join(", ")
|
|
55
|
+
be = (configuration_keys.length > 1) ? "are" : "is"
|
|
56
|
+
message = "#{self.class.class_key} is not configured correctly. #{joined} #{be} missing."
|
|
57
|
+
raise ConfigurationError, message
|
|
58
|
+
end
|
|
59
|
+
|
|
46
60
|
class << self
|
|
47
61
|
#
|
|
48
62
|
# @return [String]
|
|
@@ -62,7 +76,7 @@ module Mihari
|
|
|
62
76
|
# @return [Array<String>]
|
|
63
77
|
#
|
|
64
78
|
def class_keys
|
|
65
|
-
([class_key] + [class_key_aliases]).flatten.compact
|
|
79
|
+
([class_key] + [class_key_aliases]).flatten.compact.map(&:downcase)
|
|
66
80
|
end
|
|
67
81
|
end
|
|
68
82
|
end
|
|
@@ -2,12 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Mihari
|
|
4
4
|
module Analyzers
|
|
5
|
-
class Base <
|
|
6
|
-
include Dry::Monads[:result, :try]
|
|
7
|
-
|
|
8
|
-
include Mixins::Configurable
|
|
9
|
-
include Mixins::Retriable
|
|
10
|
-
|
|
5
|
+
class Base < Actor
|
|
11
6
|
# @return [String]
|
|
12
7
|
attr_reader :query
|
|
13
8
|
|
|
@@ -45,7 +40,7 @@ module Mihari
|
|
|
45
40
|
Mihari.config.ignore_error
|
|
46
41
|
end
|
|
47
42
|
|
|
48
|
-
# @return [Array<String>, Array<Mihari::Artifact>]
|
|
43
|
+
# @return [Array<String>, Array<Mihari::Models::Artifact>]
|
|
49
44
|
def artifacts
|
|
50
45
|
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
|
51
46
|
end
|
|
@@ -55,14 +50,14 @@ module Mihari
|
|
|
55
50
|
# - Convert data (string) into an artifact
|
|
56
51
|
# - Reject an invalid artifact
|
|
57
52
|
#
|
|
58
|
-
# @return [Array<Mihari::Artifact>]
|
|
53
|
+
# @return [Array<Mihari::Models::Artifact>]
|
|
59
54
|
#
|
|
60
55
|
def normalized_artifacts
|
|
61
56
|
retry_on_error(times: retry_times, interval: retry_interval, exponential_backoff: retry_exponential_backoff) do
|
|
62
57
|
artifacts.compact.sort.map do |artifact|
|
|
63
58
|
# No need to set data_type manually
|
|
64
59
|
# It is set automatically in #initialize
|
|
65
|
-
artifact = artifact.is_a?(Artifact) ? artifact : Artifact.new(data: artifact)
|
|
60
|
+
artifact = artifact.is_a?(Models::Artifact) ? artifact : Models::Artifact.new(data: artifact)
|
|
66
61
|
artifact.source = self.class.class_key
|
|
67
62
|
artifact
|
|
68
63
|
end.select(&:valid?).uniq(&:data)
|
|
@@ -70,7 +65,7 @@ module Mihari
|
|
|
70
65
|
end
|
|
71
66
|
|
|
72
67
|
#
|
|
73
|
-
# @return [Dry::Monads::Result::Success<Array<Mihari::Artifact>>, Dry::Monads::Result::Failure]
|
|
68
|
+
# @return [Dry::Monads::Result::Success<Array<Mihari::Models::Artifact>>, Dry::Monads::Result::Failure]
|
|
74
69
|
#
|
|
75
70
|
def result
|
|
76
71
|
Try[StandardError] { normalized_artifacts }.to_result
|
|
@@ -29,7 +29,7 @@ module Mihari
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def artifacts
|
|
32
|
-
# @type [Array<Mihari::Artifact>]
|
|
32
|
+
# @type [Array<Mihari::Models::Artifact>]
|
|
33
33
|
artifacts = client.search_with_pagination(query, pagination_limit: pagination_limit).map(&:artifacts).flatten
|
|
34
34
|
|
|
35
35
|
artifacts.select do |artifact|
|
|
@@ -43,7 +43,7 @@ module Mihari
|
|
|
43
43
|
#
|
|
44
44
|
# @return [Array<String>, nil]
|
|
45
45
|
#
|
|
46
|
-
def
|
|
46
|
+
def class_key_aliases
|
|
47
47
|
["vt"]
|
|
48
48
|
end
|
|
49
49
|
end
|
|
@@ -66,7 +66,7 @@ module Mihari
|
|
|
66
66
|
#
|
|
67
67
|
# Domain search
|
|
68
68
|
#
|
|
69
|
-
# @return [Array<Mihari::Artifact>]
|
|
69
|
+
# @return [Array<Mihari::Models::Artifact>]
|
|
70
70
|
#
|
|
71
71
|
def domain_search
|
|
72
72
|
res = client.domain_search(query)
|
|
@@ -74,14 +74,14 @@ module Mihari
|
|
|
74
74
|
data = res["data"] || []
|
|
75
75
|
data.filter_map do |item|
|
|
76
76
|
data = item.dig("attributes", "ip_address")
|
|
77
|
-
data.nil? ? nil : Artifact.new(data: data, metadata: item)
|
|
77
|
+
data.nil? ? nil : Models::Artifact.new(data: data, metadata: item)
|
|
78
78
|
end
|
|
79
79
|
end
|
|
80
80
|
|
|
81
81
|
#
|
|
82
82
|
# IP search
|
|
83
83
|
#
|
|
84
|
-
# @return [Array<Mihari::Artifact>]
|
|
84
|
+
# @return [Array<Mihari::Models::Artifact>]
|
|
85
85
|
#
|
|
86
86
|
def ip_search
|
|
87
87
|
res = client.ip_search(query)
|
|
@@ -89,7 +89,7 @@ module Mihari
|
|
|
89
89
|
data = res["data"] || []
|
|
90
90
|
data.filter_map do |item|
|
|
91
91
|
data = item.dig("attributes", "host_name")
|
|
92
|
-
Artifact.new(data: data, metadata: item)
|
|
92
|
+
Models::Artifact.new(data: data, metadata: item)
|
|
93
93
|
end.uniq
|
|
94
94
|
end
|
|
95
95
|
end
|
|
@@ -65,7 +65,7 @@ module Mihari
|
|
|
65
65
|
#
|
|
66
66
|
# @param [Hash] response
|
|
67
67
|
#
|
|
68
|
-
# @return [Array<Mihari::Artifact>]
|
|
68
|
+
# @return [Array<Mihari::Models::Artifact>]
|
|
69
69
|
#
|
|
70
70
|
def convert(res)
|
|
71
71
|
matches = res["matches"] || []
|
|
@@ -73,9 +73,9 @@ module Mihari
|
|
|
73
73
|
data = match["ip"]
|
|
74
74
|
|
|
75
75
|
if data.is_a?(Array)
|
|
76
|
-
data.map { |d| Artifact.new(data: d, metadata: match) }
|
|
76
|
+
data.map { |d| Models::Artifact.new(data: d, metadata: match) }
|
|
77
77
|
else
|
|
78
|
-
Artifact.new(data: data, metadata: match)
|
|
78
|
+
Models::Artifact.new(data: data, metadata: match)
|
|
79
79
|
end
|
|
80
80
|
end.flatten
|
|
81
81
|
end
|
data/lib/mihari/clients/crtsh.rb
CHANGED
|
@@ -19,7 +19,7 @@ module Mihari
|
|
|
19
19
|
# @param [String, nil] match "=", "ILIKE", "LIKE", "single", "any" or nil
|
|
20
20
|
# @param [String, nil] exclude "expired" or nil
|
|
21
21
|
#
|
|
22
|
-
# @return [Array<Mihari::Artifact>]
|
|
22
|
+
# @return [Array<Mihari::Models::Artifact>]
|
|
23
23
|
#
|
|
24
24
|
def search(identity, match: nil, exclude: nil)
|
|
25
25
|
params = { identity: identity, match: match, exclude: exclude, output: "json" }.compact
|
|
@@ -29,7 +29,7 @@ module Mihari
|
|
|
29
29
|
|
|
30
30
|
parsed.map do |result|
|
|
31
31
|
values = result["name_value"].to_s.lines.map(&:chomp)
|
|
32
|
-
values.map { |value| Artifact.new(data: value, metadata: result) }
|
|
32
|
+
values.map { |value| Models::Artifact.new(data: value, metadata: result) }
|
|
33
33
|
end.flatten
|
|
34
34
|
end
|
|
35
35
|
end
|
|
@@ -39,7 +39,7 @@ module Mihari
|
|
|
39
39
|
#
|
|
40
40
|
# @param [String] query
|
|
41
41
|
#
|
|
42
|
-
# @return [Array<Mihari::Artifact>]
|
|
42
|
+
# @return [Array<Mihari::Models::Artifact>]
|
|
43
43
|
#
|
|
44
44
|
def reverse_whois_search(query)
|
|
45
45
|
params = {
|
|
@@ -50,7 +50,7 @@ module Mihari
|
|
|
50
50
|
results = res["results"] || []
|
|
51
51
|
results.map do |result|
|
|
52
52
|
data = result["domain"]
|
|
53
|
-
Artifact.new(data: data, metadata: result)
|
|
53
|
+
Models::Artifact.new(data: data, metadata: result)
|
|
54
54
|
end.flatten
|
|
55
55
|
end
|
|
56
56
|
|
|
@@ -59,7 +59,7 @@ module Mihari
|
|
|
59
59
|
#
|
|
60
60
|
# @param [String] query
|
|
61
61
|
#
|
|
62
|
-
# @return [Array<Mihari::Artifact>]
|
|
62
|
+
# @return [Array<Mihari::Models::Artifact>]
|
|
63
63
|
#
|
|
64
64
|
def ssl_search(query)
|
|
65
65
|
params = { query: query }
|
|
@@ -67,7 +67,7 @@ module Mihari
|
|
|
67
67
|
results = res["results"] || []
|
|
68
68
|
results.map do |result|
|
|
69
69
|
data = result["ipAddresses"]
|
|
70
|
-
data.map { |d| Artifact.new(data: d, metadata: result) }
|
|
70
|
+
data.map { |d| Models::Artifact.new(data: d, metadata: result) }
|
|
71
71
|
end.flatten
|
|
72
72
|
end
|
|
73
73
|
|
|
@@ -36,13 +36,13 @@ module Mihari
|
|
|
36
36
|
#
|
|
37
37
|
# @param [String] query
|
|
38
38
|
#
|
|
39
|
-
# @return [Array<Mihari::Artifact>]
|
|
39
|
+
# @return [Array<Mihari::Models::Artifact>]
|
|
40
40
|
#
|
|
41
41
|
def ip_search(query)
|
|
42
42
|
records = search_by_ip(query)
|
|
43
43
|
records.filter_map do |record|
|
|
44
44
|
data = record["hostname"]
|
|
45
|
-
Artifact.new(data: data, metadata: record)
|
|
45
|
+
Models::Artifact.new(data: data, metadata: record)
|
|
46
46
|
end
|
|
47
47
|
end
|
|
48
48
|
|
|
@@ -57,7 +57,7 @@ module Mihari
|
|
|
57
57
|
records = search_by_mail(query)
|
|
58
58
|
records.filter_map do |record|
|
|
59
59
|
data = record["hostname"]
|
|
60
|
-
Artifact.new(data: data, metadata: record)
|
|
60
|
+
Models::Artifact.new(data: data, metadata: record)
|
|
61
61
|
end
|
|
62
62
|
end
|
|
63
63
|
|
data/lib/mihari/commands/rule.rb
CHANGED
|
@@ -17,10 +17,7 @@ module Mihari
|
|
|
17
17
|
# @param [String] path
|
|
18
18
|
#
|
|
19
19
|
def validate(path)
|
|
20
|
-
res = Dry::Monads::Try[ValidationError]
|
|
21
|
-
Services::RuleProxy.from_yaml File.read(path)
|
|
22
|
-
end
|
|
23
|
-
|
|
20
|
+
res = Dry::Monads::Try[ValidationError] { Mihari::Rule.from_yaml File.read(path) }
|
|
24
21
|
rule = res.value!
|
|
25
22
|
puts rule.data.to_yaml
|
|
26
23
|
end
|
|
@@ -42,13 +39,6 @@ module Mihari
|
|
|
42
39
|
end
|
|
43
40
|
|
|
44
41
|
no_commands do
|
|
45
|
-
#
|
|
46
|
-
# @return [Mihari::Services::Rule]
|
|
47
|
-
#
|
|
48
|
-
def rule
|
|
49
|
-
Services::RuleProxy.from_yaml File.read(File.expand_path("../templates/rule.yml.erb", __dir__))
|
|
50
|
-
end
|
|
51
|
-
|
|
52
42
|
#
|
|
53
43
|
# Create a new rule
|
|
54
44
|
#
|
|
@@ -58,6 +48,7 @@ module Mihari
|
|
|
58
48
|
# @return [nil]
|
|
59
49
|
#
|
|
60
50
|
def initialize_rule(path, files = Dry::Files.new)
|
|
51
|
+
rule = Mihari::Rule.from_yaml File.read(File.expand_path("../templates/rule.yml.erb", __dir__))
|
|
61
52
|
files.write(path, rule.yaml)
|
|
62
53
|
end
|
|
63
54
|
end
|
data/lib/mihari/emitters/base.rb
CHANGED
|
@@ -2,49 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
module Mihari
|
|
4
4
|
module Emitters
|
|
5
|
-
class Base <
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
include Mixins::Configurable
|
|
9
|
-
include Mixins::Retriable
|
|
10
|
-
|
|
11
|
-
# @return [Array<Mihari::Artifact>]
|
|
12
|
-
attr_reader :artifacts
|
|
13
|
-
|
|
14
|
-
# @return [Mihari::Services::Rule]
|
|
5
|
+
class Base < Actor
|
|
6
|
+
# @return [Mihari::Rule]
|
|
15
7
|
attr_reader :rule
|
|
16
8
|
|
|
17
9
|
#
|
|
18
|
-
# @param [
|
|
19
|
-
# @param [Mihari::Services::RuleProxy] rule
|
|
10
|
+
# @param [Mihari::Rule] rule
|
|
20
11
|
# @param [Hash, nil] options
|
|
21
12
|
# @param [Hash] **_params
|
|
22
13
|
#
|
|
23
|
-
def initialize(
|
|
14
|
+
def initialize(rule:, options: nil, **_params)
|
|
24
15
|
super(options: options)
|
|
25
16
|
|
|
26
|
-
@artifacts = artifacts
|
|
27
17
|
@rule = rule
|
|
28
18
|
end
|
|
29
19
|
|
|
30
|
-
#
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def result
|
|
20
|
+
#
|
|
21
|
+
# @param [Array<Mihari::Models::Artifact>] artifacts
|
|
22
|
+
#
|
|
23
|
+
def emit_result(artifacts)
|
|
36
24
|
Try[StandardError] do
|
|
37
25
|
retry_on_error(
|
|
38
26
|
times: retry_times,
|
|
39
27
|
interval: retry_interval,
|
|
40
28
|
exponential_backoff: retry_exponential_backoff
|
|
41
|
-
)
|
|
42
|
-
emit
|
|
43
|
-
end
|
|
29
|
+
) { emit artifacts }
|
|
44
30
|
end.to_result
|
|
45
31
|
end
|
|
46
32
|
|
|
47
|
-
|
|
33
|
+
#
|
|
34
|
+
# @param [Array<Mihari::Models::Artifact>] artifacts
|
|
35
|
+
#
|
|
36
|
+
def emit(artifacts)
|
|
48
37
|
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
|
49
38
|
end
|
|
50
39
|
|
|
@@ -3,22 +3,20 @@
|
|
|
3
3
|
module Mihari
|
|
4
4
|
module Emitters
|
|
5
5
|
class Database < Base
|
|
6
|
-
def valid?
|
|
7
|
-
configured?
|
|
8
|
-
end
|
|
9
|
-
|
|
10
6
|
#
|
|
11
7
|
# Create an alert
|
|
12
8
|
#
|
|
13
|
-
# @
|
|
9
|
+
# @param [Array<Mihari::Models::Artifact>] artifacts
|
|
10
|
+
#
|
|
11
|
+
# @return [Mihari::Models::Alert, nil]
|
|
14
12
|
#
|
|
15
|
-
def emit
|
|
13
|
+
def emit(artifacts)
|
|
16
14
|
return if artifacts.empty?
|
|
17
15
|
|
|
18
|
-
tags = rule.tags.filter_map { |name| Tag.find_or_create_by(name: name) }.uniq
|
|
19
|
-
taggings = tags.map { |tag| Tagging.new(tag_id: tag.id) }
|
|
16
|
+
tags = rule.tags.filter_map { |name| Models::Tag.find_or_create_by(name: name) }.uniq
|
|
17
|
+
taggings = tags.map { |tag| Models::Tagging.new(tag_id: tag.id) }
|
|
20
18
|
|
|
21
|
-
alert = Alert.new(artifacts: artifacts, taggings: taggings, rule_id: rule.id)
|
|
19
|
+
alert = Models::Alert.new(artifacts: artifacts, taggings: taggings, rule_id: rule.id)
|
|
22
20
|
alert.save
|
|
23
21
|
alert
|
|
24
22
|
end
|
data/lib/mihari/emitters/misp.rb
CHANGED
|
@@ -9,50 +9,39 @@ module Mihari
|
|
|
9
9
|
# @return [String, nil]
|
|
10
10
|
attr_reader :api_key
|
|
11
11
|
|
|
12
|
-
# @return [Array<Mihari::Artifact>]
|
|
13
|
-
attr_reader :artifacts
|
|
14
|
-
|
|
15
12
|
# @return [Mihari::Services::Rule]
|
|
16
13
|
attr_reader :rule
|
|
17
14
|
|
|
15
|
+
# @return [Array<Mihari::Models::Artifact>]
|
|
16
|
+
attr_accessor :artifacts
|
|
17
|
+
|
|
18
18
|
#
|
|
19
|
-
# @param [Array<Mihari::Artifact>] artifacts
|
|
20
19
|
# @param [Mihari::Services::Rule] rule
|
|
21
20
|
# @param [Hash, nil] options
|
|
22
21
|
# @param [Hash] **params
|
|
23
22
|
#
|
|
24
|
-
def initialize(
|
|
25
|
-
super(
|
|
23
|
+
def initialize(rule:, options: nil, **params)
|
|
24
|
+
super(rule: rule, options: options)
|
|
26
25
|
|
|
27
26
|
@url = params[:url] || Mihari.config.misp_url
|
|
28
27
|
@api_key = params[:api_key] || Mihari.config.misp_api_key
|
|
28
|
+
|
|
29
|
+
@artifacts = []
|
|
29
30
|
end
|
|
30
31
|
|
|
32
|
+
#
|
|
31
33
|
# @return [Boolean]
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
Mihari.logger.info("MISP API key is not set") unless api_key?
|
|
36
|
-
return false
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
unless ping?
|
|
40
|
-
Mihari.logger.info("MISP URL (#{url}) is not reachable")
|
|
41
|
-
return false
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
true
|
|
34
|
+
#
|
|
35
|
+
def configured?
|
|
36
|
+
api_key? && url?
|
|
45
37
|
end
|
|
46
38
|
|
|
47
39
|
#
|
|
48
40
|
# Create a MISP event
|
|
49
41
|
#
|
|
50
|
-
# @param [
|
|
51
|
-
# @param [Mihari::Services::Rule] rule
|
|
52
|
-
#
|
|
53
|
-
# @return [::MISP::Event]
|
|
42
|
+
# @param [Array<Mihari::Models::Artifact>] artifacts
|
|
54
43
|
#
|
|
55
|
-
def emit
|
|
44
|
+
def emit(artifacts)
|
|
56
45
|
return if artifacts.empty?
|
|
57
46
|
|
|
58
47
|
client.create_event({
|
|
@@ -77,7 +66,7 @@ module Mihari
|
|
|
77
66
|
#
|
|
78
67
|
# Build a MISP attribute
|
|
79
68
|
#
|
|
80
|
-
# @param [Mihari::Artifact] artifact
|
|
69
|
+
# @param [Mihari::Models::Artifact] artifact
|
|
81
70
|
#
|
|
82
71
|
# @return [Hash]
|
|
83
72
|
#
|
|
@@ -143,19 +132,6 @@ module Mihari
|
|
|
143
132
|
def api_key?
|
|
144
133
|
!api_key.nil? && !api_key.empty?
|
|
145
134
|
end
|
|
146
|
-
|
|
147
|
-
#
|
|
148
|
-
# Check whether a URL is reachable or not
|
|
149
|
-
#
|
|
150
|
-
# @return [Boolean]
|
|
151
|
-
#
|
|
152
|
-
def ping?
|
|
153
|
-
base_url = url.end_with?("/") ? url[0..-2] : url
|
|
154
|
-
login_url = "#{base_url}/users/login"
|
|
155
|
-
|
|
156
|
-
http = Net::Ping::HTTP.new(login_url)
|
|
157
|
-
http.ping?
|
|
158
|
-
end
|
|
159
135
|
end
|
|
160
136
|
end
|
|
161
137
|
end
|
|
@@ -131,18 +131,22 @@ module Mihari
|
|
|
131
131
|
# @return [String]
|
|
132
132
|
attr_reader :username
|
|
133
133
|
|
|
134
|
+
# @return [Array<Mihari::Models::Artifact>]
|
|
135
|
+
attr_accessor :artifacts
|
|
136
|
+
|
|
134
137
|
#
|
|
135
|
-
# @param [Array<Mihari::Artifact>] artifacts
|
|
136
138
|
# @param [Mihari::Services::Rule] rule
|
|
137
139
|
# @param [Hash, nil] options
|
|
138
140
|
# @param [Hash] **params
|
|
139
141
|
#
|
|
140
|
-
def initialize(
|
|
141
|
-
super(
|
|
142
|
+
def initialize(rule:, options: nil, **params)
|
|
143
|
+
super(rule: rule, options: options)
|
|
142
144
|
|
|
143
145
|
@webhook_url = params[:webhook_url] || Mihari.config.slack_webhook_url
|
|
144
146
|
@channel = params[:channel] || Mihari.config.slack_channel || DEFAULT_CHANNEL
|
|
145
147
|
@username = DEFAULT_USERNAME
|
|
148
|
+
|
|
149
|
+
@artifacts = []
|
|
146
150
|
end
|
|
147
151
|
|
|
148
152
|
#
|
|
@@ -154,12 +158,10 @@ module Mihari
|
|
|
154
158
|
!webhook_url.nil?
|
|
155
159
|
end
|
|
156
160
|
|
|
157
|
-
#
|
|
158
|
-
# Check webhook URL is set. Alias of #webhook_url?
|
|
159
161
|
#
|
|
160
162
|
# @return [Boolean]
|
|
161
163
|
#
|
|
162
|
-
def
|
|
164
|
+
def configured?
|
|
163
165
|
webhook_url?
|
|
164
166
|
end
|
|
165
167
|
|
|
@@ -211,19 +213,20 @@ module Mihari
|
|
|
211
213
|
].join("\n")
|
|
212
214
|
end
|
|
213
215
|
|
|
214
|
-
|
|
216
|
+
#
|
|
217
|
+
# @param [Array<Mihari::Models::Artifact>] artifacts
|
|
218
|
+
#
|
|
219
|
+
def emit(artifacts)
|
|
215
220
|
return if artifacts.empty?
|
|
216
221
|
|
|
222
|
+
@artifacts = artifacts
|
|
223
|
+
|
|
217
224
|
notifier.post(text: text, attachments: attachments, mrkdwn: true)
|
|
218
225
|
end
|
|
219
226
|
|
|
220
227
|
def configuration_keys
|
|
221
228
|
%w[slack_webhook_url slack_channel]
|
|
222
229
|
end
|
|
223
|
-
|
|
224
|
-
def configured?
|
|
225
|
-
valid?
|
|
226
|
-
end
|
|
227
230
|
end
|
|
228
231
|
end
|
|
229
232
|
end
|