mihari 5.2.2 → 5.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/build_frontend.sh +1 -9
  3. data/frontend/.browserslistrc +3 -0
  4. data/frontend/.eslintrc.js +33 -0
  5. data/frontend/.gitignore +25 -0
  6. data/frontend/README.md +3 -0
  7. data/frontend/babel.config.js +3 -0
  8. data/frontend/index.html +21 -0
  9. data/frontend/jest.config.js +9 -0
  10. data/frontend/package-lock.json +13216 -0
  11. data/frontend/package.json +54 -0
  12. data/frontend/public/favicon.ico +0 -0
  13. data/frontend/scripts/swagger_doc_to_yaml.rb +23 -0
  14. data/frontend/src/App.vue +27 -0
  15. data/frontend/src/api-helper.ts +113 -0
  16. data/frontend/src/api.ts +105 -0
  17. data/frontend/src/components/ErrorMessage.vue +32 -0
  18. data/frontend/src/components/Loading.vue +15 -0
  19. data/frontend/src/components/Navbar.vue +59 -0
  20. data/frontend/src/components/Pagination.vue +126 -0
  21. data/frontend/src/components/alert/Alert.vue +92 -0
  22. data/frontend/src/components/alert/Alerts.vue +66 -0
  23. data/frontend/src/components/alert/AlertsWithPagination.vue +91 -0
  24. data/frontend/src/components/alert/AlertsWrapper.vue +141 -0
  25. data/frontend/src/components/alert/Form.vue +185 -0
  26. data/frontend/src/components/artifact/AS.vue +29 -0
  27. data/frontend/src/components/artifact/Artifact.vue +321 -0
  28. data/frontend/src/components/artifact/ArtifactTag.vue +70 -0
  29. data/frontend/src/components/artifact/ArtifactTags.vue +29 -0
  30. data/frontend/src/components/artifact/ArtifactWrapper.vue +62 -0
  31. data/frontend/src/components/artifact/CPEs.vue +23 -0
  32. data/frontend/src/components/artifact/DnsRecords.vue +38 -0
  33. data/frontend/src/components/artifact/Ports.vue +23 -0
  34. data/frontend/src/components/artifact/ReverseDnsNames.vue +31 -0
  35. data/frontend/src/components/artifact/Tags.vue +29 -0
  36. data/frontend/src/components/artifact/WhoisRecord.vue +49 -0
  37. data/frontend/src/components/config/Configs.vue +68 -0
  38. data/frontend/src/components/config/ConfigsWrapper.vue +40 -0
  39. data/frontend/src/components/link/Link.vue +32 -0
  40. data/frontend/src/components/link/Links.vue +47 -0
  41. data/frontend/src/components/rule/EditRule.vue +74 -0
  42. data/frontend/src/components/rule/EditRuleWrapper.vue +56 -0
  43. data/frontend/src/components/rule/Form.vue +160 -0
  44. data/frontend/src/components/rule/InputForm.vue +80 -0
  45. data/frontend/src/components/rule/NewRule.vue +60 -0
  46. data/frontend/src/components/rule/Rule.vue +108 -0
  47. data/frontend/src/components/rule/RuleWrapper.vue +62 -0
  48. data/frontend/src/components/rule/Rules.vue +88 -0
  49. data/frontend/src/components/rule/RulesWrapper.vue +130 -0
  50. data/frontend/src/components/rule/YAML.vue +47 -0
  51. data/frontend/src/components/tag/Tag.vue +73 -0
  52. data/frontend/src/components/tag/Tags.vue +37 -0
  53. data/frontend/src/countries.ts +350 -0
  54. data/frontend/src/index.ts +23 -0
  55. data/frontend/src/links/anyrun.ts +19 -0
  56. data/frontend/src/links/base.ts +14 -0
  57. data/frontend/src/links/censys.ts +20 -0
  58. data/frontend/src/links/crtsh.ts +20 -0
  59. data/frontend/src/links/dnslytics.ts +38 -0
  60. data/frontend/src/links/greynoise.ts +20 -0
  61. data/frontend/src/links/index.ts +40 -0
  62. data/frontend/src/links/intezer.ts +20 -0
  63. data/frontend/src/links/otx.ts +33 -0
  64. data/frontend/src/links/securitytrails.ts +38 -0
  65. data/frontend/src/links/shodan.ts +20 -0
  66. data/frontend/src/links/urlscan.ts +50 -0
  67. data/frontend/src/links/virustotal.ts +72 -0
  68. data/frontend/src/main.ts +11 -0
  69. data/frontend/src/router/index.ts +57 -0
  70. data/frontend/src/rule.ts +14 -0
  71. data/frontend/src/shims-vue.d.ts +6 -0
  72. data/frontend/src/swagger.yaml +737 -0
  73. data/frontend/src/types.ts +188 -0
  74. data/frontend/src/utils.ts +60 -0
  75. data/frontend/src/views/Alerts.vue +20 -0
  76. data/frontend/src/views/Artifact.vue +44 -0
  77. data/frontend/src/views/Configs.vue +20 -0
  78. data/frontend/src/views/EditRule.vue +44 -0
  79. data/frontend/src/views/NewRule.vue +26 -0
  80. data/frontend/src/views/Rule.vue +44 -0
  81. data/frontend/src/views/Rules.vue +20 -0
  82. data/frontend/tests/unit/utils.spec.ts +7 -0
  83. data/frontend/tsconfig.json +40 -0
  84. data/frontend/vite.config.js +24 -0
  85. data/lefthook.yml +10 -0
  86. data/lib/mihari/analyzers/base.rb +22 -5
  87. data/lib/mihari/analyzers/binaryedge.rb +0 -1
  88. data/lib/mihari/analyzers/censys.rb +7 -2
  89. data/lib/mihari/analyzers/circl.rb +1 -1
  90. data/lib/mihari/analyzers/passivetotal.rb +1 -1
  91. data/lib/mihari/analyzers/rule.rb +43 -73
  92. data/lib/mihari/analyzers/virustotal_intelligence.rb +1 -2
  93. data/lib/mihari/clients/base.rb +1 -1
  94. data/lib/mihari/commands/database.rb +12 -11
  95. data/lib/mihari/commands/rule.rb +47 -45
  96. data/lib/mihari/commands/search.rb +73 -45
  97. data/lib/mihari/commands/version.rb +8 -6
  98. data/lib/mihari/commands/web.rb +26 -23
  99. data/lib/mihari/emitters/base.rb +14 -1
  100. data/lib/mihari/emitters/database.rb +3 -10
  101. data/lib/mihari/emitters/misp.rb +16 -5
  102. data/lib/mihari/emitters/slack.rb +13 -15
  103. data/lib/mihari/emitters/the_hive.rb +17 -19
  104. data/lib/mihari/emitters/webhook.rb +23 -23
  105. data/lib/mihari/enrichers/whois.rb +1 -0
  106. data/lib/mihari/entities/rule.rb +1 -1
  107. data/lib/mihari/entities/tag.rb +1 -1
  108. data/lib/mihari/feed/parser.rb +1 -0
  109. data/lib/mihari/feed/reader.rb +29 -14
  110. data/lib/mihari/mixins/configurable.rb +13 -4
  111. data/lib/mihari/schemas/analyzer.rb +2 -7
  112. data/lib/mihari/schemas/rule.rb +1 -1
  113. data/lib/mihari/structs/censys.rb +96 -82
  114. data/lib/mihari/structs/config.rb +46 -21
  115. data/lib/mihari/structs/google_public_dns.rb +27 -23
  116. data/lib/mihari/structs/greynoise.rb +44 -38
  117. data/lib/mihari/structs/onyphe.rb +34 -30
  118. data/lib/mihari/structs/rule.rb +1 -1
  119. data/lib/mihari/structs/shodan.rb +77 -69
  120. data/lib/mihari/structs/urlscan.rb +42 -36
  121. data/lib/mihari/structs/virustotal_intelligence.rb +57 -49
  122. data/lib/mihari/type_checker.rb +10 -8
  123. data/lib/mihari/version.rb +1 -1
  124. data/lib/mihari/web/public/assets/index-ac4e5ffa.js +50 -0
  125. data/lib/mihari/web/public/index.html +1 -1
  126. data/mihari.gemspec +8 -8
  127. metadata +103 -22
  128. data/.gitmodules +0 -0
  129. data/.overcommit.yml +0 -12
  130. data/lib/mihari/web/public/assets/index-cbe1734c.js +0 -50
