mihari 4.11.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (154) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +1 -1
  3. data/README.md +13 -3
  4. data/Steepfile +0 -1
  5. data/build_frontend.sh +0 -3
  6. data/docker/Dockerfile +11 -12
  7. data/images/Tines-Full_Logo-Tines_Black.png +0 -0
  8. data/lib/mihari/analyzers/base.rb +12 -28
  9. data/lib/mihari/analyzers/rule.rb +23 -36
  10. data/lib/mihari/cli/main.rb +6 -11
  11. data/lib/mihari/commands/initializer.rb +47 -0
  12. data/lib/mihari/commands/{search.rb → searcher.rb} +9 -20
  13. data/lib/mihari/commands/validator.rb +2 -2
  14. data/lib/mihari/commands/web.rb +4 -2
  15. data/lib/mihari/constants.rb +3 -3
  16. data/lib/mihari/database.rb +52 -87
  17. data/lib/mihari/emitters/database.rb +16 -7
  18. data/lib/mihari/emitters/misp.rb +13 -5
  19. data/lib/mihari/emitters/slack.rb +15 -8
  20. data/lib/mihari/emitters/the_hive.rb +42 -21
  21. data/lib/mihari/emitters/webhook.rb +99 -31
  22. data/lib/mihari/entities/alert.rb +7 -5
  23. data/lib/mihari/entities/artifact.rb +20 -8
  24. data/lib/mihari/entities/config.rb +2 -6
  25. data/lib/mihari/entities/rule.rb +8 -0
  26. data/lib/mihari/http.rb +13 -13
  27. data/lib/mihari/mixins/{disallowed_data_value.rb → falsepositive.rb} +8 -8
  28. data/lib/mihari/models/alert.rb +2 -15
  29. data/lib/mihari/models/artifact.rb +28 -17
  30. data/lib/mihari/models/rule.rb +7 -13
  31. data/lib/mihari/schemas/emitter.rb +6 -8
  32. data/lib/mihari/schemas/rule.rb +11 -13
  33. data/lib/mihari/structs/config.rb +41 -0
  34. data/lib/mihari/structs/filters.rb +2 -2
  35. data/lib/mihari/structs/rule.rb +96 -83
  36. data/lib/mihari/templates/rule.yml.erb +5 -23
  37. data/lib/mihari/types.rb +1 -1
  38. data/lib/mihari/version.rb +1 -1
  39. data/lib/mihari/web/api.rb +0 -2
  40. data/lib/mihari/web/app.rb +10 -4
  41. data/lib/mihari/web/endpoints/alerts.rb +11 -3
  42. data/lib/mihari/web/endpoints/configs.rb +1 -6
  43. data/lib/mihari/web/endpoints/rules.rb +27 -15
  44. data/lib/mihari/web/public/assets/{fa-brands-400.c7ae37d3.ttf → fa-brands-400-2ef6fdde.ttf} +0 -0
  45. data/lib/mihari/web/public/assets/fa-brands-400-f4617423.woff2 +0 -0
  46. data/lib/mihari/web/public/assets/fa-regular-400-12dea17b.ttf +0 -0
  47. data/lib/mihari/web/public/assets/fa-regular-400-7ba24c41.woff2 +0 -0
  48. data/lib/mihari/web/public/assets/fa-solid-900-67a880b4.ttf +0 -0
  49. data/lib/mihari/web/public/assets/fa-solid-900-e2c5cf54.woff2 +0 -0
  50. data/lib/mihari/web/public/assets/fa-v4compatibility-7c377405.woff2 +0 -0
  51. data/lib/mihari/web/public/assets/fa-v4compatibility-8d9500e8.ttf +0 -0
  52. data/lib/mihari/web/public/assets/{index.e1e67d84.css → index-625e95fe.css} +3 -3
  53. data/lib/mihari/web/public/assets/index-63900d73.js +50 -0
  54. data/lib/mihari/web/public/index.html +3 -3
  55. data/lib/mihari/web/public/redoc-static.html +26 -27
  56. data/lib/mihari.rb +11 -21
  57. data/mihari.gemspec +14 -14
  58. metadata +46 -131
  59. data/lib/mihari/cli/init.rb +0 -11
  60. data/lib/mihari/cli/validator.rb +0 -11
  61. data/lib/mihari/commands/init.rb +0 -51
  62. data/lib/mihari/emitters/http.rb +0 -127
  63. data/lib/mihari/entities/source.rb +0 -9
  64. data/lib/mihari/status.rb +0 -55
  65. data/lib/mihari/web/endpoints/sources.rb +0 -19
  66. data/lib/mihari/web/public/assets/fa-brands-400.3fe890d0.woff2 +0 -0
  67. data/lib/mihari/web/public/assets/fa-regular-400.fdc1f753.ttf +0 -0
  68. data/lib/mihari/web/public/assets/fa-regular-400.fe69d948.woff2 +0 -0
  69. data/lib/mihari/web/public/assets/fa-solid-900.6d53c706.ttf +0 -0
  70. data/lib/mihari/web/public/assets/fa-solid-900.d27bc752.woff2 +0 -0
  71. data/lib/mihari/web/public/assets/fa-v4compatibility.4d73f280.ttf +0 -0
  72. data/lib/mihari/web/public/assets/fa-v4compatibility.7d1c2ce5.woff2 +0 -0
  73. data/lib/mihari/web/public/assets/index.d3a61a69.js +0 -68
  74. data/sig/lib/mihari/analyzers/base.rbs +0 -90
  75. data/sig/lib/mihari/analyzers/binaryedge.rbs +0 -26
  76. data/sig/lib/mihari/analyzers/censys.rbs +0 -41
  77. data/sig/lib/mihari/analyzers/circl.rbs +0 -31
  78. data/sig/lib/mihari/analyzers/crtsh.rbs +0 -17
  79. data/sig/lib/mihari/analyzers/dnpedia.rbs +0 -15
  80. data/sig/lib/mihari/analyzers/dnstwister.rbs +0 -25
  81. data/sig/lib/mihari/analyzers/feed.rbs +0 -20
  82. data/sig/lib/mihari/analyzers/onyphe.rbs +0 -34
  83. data/sig/lib/mihari/analyzers/otx.rbs +0 -33
  84. data/sig/lib/mihari/analyzers/passivetotal.rbs +0 -35
  85. data/sig/lib/mihari/analyzers/pulsedive.rbs +0 -27
  86. data/sig/lib/mihari/analyzers/rule.rbs +0 -68
  87. data/sig/lib/mihari/analyzers/securitytrails.rbs +0 -33
  88. data/sig/lib/mihari/analyzers/shodan.rbs +0 -36
  89. data/sig/lib/mihari/analyzers/urlscan.rbs +0 -31
  90. data/sig/lib/mihari/analyzers/virustotal.rbs +0 -31
  91. data/sig/lib/mihari/analyzers/virustotal_intelligence.rbs +0 -33
  92. data/sig/lib/mihari/analyzers/zoomeye.rbs +0 -35
  93. data/sig/lib/mihari/cli/base.rbs +0 -9
  94. data/sig/lib/mihari/cli/init.rbs +0 -7
  95. data/sig/lib/mihari/cli/main.rbs +0 -9
  96. data/sig/lib/mihari/cli/validator.rbs +0 -7
  97. data/sig/lib/mihari/commands/init.rbs +0 -9
  98. data/sig/lib/mihari/commands/json.rbs +0 -7
  99. data/sig/lib/mihari/commands/search.rbs +0 -35
  100. data/sig/lib/mihari/commands/validator.rbs +0 -9
  101. data/sig/lib/mihari/commands/web.rbs +0 -7
  102. data/sig/lib/mihari/constants.rbs +0 -5
  103. data/sig/lib/mihari/database.rbs +0 -25
  104. data/sig/lib/mihari/emitters/base.rbs +0 -18
  105. data/sig/lib/mihari/emitters/database.rbs +0 -9
  106. data/sig/lib/mihari/emitters/http.rbs +0 -35
  107. data/sig/lib/mihari/emitters/misp.rbs +0 -34
  108. data/sig/lib/mihari/emitters/slack.rbs +0 -73
  109. data/sig/lib/mihari/emitters/stdout.rbs +0 -9
  110. data/sig/lib/mihari/emitters/the_hive.rbs +0 -32
  111. data/sig/lib/mihari/emitters/webhook.rbs +0 -20
  112. data/sig/lib/mihari/enrichers/base.rbs +0 -12
  113. data/sig/lib/mihari/enrichers/google_public_dns.rbs +0 -18
  114. data/sig/lib/mihari/enrichers/ipinfo.rbs +0 -16
  115. data/sig/lib/mihari/errors.rbs +0 -10
  116. data/sig/lib/mihari/feed/parser.rbs +0 -11
  117. data/sig/lib/mihari/feed/reader.rbs +0 -56
  118. data/sig/lib/mihari/http.rbs +0 -64
  119. data/sig/lib/mihari/mixins/autonomous_system.rbs +0 -14
  120. data/sig/lib/mihari/mixins/configurable.rbs +0 -30
  121. data/sig/lib/mihari/mixins/configuration.rbs +0 -45
  122. data/sig/lib/mihari/mixins/disallowed_data_value.rbs +0 -23
  123. data/sig/lib/mihari/mixins/error_notification.rbs +0 -12
  124. data/sig/lib/mihari/mixins/hash.rbs +0 -14
  125. data/sig/lib/mihari/mixins/refang.rbs +0 -14
  126. data/sig/lib/mihari/mixins/retriable.rbs +0 -15
  127. data/sig/lib/mihari/models/alert.rbs +0 -18
  128. data/sig/lib/mihari/models/artifact.rbs +0 -69
  129. data/sig/lib/mihari/models/autonomous_system.rbs +0 -14
  130. data/sig/lib/mihari/models/cpe.rbs +0 -7
  131. data/sig/lib/mihari/models/dns.rbs +0 -19
  132. data/sig/lib/mihari/models/geolocation.rbs +0 -15
  133. data/sig/lib/mihari/models/port.rbs +0 -7
  134. data/sig/lib/mihari/models/reverse_dns.rbs +0 -14
  135. data/sig/lib/mihari/models/rule.rbs +0 -17
  136. data/sig/lib/mihari/models/tag.rbs +0 -5
  137. data/sig/lib/mihari/models/tagging.rbs +0 -4
  138. data/sig/lib/mihari/models/whois.rbs +0 -66
  139. data/sig/lib/mihari/status.rbs +0 -25
  140. data/sig/lib/mihari/structs/censys.rbs +0 -58
  141. data/sig/lib/mihari/structs/filters.rbs +0 -40
  142. data/sig/lib/mihari/structs/google_public_dns.rbs +0 -21
  143. data/sig/lib/mihari/structs/greynoise.rbs +0 -30
  144. data/sig/lib/mihari/structs/ipinfo.rbs +0 -17
  145. data/sig/lib/mihari/structs/onyphe.rbs +0 -25
  146. data/sig/lib/mihari/structs/rule.rbs +0 -57
  147. data/sig/lib/mihari/structs/shodan.rbs +0 -30
  148. data/sig/lib/mihari/structs/urlscan.rbs +0 -28
  149. data/sig/lib/mihari/structs/virustotal_intelligence.rbs +0 -33
  150. data/sig/lib/mihari/type_checker.rbs +0 -48
  151. data/sig/lib/mihari/types.rbs +0 -23
  152. data/sig/lib/mihari/version.rbs +0 -3
  153. data/sig/lib/mihari/web/app.rbs +0 -5
  154. data/sig/lib/mihari.rbs +0 -54
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eda042a7f3e0c70bb86a1008d24556b2daf4264142e01936e4bdab3da275ff39
4
- data.tar.gz: a5e3711ecb0fb982ce6280fbed12261d753e2224966e3961ac2cfe2b83e0e15d
3
+ metadata.gz: 41784cbe3811a8f5f2a6a4de663ceaa03e2635c329c455b523e5fc55fda0c9e7
4
+ data.tar.gz: '052987fcb8805a8f71a0716d5e0970b0c4c7451059dded4b5149679ccda6c999'
5
5
  SHA512:
