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.
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