@@ -46,7 +46,7 @@ module Mihari
46
46
  #
47
47
  # @param [Mihari::Structs::Rule] rule
48
48
  #
49
- def initialize(rule:)
49
+ def initialize(rule)
50
50
  @rule = rule
51
51
  @base_time = Time.now.utc
52
52
 
@@ -54,12 +54,12 @@ module Mihari
54
54
  end
55
55
 
56
56
  #
57
- # Returns a list of artifacts matched with queries
57
+ # Returns a list of artifacts matched with queries/analyzers
58
58
  #
59
59
  # @return [Array<Mihari::Artifact>]
60
60
  #
61
61
  def artifacts
62
- rule.queries.map { |params| run_query(params.deep_dup) }.flatten
62
+ analyzers.flat_map(&:normalized_artifacts)
63
63
  end
64
64
 
65
65
  #
@@ -111,26 +111,15 @@ module Mihari
111
111
  # @return [Array<Mihari::Alert>]
112
112
  #
113
113
  def bulk_emit
114
- Parallel.map(valid_emitters) { |emitter| emit emitter }.compact
115
- end
116
-
117
- #
118
- # Emit an alert
119
- #
120
- # @param [Mihari::Emitters::Base] emitter
121
- #
122
- # @return [Mihari::Alert, nil]
123
- #
124
- def emit(emitter)
125
- return if enriched_artifacts.empty?
126
-
127
- alert_or_something = emitter.run(artifacts: enriched_artifacts, rule: rule)
128
-
129
- Mihari.logger.info "Emission by #{emitter.class} is succeeded"
130
-
131
- alert_or_something
132
- rescue StandardError => e
133
- Mihari.logger.info "Emission by #{emitter.class} is failed: #{e}"
114
+ return [] if enriched_artifacts.empty?
115
+
116
+ Parallel.map(valid_emitters) do |emitter|
117
+ emission = emitter.emit
118
+ Mihari.logger.info "Emission by #{emitter.class} is succeeded"
119
+ emission
120
+ rescue StandardError => e
121
+ Mihari.logger.info "Emission by #{emitter.class} is failed: #{e}"
122
+ end.compact
134
123
  end