6
- metadata.gz: f33e198db0d4964eb15372b30ef6733aaf3efe51b31640e62e33da2412089e30f618ea9509cc86d57a6e38ae65eac1dd5c7f3fab9c2dc1d5793d005bdcb95862
7
- data.tar.gz: d7824b329d6117c2539e62044374b989538e4d22fd7641eb63b11db3b766ef52943f3559cb6a43be440b60f55609a763e316661e69196d6749486c8027cad5b2
6
+ metadata.gz: f16447e83adb4630baf9587eecea0dbb6f580de23894d70fa2c1729483faa6dc1c86fe5de2bd782100f8cba89e2a50e7ce2f0ab675de822fe5fb767fb2c6f45f
7
+ data.tar.gz: af70fcc510ed4fd6a434481a8372c56d20a54b605ee0b49425a1315531d2261db6df9588f1961ba34796728ff40f0ce188ad58e6c542f4c3644a8c8251b37eb5
@@ -43,7 +43,7 @@ jobs:
43
43
  strategy:
44
44
  fail-fast: false
45
45
  matrix:
46
- ruby: [2.7, "3.0", 3.1]
46
+ ruby: [2.7, "3.0", 3.1, 3.2]
47
47
 
48
48
  steps:
49
49
  - uses: actions/checkout@v3
