mihari 4.9.0 → 4.10.0
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/lib/mihari/analyzers/base.rb +14 -2
- data/lib/mihari/commands/search.rb +1 -1
- data/lib/mihari/emitters/misp.rb +23 -12
- data/lib/mihari/emitters/the_hive.rb +35 -17
- data/lib/mihari/schemas/emitter.rb +2 -0
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari.rb +4 -2
- data/mihari.gemspec +4 -5
- data/sig/lib/mihari/emitters/misp.rbs +3 -3
- data/sig/lib/mihari/emitters/the_hive.rbs +3 -3
- data/sig/lib/mihari.rbs +2 -2
- metadata +10 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 54ef0421cb5da34c8174fe0fdc3b43d310770dca779dd51fac284a678b86fc0a
|
4
|
+
data.tar.gz: 6b24c7b37c00f7ebed9ee0fcfbfb66b95d7d60ea7c7792a40be32607bdda07fe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 941a6e47d53a2287d793fe28fab3e6e9295076ed39e50fcf8f55e6cdca102381ae425b3db603acdfea9b28e438a7f57627ed793be1efcb0093d83b17ac128535
|
7
|
+
data.tar.gz: 25055c04746f33620e0c762f1d79d6407d8a9156d47820c2129ed4b72e926e7cf2d3ea275ff546046bb5b9ce82fe069b222d56f5b69700eb44179f83d80c058c
|
@@ -72,10 +72,22 @@ module Mihari
|
|
72
72
|
#
|
73
73
|
# @param [Mihari::Emitters::Base] emitter
|
74
74
|
#
|
75
|
-
# @return [nil]
|
75
|
+
# @return [Mihari::Alert, nil]
|
76
76
|
#
|
77
77
|
def run_emitter(emitter)
|
78
|
-
|
78
|
+
return if enriched_artifacts.empty?
|
79
|
+
|
80
|
+
alert_or_something = emitter.run(
|
81
|
+
title: title,
|
82
|
+
description: description,
|
83
|
+
artifacts: enriched_artifacts,
|
84
|
+
source: source,
|
85
|
+
tags: tags
|
86
|
+
)
|
87
|
+
|
88
|
+
Mihari.logger.info "Emission by #{emitter.class} is succedded"
|
89
|
+
|
90
|
+
alert_or_something
|
79
91
|
rescue StandardError => e
|
80
92
|
Mihari.logger.info "Emission by #{emitter.class} is failed: #{e}"
|
81
93
|
end
|
data/lib/mihari/emitters/misp.rb
CHANGED
@@ -6,7 +6,7 @@ module Mihari
|
|
6
6
|
module Emitters
|
7
7
|
class MISP < Base
|
8
8
|
# @return [String, nil]
|
9
|
-
attr_reader :
|
9
|
+
attr_reader :url
|
10
10
|
|
11
11
|
# @return [String, nil]
|
12
12
|
attr_reader :api_key
|
@@ -14,18 +14,29 @@ module Mihari
|
|
14
14
|
def initialize(*args, **kwargs)
|
15
15
|
super(*args, **kwargs)
|
16
16
|
|
17
|
-
@
|
17
|
+
@url = kwargs[:url] || kwargs[:api_endpoint] || Mihari.config.misp_url
|
18
18
|
@api_key = kwargs[:api_key] || Mihari.config.misp_api_key
|
19
19
|
|
20
20
|
::MISP.configure do |config|
|
21
|
-
config.api_endpoint =
|
21
|
+
config.api_endpoint = url
|
22
22
|
config.api_key = api_key
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
26
|
# @return [Boolean]
|
27
27
|
def valid?
|
28
|
-
|
28
|
+
unless url? && api_key?
|
29
|
+
Mihari.logger.info("MISP URL is not set") unless url?
|
30
|
+
Mihari.logger.info("MISP API key is not set") unless api_key?
|
31
|
+
return false
|
32
|
+
end
|
33
|
+
|
34
|
+
unless ping?
|
35
|
+
Mihari.logger.info("MISP URL (#{url}) is not reachable")
|
36
|
+
return false
|
37
|
+
end
|
38
|
+
|
39
|
+
true
|
29
40
|
end
|
30
41
|
|
31
42
|
def emit(title:, artifacts:, tags: [], **_options)
|
@@ -47,7 +58,7 @@ module Mihari
|
|
47
58
|
private
|
48
59
|
|
49
60
|
def configuration_keys
|
50
|
-
%w[
|
61
|
+
%w[misp_url misp_api_key]
|
51
62
|
end
|
52
63
|
|
53
64
|
#
|
@@ -103,12 +114,12 @@ module Mihari
|
|
103
114
|
end
|
104
115
|
|
105
116
|
#
|
106
|
-
# Check whether
|
117
|
+
# Check whether a URL is set or not
|
107
118
|
#
|
108
119
|
# @return [Boolean]
|
109
120
|
#
|
110
|
-
def
|
111
|
-
!
|
121
|
+
def url?
|
122
|
+
!url.nil? && !url.empty?
|
112
123
|
end
|
113
124
|
|
114
125
|
#
|
@@ -121,15 +132,15 @@ module Mihari
|
|
121
132
|
end
|
122
133
|
|
123
134
|
#
|
124
|
-
# Check whether
|
135
|
+
# Check whether a URL is reachable or not
|
125
136
|
#
|
126
137
|
# @return [Boolean]
|
127
138
|
#
|
128
139
|
def ping?
|
129
|
-
base_url =
|
130
|
-
|
140
|
+
base_url = url.end_with?("/") ? url[0..-2] : url
|
141
|
+
login_url = "#{base_url}/users/login"
|
131
142
|
|
132
|
-
http = Net::Ping::HTTP.new(
|
143
|
+
http = Net::Ping::HTTP.new(login_url)
|
133
144
|
http.ping?
|
134
145
|
end
|
135
146
|
end
|
@@ -6,7 +6,7 @@ module Mihari
|
|
6
6
|
module Emitters
|
7
7
|
class TheHive < Base
|
8
8
|
# @return [String, nil]
|
9
|
-
attr_reader :
|
9
|
+
attr_reader :url
|
10
10
|
|
11
11
|
# @return [String, nil]
|
12
12
|
attr_reader :api_key
|
@@ -17,14 +17,25 @@ module Mihari
|
|
17
17
|
def initialize(*args, **kwargs)
|
18
18
|
super(*args, **kwargs)
|
19
19
|
|
20
|
-
@
|
20
|
+
@url = kwargs[:url] || kwargs[:api_endpoint] || Mihari.config.thehive_url
|
21
21
|
@api_key = kwargs[:api_key] || Mihari.config.thehive_api_key
|
22
22
|
@api_version = kwargs[:api_version] || Mihari.config.thehive_api_version
|
23
23
|
end
|
24
24
|
|
25
25
|
# @return [Boolean]
|
26
26
|
def valid?
|
27
|
-
|
27
|
+
unless url? && api_key?
|
28
|
+
Mihari.logger.info("TheHive URL is not set") unless url?
|
29
|
+
Mihari.logger.info("TheHive API key is not set") unless api_key?
|
30
|
+
return false
|
31
|
+
end
|
32
|
+
|
33
|
+
unless ping?
|
34
|
+
Mihari.logger.info("TheHive URL (#{url}) is not reachable")
|
35
|
+
return false
|
36
|
+
end
|
37
|
+
|
38
|
+
true
|
28
39
|
end
|
29
40
|
|
30
41
|
def emit(title:, description:, artifacts:, tags: [], **_options)
|
@@ -57,20 +68,20 @@ module Mihari
|
|
57
68
|
private
|
58
69
|
|
59
70
|
def configuration_keys
|
60
|
-
%w[
|
71
|
+
%w[thehive_url thehive_api_key]
|
61
72
|
end
|
62
73
|
|
63
74
|
def api
|
64
|
-
@api ||= Hachi::API.new(api_endpoint:
|
75
|
+
@api ||= Hachi::API.new(api_endpoint: url, api_key: api_key, api_version: normalized_api_version)
|
65
76
|
end
|
66
77
|
|
67
78
|
#
|
68
|
-
# Check whether
|
79
|
+
# Check whether a URL is set or not
|
69
80
|
#
|
70
81
|
# @return [Boolean]
|
71
82
|
#
|
72
|
-
def
|
73
|
-
!
|
83
|
+
def url?
|
84
|
+
!url.nil?
|
74
85
|
end
|
75
86
|
|
76
87
|
#
|
@@ -83,7 +94,10 @@ module Mihari
|
|
83
94
|
end
|
84
95
|
|
85
96
|
def payload(title:, description:, artifacts:, tags: [])
|
86
|
-
|
97
|
+
if normalized_api_version.nil?
|
98
|
+
return v4_payload(title: title, description: description, artifacts: artifacts,
|
99
|
+
tags: tags)
|
100
|
+
end
|
87
101
|
|
88
102
|
v5_payload(title: title, description: description, artifacts: artifacts, tags: tags)
|
89
103
|
end
|
@@ -92,7 +106,9 @@ module Mihari
|
|
92
106
|
{
|
93
107
|
title: title,
|
94
108
|
description: description,
|
95
|
-
artifacts: artifacts.map
|
109
|
+
artifacts: artifacts.map do |artifact|
|
110
|
+
{ data: artifact.data, data_type: artifact.data_type, message: description }
|
111
|
+
end,
|
96
112
|
tags: tags,
|
97
113
|
type: "external",
|
98
114
|
source: "mihari"
|
@@ -103,7 +119,9 @@ module Mihari
|
|
103
119
|
{
|
104
120
|
title: title,
|
105
121
|
description: description,
|
106
|
-
observables: artifacts.map
|
122
|
+
observables: artifacts.map do |artifact|
|
123
|
+
{ data: artifact.data, data_type: artifact.data_type, message: description }
|
124
|
+
end,
|
107
125
|
tags: tags,
|
108
126
|
type: "external",
|
109
127
|
source: "mihari",
|
@@ -112,23 +130,23 @@ module Mihari
|
|
112
130
|
end
|
113
131
|
|
114
132
|
#
|
115
|
-
# Check whether
|
133
|
+
# Check whether a URL is reachable or not
|
116
134
|
#
|
117
135
|
# @return [Boolean]
|
118
136
|
#
|
119
137
|
def ping?
|
120
|
-
base_url =
|
138
|
+
base_url = url.end_with?("/") ? url[0..-2] : url
|
121
139
|
|
122
140
|
if normalized_api_version.nil?
|
123
141
|
# for v4
|
124
|
-
base_url =
|
125
|
-
|
142
|
+
base_url = url.end_with?("/") ? url[0..-2] : url
|
143
|
+
public_url = "#{base_url}/index.html"
|
126
144
|
else
|
127
145
|
# for v5
|
128
|
-
|
146
|
+
public_url = "#{base_url}/api/v1/status/public"
|
129
147
|
end
|
130
148
|
|
131
|
-
http = Net::Ping::HTTP.new(
|
149
|
+
http = Net::Ping::HTTP.new(public_url)
|
132
150
|
|
133
151
|
# use GET for v5
|
134
152
|
http.get_request = true if normalized_api_version
|
@@ -9,12 +9,14 @@ module Mihari
|
|
9
9
|
MISP = Dry::Schema.Params do
|
10
10
|
required(:emitter).value(Types::String.enum("misp"))
|
11
11
|
optional(:api_endpoint).value(:string)
|
12
|
+
optional(:url).value(:string)
|
12
13
|
optional(:api_key).value(:string)
|
13
14
|
end
|
14
15
|
|
15
16
|
TheHive = Dry::Schema.Params do
|
16
17
|
required(:emitter).value(Types::String.enum("the_hive"))
|
17
18
|
optional(:api_endpoint).value(:string)
|
19
|
+
optional(:url).value(:string)
|
18
20
|
optional(:api_key).value(:string)
|
19
21
|
optional(:api_version).value(Types::String.enum("v4", "v5")).default("v4")
|
20
22
|
end
|
data/lib/mihari/version.rb
CHANGED
data/lib/mihari.rb
CHANGED
@@ -84,7 +84,8 @@ module Mihari
|
|
84
84
|
|
85
85
|
setting :ipinfo_api_key, default: ENV.fetch("IPINFO_API_KEY", nil)
|
86
86
|
|
87
|
-
|
87
|
+
# TODO: deprecate MISP_API_ENDPOINT
|
88
|
+
setting :misp_url, default: ENV.fetch("MISP_URL", nil) || ENV.fetch("MISP_API_ENDPOINT", nil)
|
88
89
|
setting :misp_api_key, default: ENV.fetch("MISP_API_KEY", nil)
|
89
90
|
|
90
91
|
setting :onyphe_api_key, default: ENV.fetch("ONYPHE_API_KEY", nil)
|
@@ -105,7 +106,8 @@ module Mihari
|
|
105
106
|
|
106
107
|
setting :spyse_api_key, default: ENV.fetch("SPYSE_API_KEY", nil)
|
107
108
|
|
108
|
-
|
109
|
+
# TODO: deprecate THEHIVE_API_ENDPOINT
|
110
|
+
setting :thehive_url, default: ENV.fetch("THEHIVE_URL", nil) || ENV.fetch("THEHIVE_API_ENDPOINT", nil)
|
109
111
|
setting :thehive_api_key, default: ENV.fetch("THEHIVE_API_KEY", nil)
|
110
112
|
setting :thehive_api_version, default: ENV.fetch("THEHIVE_API_VERSION", nil)
|
111
113
|
|
data/mihari.gemspec
CHANGED
@@ -40,7 +40,7 @@ Gem::Specification.new do |spec|
|
|
40
40
|
spec.add_development_dependency "rspec", "~> 3.11"
|
41
41
|
spec.add_development_dependency "simplecov-lcov", "~> 0.8.0"
|
42
42
|
spec.add_development_dependency "standard", "~> 1.16"
|
43
|
-
spec.add_development_dependency "steep", "~> 1.
|
43
|
+
spec.add_development_dependency "steep", "~> 1.2"
|
44
44
|
spec.add_development_dependency "timecop", "~> 0.9"
|
45
45
|
spec.add_development_dependency "vcr", "~> 6.1"
|
46
46
|
spec.add_development_dependency "webmock", "~> 3.18"
|
@@ -54,11 +54,11 @@ Gem::Specification.new do |spec|
|
|
54
54
|
spec.add_dependency "dnpedia", "0.1.0"
|
55
55
|
spec.add_dependency "dnstwister", "0.1.0"
|
56
56
|
spec.add_dependency "dotenv", "2.8.1"
|
57
|
-
spec.add_dependency "dry-configurable", "0.
|
57
|
+
spec.add_dependency "dry-configurable", "0.16.0"
|
58
58
|
spec.add_dependency "dry-container", "0.11.0"
|
59
59
|
spec.add_dependency "dry-files", "0.3.0"
|
60
60
|
spec.add_dependency "dry-initializer", "3.1.1"
|
61
|
-
spec.add_dependency "dry-schema", "1.10.
|
61
|
+
spec.add_dependency "dry-schema", "1.10.6"
|
62
62
|
spec.add_dependency "dry-struct", "1.4.0"
|
63
63
|
spec.add_dependency "dry-validation", "1.8.1"
|
64
64
|
spec.add_dependency "email_address", "0.2.4"
|
@@ -88,10 +88,9 @@ Gem::Specification.new do |spec|
|
|
88
88
|
spec.add_dependency "rack-cors", "1.1.1"
|
89
89
|
spec.add_dependency "securitytrails", "1.0.0"
|
90
90
|
spec.add_dependency "semantic_logger", "4.11.0"
|
91
|
-
spec.add_dependency "sentry-ruby", "5.
|
91
|
+
spec.add_dependency "sentry-ruby", "5.5.0"
|
92
92
|
spec.add_dependency "shodanx", "0.2.1"
|
93
93
|
spec.add_dependency "slack-notifier", "2.4.0"
|
94
|
-
spec.add_dependency "spysex", "0.2.0"
|
95
94
|
spec.add_dependency "sqlite3", "1.5.0"
|
96
95
|
spec.add_dependency "thor", "1.2.1"
|
97
96
|
spec.add_dependency "urlscan", "0.8.0"
|
@@ -2,7 +2,7 @@ module Mihari
|
|
2
2
|
module Emitters
|
3
3
|
class MISP < Base
|
4
4
|
|
5
|
-
attr_reader
|
5
|
+
attr_reader url: String?
|
6
6
|
|
7
7
|
attr_reader api_key: String?
|
8
8
|
|
@@ -16,7 +16,7 @@ module Mihari
|
|
16
16
|
|
17
17
|
private
|
18
18
|
|
19
|
-
def configuration_keys: () -> ::Array["
|
19
|
+
def configuration_keys: () -> ::Array["misp_url" | "misp_api_key"]
|
20
20
|
|
21
21
|
def build_attribute: (Mihari::Artifact artifact) -> untyped
|
22
22
|
|
@@ -24,7 +24,7 @@ module Mihari
|
|
24
24
|
|
25
25
|
def to_misp_type: (type: String `type`, value: String value) -> String?
|
26
26
|
|
27
|
-
def
|
27
|
+
def url?: () -> bool
|
28
28
|
|
29
29
|
def api_key?: () -> bool
|
30
30
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Mihari
|
2
2
|
module Emitters
|
3
3
|
class TheHive < Base
|
4
|
-
attr_reader
|
4
|
+
attr_reader url: String?
|
5
5
|
|
6
6
|
attr_reader api_key: String?
|
7
7
|
|
@@ -16,12 +16,12 @@ module Mihari
|
|
16
16
|
|
17
17
|
private
|
18
18
|
|
19
|
-
def configuration_keys: () -> ::Array["
|
19
|
+
def configuration_keys: () -> ::Array["thehive_url" | "thehive_api_key"]
|
20
20
|
|
21
21
|
def api: () -> untyped
|
22
22
|
|
23
23
|
# @return [true, false]
|
24
|
-
def
|
24
|
+
def url?: () -> bool
|
25
25
|
|
26
26
|
# @return [true, false]
|
27
27
|
def api_key?: () -> bool
|
data/sig/lib/mihari.rbs
CHANGED
@@ -5,7 +5,7 @@ class Configuration
|
|
5
5
|
attr_accessor circl_passive_password (): String?
|
6
6
|
attr_accessor circl_passive_username (): String?
|
7
7
|
attr_accessor ipinfo_api_key (): String?
|
8
|
-
attr_accessor
|
8
|
+
attr_accessor misp_url (): String?
|
9
9
|
attr_accessor misp_api_key (): String?
|
10
10
|
attr_accessor onyphe_api_key (): String?
|
11
11
|
attr_accessor otx_api_key (): String?
|
@@ -17,7 +17,7 @@ class Configuration
|
|
17
17
|
attr_accessor slack_channel (): String?
|
18
18
|
attr_accessor slack_webhook_url (): String?
|
19
19
|
attr_accessor spyse_api_key (): String?
|
20
|
-
attr_accessor
|
20
|
+
attr_accessor thehive_url (): String?
|
21
21
|
attr_accessor thehive_api_key (): String?
|
22
22
|
attr_accessor thehive_api_version (): String?
|
23
23
|
attr_accessor urlscan_api_key (): String?
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mihari
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Manabu Niseki
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-10-
|
11
|
+
date: 2022-10-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -198,14 +198,14 @@ dependencies:
|
|
198
198
|
requirements:
|
199
199
|
- - "~>"
|
200
200
|
- !ruby/object:Gem::Version
|
201
|
-
version: '1.
|
201
|
+
version: '1.2'
|
202
202
|
type: :development
|
203
203
|
prerelease: false
|
204
204
|
version_requirements: !ruby/object:Gem::Requirement
|
205
205
|
requirements:
|
206
206
|
- - "~>"
|
207
207
|
- !ruby/object:Gem::Version
|
208
|
-
version: '1.
|
208
|
+
version: '1.2'
|
209
209
|
- !ruby/object:Gem::Dependency
|
210
210
|
name: timecop
|
211
211
|
requirement: !ruby/object:Gem::Requirement
|
@@ -380,14 +380,14 @@ dependencies:
|
|
380
380
|
requirements:
|
381
381
|
- - '='
|
382
382
|
- !ruby/object:Gem::Version
|
383
|
-
version: 0.
|
383
|
+
version: 0.16.0
|
384
384
|
type: :runtime
|
385
385
|
prerelease: false
|
386
386
|
version_requirements: !ruby/object:Gem::Requirement
|
387
387
|
requirements:
|
388
388
|
- - '='
|
389
389
|
- !ruby/object:Gem::Version
|
390
|
-
version: 0.
|
390
|
+
version: 0.16.0
|
391
391
|
- !ruby/object:Gem::Dependency
|
392
392
|
name: dry-container
|
393
393
|
requirement: !ruby/object:Gem::Requirement
|
@@ -436,14 +436,14 @@ dependencies:
|
|
436
436
|
requirements:
|
437
437
|
- - '='
|
438
438
|
- !ruby/object:Gem::Version
|
439
|
-
version: 1.10.
|
439
|
+
version: 1.10.6
|
440
440
|
type: :runtime
|
441
441
|
prerelease: false
|
442
442
|
version_requirements: !ruby/object:Gem::Requirement
|
443
443
|
requirements:
|
444
444
|
- - '='
|
445
445
|
- !ruby/object:Gem::Version
|
446
|
-
version: 1.10.
|
446
|
+
version: 1.10.6
|
447
447
|
- !ruby/object:Gem::Dependency
|
448
448
|
name: dry-struct
|
449
449
|
requirement: !ruby/object:Gem::Requirement
|
@@ -856,14 +856,14 @@ dependencies:
|
|
856
856
|
requirements:
|
857
857
|
- - '='
|
858
858
|
- !ruby/object:Gem::Version
|
859
|
-
version: 5.
|
859
|
+
version: 5.5.0
|
860
860
|
type: :runtime
|
861
861
|
prerelease: false
|
862
862
|
version_requirements: !ruby/object:Gem::Requirement
|
863
863
|
requirements:
|
864
864
|
- - '='
|
865
865
|
- !ruby/object:Gem::Version
|
866
|
-
version: 5.
|
866
|
+
version: 5.5.0
|
867
867
|
- !ruby/object:Gem::Dependency
|
868
868
|
name: shodanx
|
869
869
|
requirement: !ruby/object:Gem::Requirement
|
@@ -892,20 +892,6 @@ dependencies:
|
|
892
892
|
- - '='
|
893
893
|
- !ruby/object:Gem::Version
|
894
894
|
version: 2.4.0
|
895
|
-
- !ruby/object:Gem::Dependency
|
896
|
-
name: spysex
|
897
|
-
requirement: !ruby/object:Gem::Requirement
|
898
|
-
requirements:
|
899
|
-
- - '='
|
900
|
-
- !ruby/object:Gem::Version
|
901
|
-
version: 0.2.0
|
902
|
-
type: :runtime
|
903
|
-
prerelease: false
|
904
|
-
version_requirements: !ruby/object:Gem::Requirement
|
905
|
-
requirements:
|
906
|
-
- - '='
|
907
|
-
- !ruby/object:Gem::Version
|
908
|
-
version: 0.2.0
|
909
895
|
- !ruby/object:Gem::Dependency
|
910
896
|
name: sqlite3
|
911
897
|
requirement: !ruby/object:Gem::Requirement
|