135
124
 
136
125
  #
@@ -165,27 +154,28 @@ module Mihari
165
154
  end
166
155
 
167
156
  #
168
- # @param [Hash] params
157
+ # Get analyzer class
158
+ #
159
+ # @param [String] analyzer_name
169
160
  #
170
- # @return [Array<Mihari::Artifact>]
161
+ # @return [Class<Mihari::Analyzers::Base>] analyzer class
171
162
  #
172
- def run_query(params)
173
- analyzer_name = params[:analyzer]
174
- klass = get_analyzer_class(analyzer_name)
175
-
176
- # set interval in the top level
177
- options = params[:options] || {}
178
- interval = options[:interval]
179
- params[:interval] = interval if interval
163
+ def get_analyzer_class(analyzer_name)
164
+ analyzer = ANALYZER_TO_CLASS[analyzer_name]
165
+ return analyzer if analyzer
180
166
 
181
- # set rule
182
- params[:rule] = rule
183
- query = params[:query]
184
- analyzer = klass.new(query, **params)
167
+ raise ArgumentError, "#{analyzer_name} is not supported"
168
+ end
185
169
 
186
- # Use #normalized_artifacts method to get artifacts as Array<Mihari::Artifact>
187
- # So Mihari::Artifact object has "source" attribute (e.g. "Shodan")
188
- analyzer.normalized_artifacts
170
+ #
171
+ # @return [Array<Mihari::Analyzers::Base>]
172
+ #
173
+ def analyzers
174
+ @analyzers ||= rule.queries.map do |query_params|
175
+ analyzer_name = query_params[:analyzer]
176
+ klass = get_analyzer_class(analyzer_name)
177
+ klass.from_query(query_params)
178
+ end
189
179
  end