data/README.md CHANGED
@@ -5,9 +5,19 @@
5
5
  [![Coverage Status](https://coveralls.io/repos/github/ninoseki/mihari/badge.svg?branch=master)](https://coveralls.io/github/ninoseki/mihari?branch=master)
6
6
  [![CodeFactor](https://www.codefactor.io/repository/github/ninoseki/mihari/badge)](https://www.codefactor.io/repository/github/ninoseki/mihari)
7
7
 
8
- ![img](https://github.com/ninoseki/mihari/raw/master/images/logo.png)
9
-
10
- [![](images/tines.png)](https://tines.io?utm_source=github&utm_medium=sponsorship&utm_campaign=ninoseki)
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
@@ -1,5 +1,4 @@
1
1
  target :lib do
2
- signature "sig"
3
2
  check "lib"
4
3
 
5
4
  repo_path "vendor/rbs/gem_rbs_collection/gems"
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.0.3-alpine3.13
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
- && cd /tmp/ \
6
- && git clone https://github.com/ninoseki/mihari.git \
7
- && cd mihari \
8
- && gem build mihari.gemspec -o mihari.gem \
9
- && gem install mihari.gem \
10
- && rm -rf /tmp/mihari \
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"]
@@ -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
- attr_accessor :ignore_old_artifacts, :ignore_threshold
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
- @ignore_old_artifacts = false
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?(ignore_old_artifacts: ignore_old_artifacts, ignore_threshold: ignore_threshold)
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
- class Rule < Base
40
- include Mixins::DisallowedDataValue
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
- option :emitters, optional: true
52
- option :enrichers, optional: true
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
- allowed_data_types.include? artifact.data_type
96
+ rule.data_types.include? artifact.data_type
110
97
  end.reject do |artifact|
111
- disallowed_data_value? artifact.data
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 normalized_disallowed_data_values
136
- @normalized_disallowed_data_values ||= disallowed_data_values.map { |v| normalize_disallowed_data_value v }
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 disallowed data value or not
127
+ # Check whether a value is a falsepositive value or not
141
128
  #
142
129
  # @return [Boolean]
143
130
  #
144
- def disallowed_data_value?(value)
145
- return true if normalized_disallowed_data_values.include?(value)
131
+ def falsepositive?(value)
132
+ return true if normalized_falsepositives.include?(value)
146
133
 
147
- normalized_disallowed_data_values.select do |disallowed_data_value|
148
- disallowed_data_value.is_a?(Regexp)
149
- end.any? do |disallowed_data_value|
150
- disallowed_data_value.match?(value)
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
 
@@ -3,28 +3,23 @@
3
3
  require "thor"
4
4
 
5
5
  # Commands
6
- require "mihari/commands/search"
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::Search
18
+ include Mihari::Commands::Searcher
20
19
  include Mihari::Commands::Version
21
20
  include Mihari::Commands::Web
22
-
23
- desc "init", "Sub commands to initialize a rule"
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 Search
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 [RULE]", "Search by a rule"
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 search_by_rule(path_or_id)
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
- rule_ = Mihari::Rule.find(id)
29
- next if rule.yaml == rule_.yaml
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
- analyzer = rule.to_analyzer
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 "No new alert created in the database"
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 "rule [PATH]", "Validate rule file format"
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 rule(path)
16
+ def validate(path)
17
17
  rule = Structs::Rule.from_path_or_id(path)
18
18
 
19
19
  begin
@@ -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: "1:1", desc: "min:max threads to use"
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
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- ALLOWED_DATA_TYPES = ["hash", "ip", "domain", "url", "mail"].freeze
4
+ DEFAULT_DATA_TYPES = %w[hash ip domain url mail].freeze
5
5
 
6
- DEFAULT_EMITTERS = ["database", "misp", "slack", "the_hive", "webhook"].map { |name| { emitter: name } }.freeze
6
+ DEFAULT_EMITTERS = %w[database misp slack the_hive].map { |name| { emitter: name } }.freeze
7
7
 
8
- DEFAULT_ENRICHERS = ["whois", "ipinfo", "shodan", "google_public_dns"].map { |name| { enricher: name } }.freeze
8
+ DEFAULT_ENRICHERS = %w[whois ipinfo shodan google_public_dns].map { |name| { enricher: name } }.freeze
9
9
  end