mihari 4.11.0 → 5.0.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/.github/workflows/test.yml +1 -1
- data/README.md +13 -3
- data/Steepfile +0 -1
- data/build_frontend.sh +0 -3
- data/docker/Dockerfile +11 -12
- data/images/Tines-Full_Logo-Tines_Black.png +0 -0
- data/lib/mihari/analyzers/base.rb +12 -28
- data/lib/mihari/analyzers/rule.rb +23 -36
- data/lib/mihari/cli/main.rb +6 -11
- data/lib/mihari/commands/initializer.rb +47 -0
- data/lib/mihari/commands/{search.rb → searcher.rb} +9 -20
- data/lib/mihari/commands/validator.rb +2 -2
- data/lib/mihari/commands/web.rb +4 -2
- data/lib/mihari/constants.rb +3 -3
- data/lib/mihari/database.rb +52 -87
- data/lib/mihari/emitters/database.rb +16 -7
- data/lib/mihari/emitters/misp.rb +13 -5
- data/lib/mihari/emitters/slack.rb +15 -8
- data/lib/mihari/emitters/the_hive.rb +42 -21
- data/lib/mihari/emitters/webhook.rb +99 -31
- data/lib/mihari/entities/alert.rb +7 -5
- data/lib/mihari/entities/artifact.rb +20 -8
- data/lib/mihari/entities/config.rb +2 -6
- data/lib/mihari/entities/rule.rb +8 -0
- data/lib/mihari/http.rb +13 -13
- data/lib/mihari/mixins/{disallowed_data_value.rb → falsepositive.rb} +8 -8
- data/lib/mihari/models/alert.rb +2 -15
- data/lib/mihari/models/artifact.rb +28 -17
- data/lib/mihari/models/rule.rb +7 -13
- data/lib/mihari/schemas/emitter.rb +6 -8
- data/lib/mihari/schemas/rule.rb +11 -13
- data/lib/mihari/structs/config.rb +41 -0
- data/lib/mihari/structs/filters.rb +2 -2
- data/lib/mihari/structs/rule.rb +96 -83
- data/lib/mihari/templates/rule.yml.erb +5 -23
- data/lib/mihari/types.rb +1 -1
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/api.rb +0 -2
- data/lib/mihari/web/app.rb +10 -4
- data/lib/mihari/web/endpoints/alerts.rb +11 -3
- data/lib/mihari/web/endpoints/configs.rb +1 -6
- data/lib/mihari/web/endpoints/rules.rb +27 -15
- data/lib/mihari/web/public/assets/{fa-brands-400.c7ae37d3.ttf → fa-brands-400-2ef6fdde.ttf} +0 -0
- data/lib/mihari/web/public/assets/fa-brands-400-f4617423.woff2 +0 -0
- data/lib/mihari/web/public/assets/fa-regular-400-12dea17b.ttf +0 -0
- data/lib/mihari/web/public/assets/fa-regular-400-7ba24c41.woff2 +0 -0
- data/lib/mihari/web/public/assets/fa-solid-900-67a880b4.ttf +0 -0
- data/lib/mihari/web/public/assets/fa-solid-900-e2c5cf54.woff2 +0 -0
- data/lib/mihari/web/public/assets/fa-v4compatibility-7c377405.woff2 +0 -0
- data/lib/mihari/web/public/assets/fa-v4compatibility-8d9500e8.ttf +0 -0
- data/lib/mihari/web/public/assets/{index.e1e67d84.css → index-625e95fe.css} +3 -3
- data/lib/mihari/web/public/assets/index-63900d73.js +50 -0
- data/lib/mihari/web/public/index.html +3 -3
- data/lib/mihari/web/public/redoc-static.html +26 -27
- data/lib/mihari.rb +11 -21
- data/mihari.gemspec +14 -14
- metadata +46 -131
- data/lib/mihari/cli/init.rb +0 -11
- data/lib/mihari/cli/validator.rb +0 -11
- data/lib/mihari/commands/init.rb +0 -51
- data/lib/mihari/emitters/http.rb +0 -127
- data/lib/mihari/entities/source.rb +0 -9
- data/lib/mihari/status.rb +0 -55
- data/lib/mihari/web/endpoints/sources.rb +0 -19
- data/lib/mihari/web/public/assets/fa-brands-400.3fe890d0.woff2 +0 -0
- data/lib/mihari/web/public/assets/fa-regular-400.fdc1f753.ttf +0 -0
- data/lib/mihari/web/public/assets/fa-regular-400.fe69d948.woff2 +0 -0
- data/lib/mihari/web/public/assets/fa-solid-900.6d53c706.ttf +0 -0
- data/lib/mihari/web/public/assets/fa-solid-900.d27bc752.woff2 +0 -0
- data/lib/mihari/web/public/assets/fa-v4compatibility.4d73f280.ttf +0 -0
- data/lib/mihari/web/public/assets/fa-v4compatibility.7d1c2ce5.woff2 +0 -0
- data/lib/mihari/web/public/assets/index.d3a61a69.js +0 -68
- data/sig/lib/mihari/analyzers/base.rbs +0 -90
- data/sig/lib/mihari/analyzers/binaryedge.rbs +0 -26
- data/sig/lib/mihari/analyzers/censys.rbs +0 -41
- data/sig/lib/mihari/analyzers/circl.rbs +0 -31
- data/sig/lib/mihari/analyzers/crtsh.rbs +0 -17
- data/sig/lib/mihari/analyzers/dnpedia.rbs +0 -15
- data/sig/lib/mihari/analyzers/dnstwister.rbs +0 -25
- data/sig/lib/mihari/analyzers/feed.rbs +0 -20
- data/sig/lib/mihari/analyzers/onyphe.rbs +0 -34
- data/sig/lib/mihari/analyzers/otx.rbs +0 -33
- data/sig/lib/mihari/analyzers/passivetotal.rbs +0 -35
- data/sig/lib/mihari/analyzers/pulsedive.rbs +0 -27
- data/sig/lib/mihari/analyzers/rule.rbs +0 -68
- data/sig/lib/mihari/analyzers/securitytrails.rbs +0 -33
- data/sig/lib/mihari/analyzers/shodan.rbs +0 -36
- data/sig/lib/mihari/analyzers/urlscan.rbs +0 -31
- data/sig/lib/mihari/analyzers/virustotal.rbs +0 -31
- data/sig/lib/mihari/analyzers/virustotal_intelligence.rbs +0 -33
- data/sig/lib/mihari/analyzers/zoomeye.rbs +0 -35
- data/sig/lib/mihari/cli/base.rbs +0 -9
- data/sig/lib/mihari/cli/init.rbs +0 -7
- data/sig/lib/mihari/cli/main.rbs +0 -9
- data/sig/lib/mihari/cli/validator.rbs +0 -7
- data/sig/lib/mihari/commands/init.rbs +0 -9
- data/sig/lib/mihari/commands/json.rbs +0 -7
- data/sig/lib/mihari/commands/search.rbs +0 -35
- data/sig/lib/mihari/commands/validator.rbs +0 -9
- data/sig/lib/mihari/commands/web.rbs +0 -7
- data/sig/lib/mihari/constants.rbs +0 -5
- data/sig/lib/mihari/database.rbs +0 -25
- data/sig/lib/mihari/emitters/base.rbs +0 -18
- data/sig/lib/mihari/emitters/database.rbs +0 -9
- data/sig/lib/mihari/emitters/http.rbs +0 -35
- data/sig/lib/mihari/emitters/misp.rbs +0 -34
- data/sig/lib/mihari/emitters/slack.rbs +0 -73
- data/sig/lib/mihari/emitters/stdout.rbs +0 -9
- data/sig/lib/mihari/emitters/the_hive.rbs +0 -32
- data/sig/lib/mihari/emitters/webhook.rbs +0 -20
- data/sig/lib/mihari/enrichers/base.rbs +0 -12
- data/sig/lib/mihari/enrichers/google_public_dns.rbs +0 -18
- data/sig/lib/mihari/enrichers/ipinfo.rbs +0 -16
- data/sig/lib/mihari/errors.rbs +0 -10
- data/sig/lib/mihari/feed/parser.rbs +0 -11
- data/sig/lib/mihari/feed/reader.rbs +0 -56
- data/sig/lib/mihari/http.rbs +0 -64
- data/sig/lib/mihari/mixins/autonomous_system.rbs +0 -14
- data/sig/lib/mihari/mixins/configurable.rbs +0 -30
- data/sig/lib/mihari/mixins/configuration.rbs +0 -45
- data/sig/lib/mihari/mixins/disallowed_data_value.rbs +0 -23
- data/sig/lib/mihari/mixins/error_notification.rbs +0 -12
- data/sig/lib/mihari/mixins/hash.rbs +0 -14
- data/sig/lib/mihari/mixins/refang.rbs +0 -14
- data/sig/lib/mihari/mixins/retriable.rbs +0 -15
- data/sig/lib/mihari/models/alert.rbs +0 -18
- data/sig/lib/mihari/models/artifact.rbs +0 -69
- data/sig/lib/mihari/models/autonomous_system.rbs +0 -14
- data/sig/lib/mihari/models/cpe.rbs +0 -7
- data/sig/lib/mihari/models/dns.rbs +0 -19
- data/sig/lib/mihari/models/geolocation.rbs +0 -15
- data/sig/lib/mihari/models/port.rbs +0 -7
- data/sig/lib/mihari/models/reverse_dns.rbs +0 -14
- data/sig/lib/mihari/models/rule.rbs +0 -17
- data/sig/lib/mihari/models/tag.rbs +0 -5
- data/sig/lib/mihari/models/tagging.rbs +0 -4
- data/sig/lib/mihari/models/whois.rbs +0 -66
- data/sig/lib/mihari/status.rbs +0 -25
- data/sig/lib/mihari/structs/censys.rbs +0 -58
- data/sig/lib/mihari/structs/filters.rbs +0 -40
- data/sig/lib/mihari/structs/google_public_dns.rbs +0 -21
- data/sig/lib/mihari/structs/greynoise.rbs +0 -30
- data/sig/lib/mihari/structs/ipinfo.rbs +0 -17
- data/sig/lib/mihari/structs/onyphe.rbs +0 -25
- data/sig/lib/mihari/structs/rule.rbs +0 -57
- data/sig/lib/mihari/structs/shodan.rbs +0 -30
- data/sig/lib/mihari/structs/urlscan.rbs +0 -28
- data/sig/lib/mihari/structs/virustotal_intelligence.rbs +0 -33
- data/sig/lib/mihari/type_checker.rbs +0 -48
- data/sig/lib/mihari/types.rbs +0 -23
- data/sig/lib/mihari/version.rbs +0 -3
- data/sig/lib/mihari/web/app.rbs +0 -5
- data/sig/lib/mihari.rbs +0 -54
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 41784cbe3811a8f5f2a6a4de663ceaa03e2635c329c455b523e5fc55fda0c9e7
|
|
4
|
+
data.tar.gz: '052987fcb8805a8f71a0716d5e0970b0c4c7451059dded4b5149679ccda6c999'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f16447e83adb4630baf9587eecea0dbb6f580de23894d70fa2c1729483faa6dc1c86fe5de2bd782100f8cba89e2a50e7ce2f0ab675de822fe5fb767fb2c6f45f
|
|
7
|
+
data.tar.gz: af70fcc510ed4fd6a434481a8372c56d20a54b605ee0b49425a1315531d2261db6df9588f1961ba34796728ff40f0ce188ad58e6c542f4c3644a8c8251b37eb5
|
data/.github/workflows/test.yml
CHANGED
data/README.md
CHANGED
|
@@ -5,9 +5,19 @@
|
|
|
5
5
|
[](https://coveralls.io/github/ninoseki/mihari?branch=master)
|
|
6
6
|
[](https://www.codefactor.io/repository/github/ninoseki/mihari)
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
<p align="center">
|
|
11
|
+
<img src="https://github.com/ninoseki/mihari/raw/master/images/logo.png"/>
|
|
12
|
+
<br/>
|
|
13
|
+
<a href="https://tines.io?utm_source=github&utm_medium=sponsorship&utm_campaign=ninoseki">
|
|
14
|
+
<img src="https://github.com/ninoseki/mihari/raw/master/images/Tines-Full_Logo-Tines_Black.png"/>
|
|
15
|
+
</a>
|
|
16
|
+
<br/>
|
|
17
|
+
Mihari is proudly supported by <a href="https://tines.io?utm_source=github&utm_medium=sponsorship&utm_campaign=ninoseki">Tines</a>
|
|
18
|
+
</p>
|
|
19
|
+
|
|
20
|
+
---
|
|
11
21
|
|
|
12
22
|
Mihari is a tool for OSINT based threat hunting.
|
|
13
23
|
|
data/Steepfile
CHANGED
data/build_frontend.sh
CHANGED
|
@@ -15,8 +15,5 @@ trash -r ${CURRENT_DIR}/lib/mihari/web/public/
|
|
|
15
15
|
mkdir -p ${CURRENT_DIR}/lib/mihari/web/public/
|
|
16
16
|
cp -r dist/* ${CURRENT_DIR}/lib/mihari/web/public
|
|
17
17
|
|
|
18
|
-
# replace favicon path
|
|
19
|
-
sed -i "" 's/href="\/favicon.ico"/href="\/static\/favicon.ico"/' ${CURRENT_DIR}/lib/mihari/web/public/index.html
|
|
20
|
-
|
|
21
18
|
# remove tmp dir
|
|
22
19
|
rm -rf ${CURRENT_DIR}/tmp/mihari-frontend
|
data/docker/Dockerfile
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
FROM ruby:3.
|
|
2
|
-
|
|
3
|
-
RUN apk --no-cache add git build-base ruby-dev sqlite-dev postgresql-dev mysql-client mysql-dev \
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
&& apk del --purge git build-base ruby-dev
|
|
1
|
+
FROM ruby:3.1.3-alpine3.17
|
|
2
|
+
|
|
3
|
+
RUN apk --no-cache add git build-base ruby-dev sqlite-dev postgresql-dev mysql-client mysql-dev && \
|
|
4
|
+
gem install pg mysql2
|
|
5
|
+
|
|
6
|
+
ARG MIHARI_VERSION=4.11.0
|
|
7
|
+
|
|
8
|
+
RUN gem install mihari -v ${MIHARI_VERSION}
|
|
9
|
+
|
|
10
|
+
RUN apk del --purge git build-base ruby-dev
|
|
12
11
|
|
|
13
12
|
ENTRYPOINT ["mihari"]
|
|
14
13
|
|
|
15
|
-
CMD ["--help"]
|
|
14
|
+
CMD ["--help"]
|
|
Binary file
|
|
@@ -5,18 +5,20 @@ module Mihari
|
|
|
5
5
|
class Base
|
|
6
6
|
extend Dry::Initializer
|
|
7
7
|
|
|
8
|
+
option :rule, default: proc {}
|
|
9
|
+
|
|
8
10
|
include Mixins::AutonomousSystem
|
|
9
11
|
include Mixins::Configurable
|
|
10
12
|
include Mixins::Database
|
|
11
13
|
include Mixins::Retriable
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
# @return [Mihari::Structs::Rule, nil]
|
|
16
|
+
attr_reader :rule
|
|
14
17
|
|
|
15
18
|
def initialize(*args, **kwargs)
|
|
16
|
-
super
|
|
19
|
+
super(*args, **kwargs)
|
|
17
20
|
|
|
18
|
-
@
|
|
19
|
-
@ignore_threshold = 0
|
|
21
|
+
@base_time = Time.now.utc
|
|
20
22
|
end
|
|
21
23
|
|
|
22
24
|
# @return [Array<String>, Array<Mihari::Artifact>]
|
|
@@ -24,26 +26,11 @@ module Mihari
|
|
|
24
26
|
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
|
25
27
|
end
|
|
26
28
|
|
|
27
|
-
# @return [String]
|
|
28
|
-
def title
|
|
29
|
-
self.class.to_s.split("::").last.to_s
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# @return [String]
|
|
33
|
-
def description
|
|
34
|
-
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
|
35
|
-
end
|
|
36
|
-
|
|
37
29
|
# @return [String]
|
|
38
30
|
def source
|
|
39
31
|
self.class.to_s.split("::").last.to_s
|
|
40
32
|
end
|
|
41
33
|
|
|
42
|
-
# @return [Array<String>]
|
|
43
|
-
def tags
|
|
44
|
-
[]
|
|
45
|
-
end
|
|
46
|
-
|
|
47
34
|
#
|
|
48
35
|
# Set artifacts & run emitters in parallel
|
|
49
36
|
#
|
|
@@ -77,13 +64,7 @@ module Mihari
|
|
|
77
64
|
def run_emitter(emitter)
|
|
78
65
|
return if enriched_artifacts.empty?
|
|
79
66
|
|
|
80
|
-
alert_or_something = emitter.run(
|
|
81
|
-
title: title,
|
|
82
|
-
description: description,
|
|
83
|
-
artifacts: enriched_artifacts,
|
|
84
|
-
source: source,
|
|
85
|
-
tags: tags
|
|
86
|
-
)
|
|
67
|
+
alert_or_something = emitter.run(artifacts: enriched_artifacts, rule: rule)
|
|
87
68
|
|
|
88
69
|
Mihari.logger.info "Emission by #{emitter.class} is succedded"
|
|
89
70
|
|
|
@@ -112,7 +93,10 @@ module Mihari
|
|
|
112
93
|
# No need to set data_type manually
|
|
113
94
|
# It is set automatically in #initialize
|
|
114
95
|
artifact.is_a?(Artifact) ? artifact : Artifact.new(data: artifact, source: source)
|
|
115
|
-
end.select(&:valid?).uniq(&:data)
|
|
96
|
+
end.select(&:valid?).uniq(&:data).map do |artifact|
|
|
97
|
+
artifact.rule_id = rule&.id
|
|
98
|
+
artifact
|
|
99
|
+
end
|
|
116
100
|
end
|
|
117
101
|
|
|
118
102
|
private
|
|
@@ -124,7 +108,7 @@ module Mihari
|
|
|
124
108
|
#
|
|
125
109
|
def unique_artifacts
|
|
126
110
|
@unique_artifacts ||= normalized_artifacts.select do |artifact|
|
|
127
|
-
artifact.unique?(
|
|
111
|
+
artifact.unique?(base_time: @base_time, artifact_lifetime: rule&.artifact_lifetime)
|
|
128
112
|
end
|
|
129
113
|
end
|
|
130
114
|
|
|
@@ -29,38 +29,21 @@ module Mihari
|
|
|
29
29
|
|
|
30
30
|
EMITTER_TO_CLASS = {
|
|
31
31
|
"database" => Emitters::Database,
|
|
32
|
-
"http" => Emitters::HTTP,
|
|
33
32
|
"misp" => Emitters::MISP,
|
|
34
33
|
"slack" => Emitters::Slack,
|
|
35
34
|
"the_hive" => Emitters::TheHive,
|
|
36
35
|
"webhook" => Emitters::Webhook
|
|
37
36
|
}.freeze
|
|
38
37
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
option :title
|
|
43
|
-
option :description
|
|
44
|
-
option :queries
|
|
45
|
-
|
|
46
|
-
option :id, default: proc { "" }
|
|
47
|
-
option :tags, default: proc { [] }
|
|
48
|
-
option :allowed_data_types, default: proc { ALLOWED_DATA_TYPES }
|
|
49
|
-
option :disallowed_data_values, default: proc { [] }
|
|
38
|
+
# @return [Mihari::Structs::Rule]
|
|
39
|
+
attr_reader :rule
|
|
50
40
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
attr_reader :source
|
|
41
|
+
class Rule < Base
|
|
42
|
+
include Mixins::FalsePositive
|
|
55
43
|
|
|
56
44
|
def initialize(**kwargs)
|
|
57
45
|
super(**kwargs)
|
|
58
46
|
|
|
59
|
-
@source = id
|
|
60
|
-
|
|
61
|
-
@emitters = emitters || DEFAULT_EMITTERS
|
|
62
|
-
@enrichers = enrichers || DEFAULT_ENRICHERS
|
|
63
|
-
|
|
64
47
|
validate_analyzer_configurations
|
|
65
48
|
end
|
|
66
49
|
|
|
@@ -72,7 +55,7 @@ module Mihari
|
|
|
72
55
|
def artifacts
|
|
73
56
|
artifacts = []
|
|
74
57
|
|
|
75
|
-
queries.each do |original_params|
|
|
58
|
+
rule.queries.each do |original_params|
|
|
76
59
|
parmas = original_params.deep_dup
|
|
77
60
|
|
|
78
61
|
analyzer_name = parmas[:analyzer]
|
|
@@ -83,8 +66,12 @@ module Mihari
|
|
|
83
66
|
# set interval in the top level
|
|
84
67
|
options = parmas[:options] || {}
|
|
85
68
|
interval = options[:interval]
|
|
69
|
+
|
|
86
70
|
parmas[:interval] = interval if interval
|
|
87
71
|
|
|
72
|
+
# set rule
|
|
73
|
+
parmas[:rule] = rule
|
|
74
|
+
|
|
88
75
|
analyzer = klass.new(query, **parmas)
|
|
89
76
|
|
|
90
77
|
# Use #normalized_artifacts method to get atrifacts as Array<Mihari::Artifact>
|
|
@@ -106,9 +93,9 @@ module Mihari
|
|
|
106
93
|
#
|
|
107
94
|
def normalized_artifacts
|
|
108
95
|
@normalized_artifacts ||= artifacts.uniq(&:data).select(&:valid?).select do |artifact|
|
|
109
|
-
|
|
96
|
+
rule.data_types.include? artifact.data_type
|
|
110
97
|
end.reject do |artifact|
|
|
111
|
-
|
|
98
|
+
falsepositive? artifact.data
|
|
112
99
|
end
|
|
113
100
|
end
|
|
114
101
|
|
|
@@ -119,7 +106,7 @@ module Mihari
|
|
|
119
106
|
#
|
|
120
107
|
def enriched_artifacts
|
|
121
108
|
@enriched_artifacts ||= Parallel.map(unique_artifacts) do |artifact|
|
|
122
|
-
enrichers.each do |enricher|
|
|
109
|
+
rule.enrichers.each do |enricher|
|
|
123
110
|
artifact.enrich_by_enricher(enricher[:enricher])
|
|
124
111
|
end
|
|
125
112
|
|
|
@@ -132,22 +119,22 @@ module Mihari
|
|
|
132
119
|
#
|
|
133
120
|
# @return [Array<Regexp, String>]
|
|
134
121
|
#
|
|
135
|
-
def
|
|
136
|
-
@
|
|
122
|
+
def normalized_falsepositives
|
|
123
|
+
@normalized_falsepositives ||= rule.falsepositives.map { |v| normalize_falsepositive v }
|
|
137
124
|
end
|
|
138
125
|
|
|
139
126
|
#
|
|
140
|
-
# Check whether a value is a
|
|
127
|
+
# Check whether a value is a falsepositive value or not
|
|
141
128
|
#
|
|
142
129
|
# @return [Boolean]
|
|
143
130
|
#
|
|
144
|
-
def
|
|
145
|
-
return true if
|
|
131
|
+
def falsepositive?(value)
|
|
132
|
+
return true if normalized_falsepositives.include?(value)
|
|
146
133
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
end.any? do |
|
|
150
|
-
|
|
134
|
+
normalized_falsepositives.select do |falsepositive|
|
|
135
|
+
falsepositive.is_a?(Regexp)
|
|
136
|
+
end.any? do |falseposistive|
|
|
137
|
+
falseposistive.match?(value)
|
|
151
138
|
end
|
|
152
139
|
end
|
|
153
140
|
|
|
@@ -168,7 +155,7 @@ module Mihari
|
|
|
168
155
|
end
|
|
169
156
|
|
|
170
157
|
def valid_emitters
|
|
171
|
-
@valid_emitters ||= emitters.filter_map do |original_params|
|
|
158
|
+
@valid_emitters ||= rule.emitters.filter_map do |original_params|
|
|
172
159
|
params = original_params.deep_dup
|
|
173
160
|
|
|
174
161
|
name = params[:emitter]
|
|
@@ -199,7 +186,7 @@ module Mihari
|
|
|
199
186
|
# Validate configuration of analyzers
|
|
200
187
|
#
|
|
201
188
|
def validate_analyzer_configurations
|
|
202
|
-
queries.each do |params|
|
|
189
|
+
rule.queries.each do |params|
|
|
203
190
|
analyzer_name = params[:analyzer]
|
|
204
191
|
klass = get_analyzer_class(analyzer_name)
|
|
205
192
|
|
data/lib/mihari/cli/main.rb
CHANGED
|
@@ -3,28 +3,23 @@
|
|
|
3
3
|
require "thor"
|
|
4
4
|
|
|
5
5
|
# Commands
|
|
6
|
-
require "mihari/commands/
|
|
6
|
+
require "mihari/commands/initializer"
|
|
7
|
+
require "mihari/commands/searcher"
|
|
8
|
+
require "mihari/commands/validator"
|
|
7
9
|
require "mihari/commands/version"
|
|
8
10
|
require "mihari/commands/web"
|
|
9
11
|
|
|
10
12
|
# CLIs
|
|
11
13
|
require "mihari/cli/base"
|
|
12
14
|
|
|
13
|
-
require "mihari/cli/init"
|
|
14
|
-
require "mihari/cli/validator"
|
|
15
|
-
|
|
16
15
|
module Mihari
|
|
17
16
|
module CLI
|
|
18
17
|
class Main < Base
|
|
19
|
-
include Mihari::Commands::
|
|
18
|
+
include Mihari::Commands::Searcher
|
|
20
19
|
include Mihari::Commands::Version
|
|
21
20
|
include Mihari::Commands::Web
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
subcommand "init", Initialization
|
|
25
|
-
|
|
26
|
-
desc "validate", "Sub commands to validate format of a rule"
|
|
27
|
-
subcommand "validate", Validator
|
|
21
|
+
include Mihari::Commands::Validator
|
|
22
|
+
include Mihari::Commands::Initializer
|
|
28
23
|
end
|
|
29
24
|
end
|
|
30
25
|
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "pathname"
|
|
4
|
+
|
|
5
|
+
module Mihari
|
|
6
|
+
module Commands
|
|
7
|
+
module Initializer
|
|
8
|
+
def self.included(thor)
|
|
9
|
+
thor.class_eval do
|
|
10
|
+
desc "init", "Initialize a new rule"
|
|
11
|
+
method_option :path, type: :string, default: "./rule.yml"
|
|
12
|
+
def init
|
|
13
|
+
path = options["path"]
|
|
14
|
+
|
|
15
|
+
warning = "#{path} exists. Do you want to overwrite it? (y/n)"
|
|
16
|
+
return if Pathname(path).exist? && !(yes? warning)
|
|
17
|
+
|
|
18
|
+
initialize_rule path
|
|
19
|
+
|
|
20
|
+
Mihari.logger.info "A new rule is initialized as #{path}."
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
no_commands do
|
|
24
|
+
#
|
|
25
|
+
# @return [Mihari::Structs::Rule]
|
|
26
|
+
#
|
|
27
|
+
def rule_template
|
|
28
|
+
Structs::Rule.from_path File.expand_path("../templates/rule.yml.erb", __dir__)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
#
|
|
32
|
+
# Create a new rule
|
|
33
|
+
#
|
|
34
|
+
# @param [String] path
|
|
35
|
+
# @param [Dry::Files] files
|
|
36
|
+
#
|
|
37
|
+
# @return [nil]
|
|
38
|
+
#
|
|
39
|
+
def initialize_rule(path, files = Dry::Files.new)
|
|
40
|
+
files.write(path, rule_template.yaml)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
module Mihari
|
|
4
4
|
module Commands
|
|
5
|
-
module
|
|
5
|
+
module Searcher
|
|
6
6
|
include Mixins::Database
|
|
7
7
|
include Mixins::ErrorNotification
|
|
8
8
|
|
|
9
9
|
def self.included(thor)
|
|
10
10
|
thor.class_eval do
|
|
11
|
-
desc "search [
|
|
11
|
+
desc "search [PATH]", "Search by a rule"
|
|
12
12
|
method_option :yes, type: :boolean, aliases: "-y", desc: "yes to overwrite the rule in the database"
|
|
13
|
-
def
|
|
13
|
+
def search(path_or_id)
|
|
14
14
|
rule = Structs::Rule.from_path_or_id path_or_id
|
|
15
15
|
|
|
16
16
|
# validate
|
|
@@ -21,38 +21,27 @@ module Mihari
|
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
# check update
|
|
24
|
-
id = rule.id
|
|
25
24
|
yes = options["yes"] || false
|
|
26
25
|
unless yes
|
|
27
26
|
with_db_connection do
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
unless yes?("This operation will overwrite the rule in the database (Rule ID: #{id}). Are you sure you want to update the rule? (yes/no)")
|
|
27
|
+
next if Mihari::Rule.find(rule.id).data == rule.data.deep_stringify_keys
|
|
28
|
+
unless yes?("This operation will overwrite the rule in the database (Rule ID: #{rule.id}). Are you sure you want to update the rule? (y/n)")
|
|
31
29
|
return
|
|
32
30
|
end
|
|
33
31
|
rescue ActiveRecord::RecordNotFound
|
|
34
32
|
next
|
|
35
33
|
end
|
|
36
34
|
end
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
# update rule model
|
|
36
|
+
rule.model.save
|
|
39
37
|
|
|
40
38
|
with_error_notification do
|
|
41
|
-
alert = analyzer.run
|
|
42
|
-
|
|
39
|
+
alert = rule.analyzer.run
|
|
43
40
|
if alert
|
|
44
41
|
data = Mihari::Entities::Alert.represent(alert)
|
|
45
42
|
puts JSON.pretty_generate(data.as_json)
|
|
46
43
|
else
|
|
47
|
-
Mihari.logger.info "
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# record a rule
|
|
51
|
-
with_db_connection do
|
|
52
|
-
model = rule.to_model
|
|
53
|
-
model.save
|
|
54
|
-
rescue ActiveRecord::RecordNotUnique
|
|
55
|
-
nil
|
|
44
|
+
Mihari.logger.info "There is no new alert created in the database"
|
|
56
45
|
end
|
|
57
46
|
end
|
|
58
47
|
end
|
|
@@ -5,7 +5,7 @@ module Mihari
|
|
|
5
5
|
module Validator
|
|
6
6
|
def self.included(thor)
|
|
7
7
|
thor.class_eval do
|
|
8
|
-
desc "
|
|
8
|
+
desc "validate [PATH]", "Validate a rule file"
|
|
9
9
|
#
|
|
10
10
|
# Validate format of a rule
|
|
11
11
|
#
|
|
@@ -13,7 +13,7 @@ module Mihari
|
|
|
13
13
|
#
|
|
14
14
|
# @return [nil]
|
|
15
15
|
#
|
|
16
|
-
def
|
|
16
|
+
def validate(path)
|
|
17
17
|
rule = Structs::Rule.from_path_or_id(path)
|
|
18
18
|
|
|
19
19
|
begin
|
data/lib/mihari/commands/web.rb
CHANGED
|
@@ -8,18 +8,20 @@ module Mihari
|
|
|
8
8
|
desc "web", "Launch the web app"
|
|
9
9
|
method_option :port, type: :numeric, default: 9292, desc: "Hostname to listen on"
|
|
10
10
|
method_option :host, type: :string, default: "localhost", desc: "Port to listen on"
|
|
11
|
-
method_option :threads, type: :string, default: "
|
|
11
|
+
method_option :threads, type: :string, default: "0:5", desc: "min:max threads to use"
|
|
12
12
|
method_option :verbose, type: :boolean, default: true, desc: "Report each request"
|
|
13
|
+
method_option :worker_timeout, type: :numeric, default: 60, desc: "Worker timeout value (in seconds)"
|
|
13
14
|
def web
|
|
14
15
|
port = options["port"]
|
|
15
16
|
host = options["host"]
|
|
16
17
|
threads = options["threads"]
|
|
17
18
|
verbose = options["verbose"]
|
|
19
|
+
worker_timeout = options["worker_timeout"]
|
|
18
20
|
|
|
19
21
|
# set rack env as production
|
|
20
22
|
ENV["RACK_ENV"] ||= "production"
|
|
21
23
|
|
|
22
|
-
Mihari::App.run!(port: port, host: host, threads: threads, verbose: verbose)
|
|
24
|
+
Mihari::App.run!(port: port, host: host, threads: threads, verbose: verbose, worker_timeout: worker_timeout)
|
|
23
25
|
end
|
|
24
26
|
end
|
|
25
27
|
end
|
data/lib/mihari/constants.rb
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Mihari
|
|
4
|
-
|
|
4
|
+
DEFAULT_DATA_TYPES = %w[hash ip domain url mail].freeze
|
|
5
5
|
|
|
6
|
-
DEFAULT_EMITTERS = [
|
|
6
|
+
DEFAULT_EMITTERS = %w[database misp slack the_hive].map { |name| { emitter: name } }.freeze
|
|
7
7
|
|
|
8
|
-
DEFAULT_ENRICHERS = [
|
|
8
|
+
DEFAULT_ENRICHERS = %w[whois ipinfo shodan google_public_dns].map { |name| { enricher: name } }.freeze
|
|
9
9
|
end
|