190
180
 
191
181
  #
@@ -203,53 +193,33 @@ module Mihari
203
193
  end
204
194
 
205
195
  #
206
- # @param [Hash] params
196
+ # Deep copied emitters
207
197
  #
208
- # @return [Mihari::Emitter:Base]
198
+ # @return [Array<Mihari::Emitters::Base>]
209
199
  #
210
- def validate_emitter(params)
211
- name = params[:emitter]
212
- params.delete(:emitter)
200
+ def emitters
201
+ rule.emitters.map(&:deep_dup).map do |params|
202
+ name = params[:emitter]
203
+ params.delete(:emitter)
213
204
 
214
- klass = get_emitter_class(name)
215
- emitter = klass.new(**params)
216
-
217
- emitter.valid? ? emitter : nil
205
+ klass = get_emitter_class(name)
206
+ klass.new(artifacts: enriched_artifacts, rule: rule, **params)
207
+ end
218
208
  end
219
209
 
220
210
  #
221
- # @return [Array<Mihari::Emitter::Base>]
211
+ # @return [Array<Mihari::Emitters::Base>]
222
212
  #
223
213
  def valid_emitters
224
- @valid_emitters ||= rule.emitters.filter_map { |params| validate_emitter(params.deep_dup) }
225
- end
226
-
227
- #
228
- # Get analyzer class
229
- #
230
- # @param [String] analyzer_name
231
- #
232
- # @return [Class<Mihari::Analyzers::Base>] analyzer class
233
- #
234
- def get_analyzer_class(analyzer_name)
235
- analyzer = ANALYZER_TO_CLASS[analyzer_name]
236
- return analyzer if analyzer
237
-
238
- raise ArgumentError, "#{analyzer_name} is not supported"
214
+ @valid_emitters ||= emitters.select(&:valid?)
239
215
  end
240
216
 
241
217
  #
242
218
  # Validate configuration of analyzers
243
219
  #
244
220
  def validate_analyzer_configurations
245
- rule.queries.each do |params|
246
- analyzer_name = params[:analyzer]
247
-
248
- klass = get_analyzer_class(analyzer_name)
249
- klass_name = klass.to_s.split("::").last
250
-
251
- instance = klass.new("dummy")
252
- raise ConfigurationError, "#{klass_name} is not configured correctly" unless instance.configured?
221
+ analyzers.map do |analyzer|
222
+ raise ConfigurationError, "#{analyzer.source} is not configured correctly" unless analyzer.configured?
253
223
  end
254
224
  end
255
225
  end
@@ -25,8 +25,7 @@ module Mihari
25
25
  end
26
26
 
27
27
  def artifacts
28
- responses = search_with_cursor
29
- responses.map(&:to_artifacts).flatten
28
+ search_with_cursor.map(&:to_artifacts).flatten
30
29
  end
31
30
 
32
31
  private
@@ -31,7 +31,7 @@ module Mihari
31
31
 
32
32
  #
33
33
  # @param [String] path
34
- # @param [Hashk, nil] params
34
+ # @param [Hash, nil] params
35
35
  #
36
36
  # @return [String] <description>
37
37
  #
@@ -3,18 +3,19 @@
3
3
  module Mihari
4
4
  module Commands
5
5
  module Database
