mihari 7.2.0 → 7.3.1
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/Dockerfile +1 -1
- data/lib/mihari/emitters/base.rb +7 -0
- data/lib/mihari/enrichers/base.rb +54 -12
- data/lib/mihari/enrichers/google_public_dns.rb +28 -7
- data/lib/mihari/enrichers/mmdb.rb +25 -7
- data/lib/mihari/enrichers/shodan.rb +35 -4
- data/lib/mihari/enrichers/whois.rb +32 -24
- data/lib/mihari/models/alert.rb +12 -0
- data/lib/mihari/models/artifact.rb +105 -181
- data/lib/mihari/models/rule.rb +21 -0
- data/lib/mihari/rule.rb +26 -14
- data/lib/mihari/schemas/alert.rb +3 -3
- data/lib/mihari/schemas/analyzer.rb +28 -28
- data/lib/mihari/schemas/concerns/orrable.rb +1 -1
- data/lib/mihari/schemas/emitter.rb +15 -15
- data/lib/mihari/schemas/enricher.rb +1 -1
- data/lib/mihari/schemas/macros.rb +2 -2
- data/lib/mihari/schemas/options.rb +14 -12
- data/lib/mihari/schemas/rule.rb +14 -14
- data/lib/mihari/services/builders.rb +0 -153
- data/lib/mihari/services/enrichers.rb +1 -1
- data/lib/mihari/services/getters.rb +1 -1
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/public/assets/{index-GWurHG1o.js → index-JHS0L8KZ.js} +29 -29
- data/lib/mihari/web/public/index.html +1 -1
- data/mihari.gemspec +2 -2
- data/requirements.txt +1 -1
- metadata +7 -7
data/lib/mihari/models/rule.rb
CHANGED
@@ -6,6 +6,27 @@ module Mihari
|
|
6
6
|
# Rule model
|
7
7
|
#
|
8
8
|
class Rule < ActiveRecord::Base
|
9
|
+
# @!attribute [rw] id
|
10
|
+
# @return [String]
|
11
|
+
|
12
|
+
# @!attribute [rw] title
|
13
|
+
# @return [String]
|
14
|
+
|
15
|
+
# @!attribute [rw] description
|
16
|
+
# @return [String]
|
17
|
+
|
18
|
+
# @!attribute [rw] data
|
19
|
+
# @return [Hash]
|
20
|
+
|
21
|
+
# @!attribute [rw] created_at
|
22
|
+
# @return [DateTime]
|
23
|
+
|
24
|
+
# @!attribute [rw] updated_at
|
25
|
+
# @return [DateTime]
|
26
|
+
|
27
|
+
# @!attribute [r] alerts
|
28
|
+
# @return [Array<Mihari::Models::Alert>]
|
29
|
+
|
9
30
|
has_many :alerts, dependent: :destroy
|
10
31
|
has_many :taggings, dependent: :destroy
|
11
32
|
has_many :tags, through: :taggings
|
data/lib/mihari/rule.rb
CHANGED
@@ -33,9 +33,9 @@ module Mihari
|
|
33
33
|
# @return [Boolean]
|
34
34
|
#
|
35
35
|
def errors?
|
36
|
-
return false if
|
36
|
+
return false if errors.nil?
|
37
37
|
|
38
|
-
|
38
|
+
!errors.empty?
|
39
39
|
end
|
40
40
|
|
41
41
|
def [](key)
|
@@ -163,9 +163,7 @@ module Mihari
|
|
163
163
|
# @return [Array<Mihari::Models::Artifact>]
|
164
164
|
#
|
165
165
|
def unique_artifacts
|
166
|
-
normalized_artifacts.select
|
167
|
-
artifact.unique?(base_time: base_time, artifact_ttl: artifact_ttl)
|
168
|
-
end
|
166
|
+
normalized_artifacts.select { |artifact| artifact.unique?(base_time: base_time, artifact_ttl: artifact_ttl) }
|
169
167
|
end
|
170
168
|
|
171
169
|
#
|
@@ -175,8 +173,10 @@ module Mihari
|
|
175
173
|
#
|
176
174
|
def enriched_artifacts
|
177
175
|
@enriched_artifacts ||= Parallel.map(unique_artifacts) do |artifact|
|
178
|
-
|
179
|
-
|
176
|
+
artifact.tap do |tapped|
|
177
|
+
# NOTE: To apply changes correctly, enrichers should be applied to an artifact serially
|
178
|
+
enrichers.each { |enricher| enricher.result(tapped) }
|
179
|
+
end
|
180
180
|
end
|
181
181
|
end
|
182
182
|
|
@@ -188,7 +188,10 @@ module Mihari
|
|
188
188
|
def bulk_emit
|
189
189
|
return [] if enriched_artifacts.empty?
|
190
190
|
|
191
|
-
|
191
|
+
[].tap do |out|
|
192
|
+
out << serial_emitters.map { |emitter| emitter.result(enriched_artifacts).value_or(nil) }
|
193
|
+
out << Parallel.map(parallel_emitters) { |emitter| emitter.result(enriched_artifacts).value_or(nil) }
|
194
|
+
end.flatten.compact
|
192
195
|
end
|
193
196
|
|
194
197
|
#
|
@@ -289,11 +292,11 @@ module Mihari
|
|
289
292
|
#
|
290
293
|
# @return [Boolean]
|
291
294
|
#
|
292
|
-
def falsepositive?(
|
293
|
-
return true if falsepositives.include?(
|
295
|
+
def falsepositive?(artifact)
|
296
|
+
return true if falsepositives.include?(artifact)
|
294
297
|
|
295
298
|
regexps = falsepositives.select { |fp| fp.is_a?(Regexp) }
|
296
|
-
regexps.any? { |fp| fp.match?(
|
299
|
+
regexps.any? { |fp| fp.match?(artifact) }
|
297
300
|
end
|
298
301
|
|
299
302
|
#
|
@@ -332,9 +335,10 @@ module Mihari
|
|
332
335
|
|
333
336
|
# @return [Array<Dry::Monads::Result::Success<Array<Mihari::Models::Artifact>>, Dry::Monads::Result::Failure>]
|
334
337
|
def analyzer_results
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
+
[].tap do |out|
|
339
|
+
out << Parallel.map(parallel_analyzers, &:result)
|
340
|
+
out << serial_analyzers.map(&:result)
|
341
|
+
end.flatten
|
338
342
|
end
|
339
343
|
|
340
344
|
#
|
@@ -365,6 +369,14 @@ module Mihari
|
|
365
369
|
end
|
366
370
|
end
|
367
371
|
|
372
|
+
def parallel_emitters
|
373
|
+
emitters.select(&:parallel?)
|
374
|
+
end
|
375
|
+
|
376
|
+
def serial_emitters
|
377
|
+
emitters.reject(&:parallel?)
|
378
|
+
end
|
379
|
+
|
368
380
|
#
|
369
381
|
# Get enricher class
|
370
382
|
#
|
data/lib/mihari/schemas/alert.rb
CHANGED
@@ -3,9 +3,9 @@
|
|
3
3
|
module Mihari
|
4
4
|
module Schemas
|
5
5
|
Alert = Dry::Schema.Params do
|
6
|
-
required(:rule_id).
|
7
|
-
required(:artifacts).
|
8
|
-
optional(:source).
|
6
|
+
required(:rule_id).filled(:string)
|
7
|
+
required(:artifacts).array { filled(:string) }
|
8
|
+
optional(:source).filled(:string)
|
9
9
|
end
|
10
10
|
|
11
11
|
#
|
@@ -20,8 +20,8 @@ module Mihari
|
|
20
20
|
key = keys.first
|
21
21
|
const_set(key.upcase, Dry::Schema.Params do
|
22
22
|
required(:analyzer).value(Types::String.enum(*keys))
|
23
|
-
required(:query).
|
24
|
-
optional(:api_key).
|
23
|
+
required(:query).filled(:string)
|
24
|
+
optional(:api_key).filled(:string)
|
25
25
|
optional(:options).hash(AnalyzerPaginationOptions)
|
26
26
|
end)
|
27
27
|
end
|
@@ -36,60 +36,60 @@ module Mihari
|
|
36
36
|
key = keys.first
|
37
37
|
const_set(key.upcase, Dry::Schema.Params do
|
38
38
|
required(:analyzer).value(Types::String.enum(*keys))
|
39
|
-
required(:query).
|
40
|
-
optional(:api_key).
|
39
|
+
required(:query).filled(:string)
|
40
|
+
optional(:api_key).filled(:string)
|
41
41
|
optional(:options).hash(AnalyzerOptions)
|
42
42
|
end)
|
43
43
|
end
|
44
44
|
|
45
45
|
DNSTwister = Dry::Schema.Params do
|
46
46
|
required(:analyzer).value(Types::String.enum(*Mihari::Analyzers::DNSTwister.keys))
|
47
|
-
required(:query).
|
47
|
+
required(:query).filled(:string)
|
48
48
|
optional(:options).hash(AnalyzerOptions)
|
49
49
|
end
|
50
50
|
|
51
51
|
Censys = Dry::Schema.Params do
|
52
52
|
required(:analyzer).value(Types::String.enum(*Mihari::Analyzers::Censys.keys))
|
53
|
-
required(:query).
|
54
|
-
optional(:id).
|
55
|
-
optional(:secret).
|
53
|
+
required(:query).filled(:string)
|
54
|
+
optional(:id).filled(:string)
|
55
|
+
optional(:secret).filled(:string)
|
56
56
|
optional(:options).hash(AnalyzerPaginationOptions)
|
57
57
|
end
|
58
58
|
|
59
59
|
CIRCL = Dry::Schema.Params do
|
60
60
|
required(:analyzer).value(Types::String.enum(*Mihari::Analyzers::CIRCL.keys))
|
61
|
-
required(:query).
|
62
|
-
optional(:username).
|
63
|
-
optional(:password).
|
61
|
+
required(:query).filled(:string)
|
62
|
+
optional(:username).filled(:string)
|
63
|
+
optional(:password).filled(:string)
|
64
64
|
optional(:options).hash(AnalyzerOptions)
|
65
65
|
end
|
66
66
|
|
67
67
|
Fofa = Dry::Schema.Params do
|
68
68
|
required(:analyzer).value(Types::String.enum(*Mihari::Analyzers::Fofa.keys))
|
69
|
-
required(:query).
|
70
|
-
optional(:api_key).
|
71
|
-
optional(:email).
|
69
|
+
required(:query).filled(:string)
|
70
|
+
optional(:api_key).filled(:string)
|
71
|
+
optional(:email).filled(:string)
|
72
72
|
optional(:options).hash(AnalyzerPaginationOptions)
|
73
73
|
end
|
74
74
|
|
75
75
|
PassiveTotal = Dry::Schema.Params do
|
76
76
|
required(:analyzer).value(Types::String.enum(*Mihari::Analyzers::PassiveTotal.keys))
|
77
|
-
required(:query).
|
78
|
-
optional(:username).
|
79
|
-
optional(:api_key).
|
77
|
+
required(:query).filled(:string)
|
78
|
+
optional(:username).filled(:string)
|
79
|
+
optional(:api_key).filled(:string)
|
80
80
|
optional(:options).hash(AnalyzerOptions)
|
81
81
|
end
|
82
82
|
|
83
83
|
ZoomEye = Dry::Schema.Params do
|
84
84
|
required(:analyzer).value(Types::String.enum(*Mihari::Analyzers::ZoomEye.keys))
|
85
|
-
required(:query).
|
85
|
+
required(:query).filled(:string)
|
86
86
|
required(:type).value(Types::String.enum("host", "web"))
|
87
87
|
optional(:options).hash(AnalyzerPaginationOptions)
|
88
88
|
end
|
89
89
|
|
90
90
|
Crtsh = Dry::Schema.Params do
|
91
91
|
required(:analyzer).value(Types::String.enum(*Mihari::Analyzers::Crtsh.keys))
|
92
|
-
required(:query).
|
92
|
+
required(:query).filled(:string)
|
93
93
|
optional(:exclude_expired).value(:bool).default(true)
|
94
94
|
optional(:match).value(Types::String.enum("=", "ILIKE", "LIKE", "single", "any", "FTS")).default(nil)
|
95
95
|
optional(:options).hash(AnalyzerOptions)
|
@@ -97,26 +97,26 @@ module Mihari
|
|
97
97
|
|
98
98
|
HunterHow = Dry::Schema.Params do
|
99
99
|
required(:analyzer).value(Types::String.enum(*Mihari::Analyzers::HunterHow.keys))
|
100
|
-
required(:query).
|
100
|
+
required(:query).filled(:string)
|
101
101
|
required(:start_time).value(:date)
|
102
102
|
required(:end_time).value(:date)
|
103
|
-
optional(:api_key).
|
103
|
+
optional(:api_key).filled(:string)
|
104
104
|
optional(:options).hash(AnalyzerPaginationOptions)
|
105
105
|
end
|
106
106
|
|
107
107
|
Feed = Dry::Schema.Params do
|
108
108
|
required(:analyzer).value(Types::String.enum(*Mihari::Analyzers::Feed.keys))
|
109
|
-
required(:query).
|
110
|
-
required(:selector).
|
109
|
+
required(:query).filled(:string)
|
110
|
+
required(:selector).filled(:string)
|
111
111
|
optional(:method).value(Types::HTTPRequestMethods).default("GET")
|
112
|
-
optional(:headers).
|
113
|
-
optional(:params).
|
114
|
-
optional(:form).
|
115
|
-
optional(:json).
|
112
|
+
optional(:headers).filled(:hash)
|
113
|
+
optional(:params).filled(:hash)
|
114
|
+
optional(:form).filled(:hash)
|
115
|
+
optional(:json).filled(:hash)
|
116
116
|
optional(:options).hash(AnalyzerOptions)
|
117
117
|
end
|
118
118
|
end
|
119
119
|
|
120
|
-
Analyzer = Schemas::Analyzers.
|
120
|
+
Analyzer = Schemas::Analyzers.compose_by_or
|
121
121
|
end
|
122
122
|
end
|
@@ -10,40 +10,40 @@ module Mihari
|
|
10
10
|
|
11
11
|
Database = Dry::Schema.Params do
|
12
12
|
required(:emitter).value(Types::String.enum(*Mihari::Emitters::Database.keys))
|
13
|
-
optional(:options).hash(
|
13
|
+
optional(:options).hash(EmitterOptions)
|
14
14
|
end
|
15
15
|
|
16
16
|
MISP = Dry::Schema.Params do
|
17
17
|
required(:emitter).value(Types::String.enum(*Mihari::Emitters::MISP.keys))
|
18
|
-
optional(:url).
|
19
|
-
optional(:api_key).
|
20
|
-
optional(:options).hash(
|
18
|
+
optional(:url).filled(:string)
|
19
|
+
optional(:api_key).filled(:string)
|
20
|
+
optional(:options).hash(EmitterOptions)
|
21
21
|
end
|
22
22
|
|
23
23
|
TheHive = Dry::Schema.Params do
|
24
24
|
required(:emitter).value(Types::String.enum(*Mihari::Emitters::TheHive.keys))
|
25
|
-
optional(:url).
|
26
|
-
optional(:api_key).
|
27
|
-
optional(:options).hash(
|
25
|
+
optional(:url).filled(:string)
|
26
|
+
optional(:api_key).filled(:string)
|
27
|
+
optional(:options).hash(EmitterOptions)
|
28
28
|
end
|
29
29
|
|
30
30
|
Slack = Dry::Schema.Params do
|
31
31
|
required(:emitter).value(Types::String.enum(*Mihari::Emitters::Slack.keys))
|
32
|
-
optional(:webhook_url).
|
33
|
-
optional(:channel).
|
34
|
-
optional(:options).hash(
|
32
|
+
optional(:webhook_url).filled(:string)
|
33
|
+
optional(:channel).filled(:string)
|
34
|
+
optional(:options).hash(EmitterOptions)
|
35
35
|
end
|
36
36
|
|
37
37
|
Webhook = Dry::Schema.Params do
|
38
38
|
required(:emitter).value(Types::String.enum(*Mihari::Emitters::Webhook.keys))
|
39
|
-
required(:url).
|
39
|
+
required(:url).filled(:string)
|
40
40
|
optional(:method).value(Types::HTTPRequestMethods).default("POST")
|
41
|
-
optional(:headers).
|
42
|
-
optional(:template).
|
43
|
-
optional(:options).hash(
|
41
|
+
optional(:headers).filled(:hash)
|
42
|
+
optional(:template).filled(:string)
|
43
|
+
optional(:options).hash(EmitterOptions)
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
-
Emitter = Schemas::Emitters.
|
47
|
+
Emitter = Schemas::Emitters.compose_by_or
|
48
48
|
end
|
49
49
|
end
|
@@ -8,9 +8,9 @@ module Dry
|
|
8
8
|
# (see https://github.com/dry-rb/dry-schema/issues/70)
|
9
9
|
#
|
10
10
|
class DSL
|
11
|
-
def default(
|
11
|
+
def default(artifact)
|
12
12
|
schema_dsl.before(:rule_applier) do |result|
|
13
|
-
result.update(name =>
|
13
|
+
result.update(name => artifact) if result.output && !result[name]
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
@@ -3,27 +3,29 @@
|
|
3
3
|
module Mihari
|
4
4
|
module Schemas
|
5
5
|
Options = Dry::Schema.Params do
|
6
|
-
optional(:retry_times).value(:integer)
|
7
|
-
optional(:retry_interval).value(:integer)
|
8
|
-
optional(:retry_exponential_backoff).value(:bool)
|
6
|
+
optional(:retry_times).value(:integer)
|
7
|
+
optional(:retry_interval).value(:integer)
|
8
|
+
optional(:retry_exponential_backoff).value(:bool)
|
9
9
|
optional(:timeout).value(:integer)
|
10
10
|
end
|
11
11
|
|
12
|
-
IgnoreErrorOptions = Dry::Schema.Params do
|
13
|
-
optional(:ignore_error).value(:bool).default(Mihari.config.ignore_error)
|
14
|
-
end
|
15
|
-
|
16
12
|
ParallelOptions = Dry::Schema.Params do
|
17
|
-
optional(:parallel).value(:bool)
|
13
|
+
optional(:parallel).value(:bool)
|
18
14
|
end
|
19
15
|
|
20
|
-
|
16
|
+
IgnoreErrorOptions = Dry::Schema.Params do
|
17
|
+
optional(:ignore_error).value(:bool)
|
18
|
+
end
|
21
19
|
|
22
20
|
PaginationOptions = Dry::Schema.Params do
|
23
|
-
optional(:pagination_interval).value(:integer)
|
24
|
-
optional(:pagination_limit).value(:integer)
|
21
|
+
optional(:pagination_interval).value(:integer)
|
22
|
+
optional(:pagination_limit).value(:integer)
|
25
23
|
end
|
26
24
|
|
27
|
-
|
25
|
+
AnalyzerOptions = Options & IgnoreErrorOptions & ParallelOptions
|
26
|
+
|
27
|
+
AnalyzerPaginationOptions = AnalyzerOptions & PaginationOptions
|
28
|
+
|
29
|
+
EmitterOptions = Options & ParallelOptions
|
28
30
|
end
|
29
31
|
end
|
data/lib/mihari/schemas/rule.rb
CHANGED
@@ -7,27 +7,27 @@ require "mihari/schemas/enricher"
|
|
7
7
|
module Mihari
|
8
8
|
module Schemas
|
9
9
|
Rule = Dry::Schema.Params do
|
10
|
-
required(:id).
|
11
|
-
required(:title).
|
12
|
-
required(:description).
|
10
|
+
required(:id).filled(:string)
|
11
|
+
required(:title).filled(:string)
|
12
|
+
required(:description).filled(:string)
|
13
13
|
|
14
|
-
optional(:
|
14
|
+
optional(:author).filled(:string)
|
15
|
+
optional(:status).filled(:string)
|
15
16
|
|
16
|
-
optional(:
|
17
|
-
optional(:references).
|
18
|
-
optional(:related).
|
19
|
-
optional(:status).value(:string)
|
17
|
+
optional(:tags).array { filled(:string) }.default([])
|
18
|
+
optional(:references).array { filled(:string) }
|
19
|
+
optional(:related).array { filled(:string) }
|
20
20
|
|
21
21
|
optional(:created_on).value(:date)
|
22
22
|
optional(:updated_on).value(:date)
|
23
23
|
|
24
|
-
required(:queries).
|
24
|
+
required(:queries).array { Analyzer }
|
25
|
+
optional(:emitters).array { Emitter }.default(DEFAULT_EMITTERS)
|
26
|
+
optional(:enrichers).array { Enricher }.default(DEFAULT_ENRICHERS)
|
25
27
|
|
26
|
-
optional(:
|
27
|
-
optional(:enrichers).value(:array).each { Enricher }.default(DEFAULT_ENRICHERS) # rubocop:disable Lint/Void
|
28
|
+
optional(:data_types).filled(array[Types::DataTypes]).default(Mihari::Types::DataTypes.values)
|
28
29
|
|
29
|
-
optional(:
|
30
|
-
optional(:falsepositives).value(array[:string]).default([])
|
30
|
+
optional(:falsepositives).array { filled(:string) }.default([])
|
31
31
|
|
32
32
|
optional(:artifact_ttl).value(:integer)
|
33
33
|
end
|
@@ -42,7 +42,7 @@ module Mihari
|
|
42
42
|
|
43
43
|
rule(:falsepositives) do
|
44
44
|
value.each do |v|
|
45
|
-
key.failure("#{v} is not a valid format
|
45
|
+
key.failure("#{v} is not a valid format") unless valid_falsepositive?(v)
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
@@ -20,158 +20,5 @@ module Mihari
|
|
20
20
|
Rule.from_yaml ERB.new(File.read(path_or_id)).result
|
21
21
|
end
|
22
22
|
end
|
23
|
-
|
24
|
-
#
|
25
|
-
# Autonomous system builder
|
26
|
-
#
|
27
|
-
class AutonomousSystemBuilder < Service
|
28
|
-
#
|
29
|
-
# @param [String] ip
|
30
|
-
# @param [Mihari::Enrichers::MMDB] enricher
|
31
|
-
#
|
32
|
-
# @return [Mihari::Models::AutonomousSystem, nil]
|
33
|
-
#
|
34
|
-
def call(ip, enricher: Enrichers::MMDB.new)
|
35
|
-
enricher.result(ip).fmap do |res|
|
36
|
-
Models::AutonomousSystem.new(number: res.asn) if res.asn
|
37
|
-
end.value_or nil
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
#
|
42
|
-
# CPE builder
|
43
|
-
#
|
44
|
-
class CPEBuilder < Service
|
45
|
-
#
|
46
|
-
# Build CPEs
|
47
|
-
#
|
48
|
-
# @param [String] ip
|
49
|
-
# @param [Mihari::Enrichers::Shodan] enricher
|
50
|
-
#
|
51
|
-
# @return [Array<Mihari::Models::CPE>]
|
52
|
-
#
|
53
|
-
def call(ip, enricher: Enrichers::Shodan.new)
|
54
|
-
enricher.result(ip).fmap do |res|
|
55
|
-
(res&.cpes || []).map { |cpe| Models::CPE.new(name: cpe) }
|
56
|
-
end.value_or []
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
#
|
61
|
-
# DNS record builder
|
62
|
-
#
|
63
|
-
class DnsRecordBuilder < Service
|
64
|
-
#
|
65
|
-
# Build DNS records
|
66
|
-
#
|
67
|
-
# @param [String] domain
|
68
|
-
# @param [Mihari::Enrichers::Shodan] enricher
|
69
|
-
#
|
70
|
-
# @return [Array<Mihari::Models::DnsRecord>]
|
71
|
-
#
|
72
|
-
def call(domain, enricher: Enrichers::GooglePublicDNS.new)
|
73
|
-
enricher.result(domain).fmap do |res|
|
74
|
-
res.answers.map { |answer| Models::DnsRecord.new(resource: answer.resource_type, value: answer.data) }
|
75
|
-
end.value_or []
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
#
|
80
|
-
# Geolocation builder
|
81
|
-
#
|
82
|
-
class GeolocationBuilder < Service
|
83
|
-
#
|
84
|
-
# Build Geolocation
|
85
|
-
#
|
86
|
-
# @param [String] ip
|
87
|
-
# @param [Mihari::Enrichers::MMDB] enricher
|
88
|
-
#
|
89
|
-
# @return [Mihari::Models::Geolocation, nil]
|
90
|
-
#
|
91
|
-
def call(ip, enricher: Enrichers::MMDB.new)
|
92
|
-
enricher.result(ip).fmap do |res|
|
93
|
-
if res.country_code
|
94
|
-
Models::Geolocation.new(
|
95
|
-
country: NormalizeCountry(res.country_code, to: :short),
|
96
|
-
country_code: res.country_code
|
97
|
-
)
|
98
|
-
end
|
99
|
-
end.value_or nil
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
#
|
104
|
-
# Port builder
|
105
|
-
#
|
106
|
-
class PortBuilder < Service
|
107
|
-
#
|
108
|
-
# Build ports
|
109
|
-
#
|
110
|
-
# @param [String] ip
|
111
|
-
# @param [Mihari::Enrichers::Shodan] enricher
|
112
|
-
#
|
113
|
-
# @return [Array<Mihari::Models::Port>]
|
114
|
-
#
|
115
|
-
def call(ip, enricher: Enrichers::Shodan.new)
|
116
|
-
enricher.result(ip).fmap do |res|
|
117
|
-
(res&.ports || []).map { |port| Models::Port.new(number: port) }
|
118
|
-
end.value_or []
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
#
|
123
|
-
# Reverse DNS name builder
|
124
|
-
#
|
125
|
-
class ReverseDnsNameBuilder < Service
|
126
|
-
#
|
127
|
-
# Build reverse DNS names
|
128
|
-
#
|
129
|
-
# @param [String] ip
|
130
|
-
# @param [Mihari::Enrichers::Shodan] enricher
|
131
|
-
#
|
132
|
-
# @return [Array<Mihari::Models::ReverseDnsName>]
|
133
|
-
#
|
134
|
-
def call(ip, enricher: Enrichers::Shodan.new)
|
135
|
-
enricher.result(ip).fmap do |res|
|
136
|
-
(res&.hostnames || []).map { |name| Models::ReverseDnsName.new(name: name) }
|
137
|
-
end.value_or []
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
#
|
142
|
-
# Vulnerability builder
|
143
|
-
#
|
144
|
-
class VulnerabilityBuilder < Service
|
145
|
-
#
|
146
|
-
# Build vulnerabilities
|
147
|
-
#
|
148
|
-
# @param [String] ip
|
149
|
-
# @param [Mihari::Enrichers::Shodan] enricher
|
150
|
-
#
|
151
|
-
# @return [Array<Mihari::Models::Vulnerability>]
|
152
|
-
#
|
153
|
-
def call(ip, enricher: Enrichers::Shodan.new)
|
154
|
-
enricher.result(ip).fmap do |res|
|
155
|
-
(res&.vulns || []).map { |name| Models::Vulnerability.new(name: name) }
|
156
|
-
end.value_or []
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
#
|
161
|
-
# Whois record builder
|
162
|
-
#
|
163
|
-
class WhoisRecordBuilder < Service
|
164
|
-
#
|
165
|
-
# Build whois record
|
166
|
-
#
|
167
|
-
# @param [String] domain
|
168
|
-
# @param [Mihari::Enrichers::Whois] enricher
|
169
|
-
#
|
170
|
-
# @return [Mihari::Models::WhoisRecord, nil]
|
171
|
-
#
|
172
|
-
def call(domain, enricher: Enrichers::Whois.new)
|
173
|
-
enricher.result(domain).value_or nil
|
174
|
-
end
|
175
|
-
end
|
176
23
|
end
|
177
24
|
end
|
data/lib/mihari/version.rb
CHANGED