mihari 5.2.2 → 5.2.4

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 (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({