6
- def self.included(thor)
7
- thor.class_eval do
8
- desc "migrate", "Migrate DB schemas"
9
- method_option :verbose, type: :boolean, default: true
10
- #
11
- # @param [String] direction
12
- #
13
- def migrate(direction = "up")
14
- verbose = options["verbose"]
15
- ActiveRecord::Migration.verbose = verbose
6
+ class << self
7
+ def included(thor)
8
+ thor.class_eval do
9
+ desc "migrate", "Migrate DB schemas"
10
+ method_option :verbose, type: :boolean, default: true
11
+ #
12
+ # @param [String] direction
13
+ #
14
+ def migrate(direction = "up")
15
+ ActiveRecord::Migration.verbose = options["verbose"]
16
16
 
17
- Mihari::Database.with_db_connection { Mihari::Database.migrate(direction.to_sym) }
17
+ Mihari::Database.with_db_connection { Mihari::Database.migrate direction.to_sym }
18
+ end
18
19
  end
19
20
  end
20
21
  end
@@ -5,60 +5,62 @@ require "pathname"
5
5
  module Mihari
6
6
  module Commands
7
7
  module Rule
8
- def self.included(thor)
9
- thor.class_eval do
10
- desc "validate [PATH]", "Validate a rule file"
11
- #
12
- # Validate format of a rule
13
- #
14
- # @param [String] path
15
- #
16
- def validate(path)
17
- rule = Structs::Rule.from_path_or_id(path)
18
-
19
- begin
20
- rule.validate!
21
- Mihari.logger.info "Valid format. The input is parsed as the following:"
22
- Mihari.logger.info rule.data.to_yaml
23
- rescue RuleValidationError
24
- nil
25
- end
26
- end
27
-
28
- desc "init [PATH]", "Initialize a new rule file"
29
- #
30
- # Initialize a new rule file
31
- #
32
- # @param [String] path
33
- #
34
- #
35
- def init(path = "./rule.yml")
36
- warning = "#{path} exists. Do you want to overwrite it? (y/n)"
37
- return if Pathname(path).exist? && !(yes? warning)
38
-
39
- initialize_rule path
40
-
41
- Mihari.logger.info "A new rule is initialized: #{path}."
42
- end
43
-
44
- no_commands do
8
+ class << self
9
+ def included(thor)
10
+ thor.class_eval do
11
+ desc "validate [PATH]", "Validate a rule file"
45
12
  #
46
- # @return [Mihari::Structs::Rule]
13
+ # Validate format of a rule
47
14
  #
48
- def rule_template
49
- Structs::Rule.from_path File.expand_path("../templates/rule.yml.erb", __dir__)
15
+ # @param [String] path
16
+ #
17
+ def validate(path)
18
+ rule = Structs::Rule.from_path_or_id(path)
19
+
20
+ begin
21
+ rule.validate!
22
+ Mihari.logger.info "Valid format. The input is parsed as the following:"
23
+ Mihari.logger.info rule.data.to_yaml
24
+ rescue RuleValidationError
25
+ nil
26
+ end
50
27
  end
51
28
 
29
+ desc "init [PATH]", "Initialize a new rule file"
52
30
  #
53
- # Create a new rule
31
+ # Initialize a new rule file
54
32
  #
55
33
  # @param [String] path
56
- # @param [Dry::Files] files
57
34
  #
58
- # @return [nil]
59
35
  #
60
- def initialize_rule(path, files = Dry::Files.new)
61
- files.write(path, rule_template.yaml)
36
+ def init(path = "./rule.yml")
37
+ warning = "#{path} exists. Do you want to overwrite it? (y/n)"
38
+ return if Pathname(path).exist? && !(yes? warning)
39
+
40
+ initialize_rule path
41
+
42
+ Mihari.logger.info "A new rule file has been initialized: #{path}."
43
+ end
44
+
45
+ no_commands do
46
+ #
47
+ # @return [Mihari::Structs::Rule]
48
+ #
49
+ def rule_template
50
+ Structs::Rule.from_path File.expand_path("../templates/rule.yml.erb", __dir__)
51
+ end
52
+
53
+ #
54
+ # Create a new rule
55
+ #
56
+ # @param [String] path
57
+ # @param [Dry::Files] files
58
+ #
59
+ # @return [nil]
60
+ #
61
+ def initialize_rule(path, files = Dry::Files.new)
62
+ files.write(path, rule_template.yaml)
63
+ end
62
64
  end
63
65
  end
64
66
  end
@@ -3,64 +3,92 @@
3
3
  module Mihari
4
4
  module Commands
5
5
  module Search
6
- include Mixins::ErrorNotification
6
+ class << self
7
+ class RuleWrapper
8
+ include Mixins::ErrorNotification
9
+
10
+ # @return [Nihari::Structs::Rule]
11
+ attr_reader :rule
12
+
13
+ # @return [Boolean]
14
+ attr_reader :force_overwrite
15
+
16
+ def initialize(rule, force_overwrite:)
17
+ @rule = rule
18
+ @force_overwrite = force_overwrite
19
+ end
20
+
21
+ def force_overwrite?
22
+ force_overwrite
23
+ end
7
24
 
8
- def self.included(thor)
9
- thor.class_eval do
10
- desc "search [PATH]", "Search by a rule"
11
- method_option :force_overwrite, type: :boolean, aliases: "-f", desc: "Force an overwrite the rule"
12
- #
13
- # Search by a rule
14
25
  #
15
- # @param [String] path_or_id
26
+ # @return [Boolean]
16
27
  #
17
- def search(path_or_id)
18
- Mihari::Database.with_db_connection do
19
- rule = Structs::Rule.from_path_or_id path_or_id
28
+ def diff?
29
+ model = Mihari::Rule.find(rule.id)
30
+ model.data != rule.data.deep_stringify_keys
31
+ rescue ActiveRecord::RecordNotFound
32
+ false
33
+ end
34
+
35
+ def update_or_create
36
+ rule.model.save
37
+ end
20
38
 
21
- begin
22
- rule.validate!
23
- rescue RuleValidationError
39
+ def run
40
+ begin
41
+ analyzer = rule.analyzer
42
+ rescue ConfigurationError => e
43
+ # if there is a configuration error, output that error without the stack trace
44
+ Mihari.logger.error e.to_s
45
+ return
46
+ end
47
+
48
+ with_error_notification do
49
+ alert = analyzer.run
50
+ if alert.nil?
51
+ Mihari.logger.info "There is no new artifact found"
24
52
  return
25
53
  end
26
54
 
27
- update_rule rule
28
- run_rule rule
55
+ data = Mihari::Entities::Alert.represent(alert)
56
+ puts JSON.pretty_generate(data.as_json)
29
57
  end
30
58
  end
31
59
  end
32
- end
33
60
 
34
- # @param [Mihari::Structs::Rule] rule
35
- #
36
- def update_rule(rule)
37
- force_overwrite = options["force_overwrite"] || false
38
- begin
39
- rule_model = Mihari::Rule.find(rule.id)
40
- has_change = rule_model.data != rule.data.deep_stringify_keys
41
- has_change_and_not_force_overwrite = has_change & !force_overwrite
61
+ def included(thor)
62
+ thor.class_eval do
63
+ desc "search [PATH]", "Search by a rule"
64
+ method_option :force_overwrite, type: :boolean, aliases: "-f", desc: "Force an overwrite the rule"
65
+ #
66
+ # Search by a rule
67
+ #
68
+ # @param [String] path_or_id
69
+ #
70
+ def search(path_or_id)
71
+ Mihari::Database.with_db_connection do
72
+ rule = Structs::Rule.from_path_or_id path_or_id
42
73
 
43
- confirm_message = "This operation will overwrite the rule in the database (Rule ID: #{rule.id}). Are you sure you want to update the rule? (y/n)"
44
- return if has_change_and_not_force_overwrite && !yes?(confirm_message)
45
- # update the rule
46
- rule.model.save
47
- rescue ActiveRecord::RecordNotFound
48
- # create a new rule
49
- rule.model.save
50
- end
51
- end
74
+ begin
75
+ rule.validate!
76
+ rescue RuleValidationError
77
+ return
78
+ end
52
79
 
53
- #
54
- # @param [Mihari::Structs::Rule] rule
55
- #
56
- def run_rule(rule)
57
- with_error_notification do
58
- alert = rule.analyzer.run
59
- if alert
60
- data = Mihari::Entities::Alert.represent(alert)
61
- puts JSON.pretty_generate(data.as_json)
62
- else
63
- Mihari.logger.info "There is no new alert created in the database"
80
+ force_overwrite = options["force_overwrite"] || false
81
+ wrapper = RuleWrapper.new(rule, force_overwrite: force_overwrite)
82
+
83
+ if wrapper.diff? && !force_overwrite
84
+ message = "There is diff in the rule (#{rule.id}). Are you sure you want to overwrite the rule? (y/n)"
85
+ return unless yes?(message)
86
+ end
87
+
88
+ wrapper.update_or_create
89
+ wrapper.run
90
+ end
91
+ end
64
92
  end
65
93
  end
66
94
  end
@@ -3,13 +3,15 @@
3
3
  module Mihari
4
4
  module Commands
5
5
  module Version
6
- def self.included(thor)
7
- thor.class_eval do
8
- map %w[--version -v] => :__print_version
6
+ class << self
7
+ def included(thor)
8
+ thor.class_eval do
9
+ map %w[--version -v] => :__print_version
9
10
 
10
- desc "--version, -v", "Print the version"
11
- def __print_version
12
- puts Mihari::VERSION
11
+ desc "--version, -v", "Print the version"
12
+ def __print_version
13
+ puts Mihari::VERSION
14
+ end
13
15
  end
14
16
  end
15
17
  end
@@ -3,29 +3,32 @@
3
3
  module Mihari
4
4
  module Commands
5
5
  module Web
6
- def self.included(thor)
7
- thor.class_eval do
8
- desc "web", "Launch the web app"
9
- method_option :port, type: :numeric, default: 9292, desc: "Hostname to listen on"
10
- method_option :host, type: :string, default: "localhost", desc: "Port to listen on"
11
- method_option :threads, type: :string, default: "0:5", desc: "min:max threads to use"
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)"
14
- method_option :hide_config_values, type: :boolean, default: false,
15
- desc: "Whether to hide config values or not"
16
- method_option :open, type: :boolean, default: true, desc: "Whether to open the app in browser or not"
17
- def web
18
- Mihari.config.hide_config_values = options["hide_config_values"]
19
- # set rack env as production
20
- ENV["RACK_ENV"] ||= "production"
21
- Mihari::App.run!(
22
- port: options["port"],
23
- host: options["host"],
24
- threads: options["threads"],
25
- verbose: options["verbose"],
26
- worker_timeout: options["worker_timeout"],
27
- open: options["open"]
28
- )
6
+ class << self
7
+ def included(thor)
8
+ thor.class_eval do
9
+ desc "web", "Launch the web app"
10
+ method_option :port, type: :numeric, default: 9292, desc: "Hostname to listen on"
11
+ method_option :host, type: :string, default: "localhost", desc: "Port to listen on"
12
+ method_option :threads, type: :string, default: "0:5", desc: "min:max threads to use"
13
+ method_option :verbose, type: :boolean, default: true, desc: "Report each request"
14
+ method_option :worker_timeout, type: :numeric, default: 60, desc: "Worker timeout value (in seconds)"
15
+ method_option :hide_config_values, type: :boolean, default: false,
16
+ desc: "Whether to hide config values or not"
17
+ method_option :open, type: :boolean, default: true, desc: "Whether to open the app in browser or not"
18
+ method_option :rack_env, type: :string, default: "production", desc: "Rack environment"
19
+ def web
20
+ Mihari.config.hide_config_values = options["hide_config_values"]
21
+ # set rack env as production
22
+ ENV["RACK_ENV"] ||= options["rack_env"]
23
+ Mihari::App.run!(
24
+ port: options["port"],
25
+ host: options["host"],
26
+ threads: options["threads"],
27
+ verbose: options["verbose"],
28
+ worker_timeout: options["worker_timeout"],
29
+ open: options["open"]
30
+ )
31
+ end
29
32
  end
30
33
  end
31
34
  end
@@ -6,7 +6,20 @@ module Mihari
6
6
  include Mixins::Configurable
7
7
  include Mixins::Retriable
8
8
 
9
- def initialize(*)
9
+ # @return [Array<Mihari::Artifact>]
10
+ attr_reader :artifacts
11
+
12
+ # @return [Mihari::Structs::Rule]
13
+ attr_reader :rule
14
+
15
+ #
16
+ # @param [Array<Mihari::Artifact>] artifacts
17
+ # @param [Mihari::Structs::Rule] rule
18
+ # @param [Hash] **_options
19
+ #
20
+ def initialize(artifacts:, rule:, **_options)
21
+ @artifacts = artifacts
22
+ @rule = rule
10
23
  end
11
24
 
12
25
  class << self
@@ -4,28 +4,21 @@ module Mihari
4
4
  module Emitters
5
5
  class Database < Base
6
6
  def valid?
7
- true
7
+ configured?
8
8
  end
9
9
 
10
10
  #
11
11
  # Create an alert
12
12
  #
13
- # @param [Arra<Mihari::Artifact>] artifacts
14
- # @param [Mihari::Structs::Rule] rule
15
- #
16
13
  # @return [Mihari::Alert]
17
14
  #
18
- def emit(artifacts:, rule:)
15
+ def emit
19
16
  return if artifacts.empty?
20
17
 
21
18
  tags = rule.tags.filter_map { |name| Tag.find_or_create_by(name: name) }.uniq
22
19
  taggings = tags.map { |tag| Tagging.new(tag_id: tag.id) }
23
20
 
24
- alert = Alert.new(
25
- artifacts: artifacts,
26
- taggings: taggings,
27
- rule_id: rule.id
28
- )
21
+ alert = Alert.new(artifacts: artifacts, taggings: taggings, rule_id: rule.id)
29
22
  alert.save
30
23
  alert
31
24
  end
@@ -9,11 +9,22 @@ module Mihari
9
9
  # @return [String, nil]
10
10
  attr_reader :api_key
11
11
 
12
- def initialize(*args, **kwargs)
13
- super(*args, **kwargs)
12
+ # @return [Array<Mihari::Artifact>]
13
+ attr_reader :artifacts
14
14
 
15
- @url = kwargs[:url] || Mihari.config.misp_url
16
- @api_key = kwargs[:api_key] || Mihari.config.misp_api_key
15
+ # @return [Mihari::Structs::Rule]
16
+ attr_reader :rule
17
+
18
+ #
19
+ # @param [Array<Mihari::Artifact>] artifacts
20
+ # @param [Mihari::Structs::Rule] rule
21
+ # @param [Hash] **options
22
+ #
23
+ def initialize(artifacts:, rule:, **options)
24
+ super(artifacts: artifacts, rule: rule, **options)
25
+
26
+ @url = options[:url] || Mihari.config.misp_url
27
+ @api_key = options[:api_key] || Mihari.config.misp_api_key
17
28
  end
18
29
 
19
30
  # @return [Boolean]
@@ -40,7 +51,7 @@ module Mihari
40
51
  #
41
52
  # @return [::MISP::Event]
42
53
  #
43
- def emit(rule:, artifacts:, **_options)
54
+ def emit
44
55
  return if artifacts.empty?
45
56
 
46
57
  client.create_event({