mihari 5.2.1 → 5.2.3

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -0
  3. data/lib/mihari/analyzers/base.rb +20 -115
  4. data/lib/mihari/analyzers/binaryedge.rb +0 -1
  5. data/lib/mihari/analyzers/censys.rb +26 -3
  6. data/lib/mihari/analyzers/circl.rb +1 -1
  7. data/lib/mihari/analyzers/onyphe.rb +1 -1
  8. data/lib/mihari/analyzers/passivetotal.rb +1 -1
  9. data/lib/mihari/analyzers/rule.rb +122 -75
  10. data/lib/mihari/analyzers/shodan.rb +1 -1
  11. data/lib/mihari/analyzers/urlscan.rb +6 -9
  12. data/lib/mihari/analyzers/virustotal_intelligence.rb +1 -6
  13. data/lib/mihari/cli/main.rb +2 -2
  14. data/lib/mihari/clients/base.rb +1 -1
  15. data/lib/mihari/commands/database.rb +12 -11
  16. data/lib/mihari/commands/rule.rb +47 -45
  17. data/lib/mihari/commands/search.rb +88 -0
  18. data/lib/mihari/commands/version.rb +8 -6
  19. data/lib/mihari/commands/web.rb +26 -23
  20. data/lib/mihari/emitters/base.rb +14 -1
  21. data/lib/mihari/emitters/database.rb +3 -10
  22. data/lib/mihari/emitters/misp.rb +16 -5
  23. data/lib/mihari/emitters/slack.rb +13 -15
  24. data/lib/mihari/emitters/the_hive.rb +17 -19
  25. data/lib/mihari/emitters/webhook.rb +23 -23
  26. data/lib/mihari/enrichers/whois.rb +1 -0
  27. data/lib/mihari/feed/parser.rb +1 -0
  28. data/lib/mihari/feed/reader.rb +29 -14
  29. data/lib/mihari/mixins/configurable.rb +13 -4
  30. data/lib/mihari/mixins/error_notification.rb +0 -2
  31. data/lib/mihari/models/artifact.rb +1 -1
  32. data/lib/mihari/schemas/rule.rb +2 -17
  33. data/lib/mihari/structs/censys.rb +226 -56
  34. data/lib/mihari/structs/config.rb +48 -18
  35. data/lib/mihari/structs/google_public_dns.rb +56 -14
  36. data/lib/mihari/structs/greynoise.rb +122 -29
  37. data/lib/mihari/structs/ipinfo.rb +40 -0
  38. data/lib/mihari/structs/onyphe.rb +112 -26
  39. data/lib/mihari/structs/rule.rb +4 -2
  40. data/lib/mihari/structs/shodan.rb +189 -47
  41. data/lib/mihari/structs/urlscan.rb +123 -20
  42. data/lib/mihari/structs/virustotal_intelligence.rb +129 -26
  43. data/lib/mihari/type_checker.rb +10 -8
  44. data/lib/mihari/version.rb +1 -1
  45. data/lib/mihari.rb +1 -0
  46. data/mihari.gemspec +11 -10
  47. metadata +35 -36
  48. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -43
  49. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -15
  50. data/.github/workflows/test.yml +0 -90
  51. data/config/pre_commit.yml +0 -3
  52. data/docker/Dockerfile +0 -14
  53. data/examples/ipinfo_hosted_domains.rb +0 -45
  54. data/images/Tines-Full_Logo-Tines_Black.png +0 -0
  55. data/images/alert.png +0 -0
  56. data/images/logo.png +0 -0
  57. data/images/misp.png +0 -0
  58. data/images/overview.jpg +0 -0
  59. data/images/slack.png +0 -0
  60. data/images/tines.png +0 -0
  61. data/images/web_alerts.png +0 -0
  62. data/images/web_config.png +0 -0
  63. data/lib/mihari/commands/searcher.rb +0 -61
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5ce7e22d3d54cc3e66fec62b494ce5f9def9107129513cd51803c71b8ae48b18
4
- data.tar.gz: fd85f33a666a77a8b43d12dc3234ab2656cd843c8f7e23025c1a5f076947fbcf
3
+ metadata.gz: 2a66fb2d71bcae401062277921a8ade6a3e9e9d961b193a80deacd3a8a934d4c
4
+ data.tar.gz: b4dcccfa58019f819241f8679b5d1ae846002f0a95307cd95856f2b6d04a6dd1
5
5
  SHA512:
6
- metadata.gz: a17809e3c6f52e7d37f6df2fc92b0ad5a1d134556e6ef13cb64b3802074a44f2ecd0c2475ab632e5da0f57e7c68a44fca15f722f31124fbb9e8af512f46aa2ac
7
- data.tar.gz: d777cac77ceeb2a98c8f37a8e001d9240403b47a1e2326a6f1bc42c3cf48e7813dd69ab2260ce5a29a34b7b9b3e0ef54fa541785f8b5dbc82b787381e549525e
6
+ metadata.gz: e215400c8dce2b864bc26a951ed8ea35757441e7d25bdba6c66632d6716991bad202170f9984d09b7a709f6c9aceb731e5b40396efe621ffc55810a10db45db2
7
+ data.tar.gz: 745522a9cefaed75e5b266a429dea7afc0efe34f26f8591af18e3bc27a0746659ed8d64f6aa64f6edc0f9195acbf26e4a7ee078c052bab760f985da779c4e6e4
data/.rubocop.yml CHANGED
@@ -8,3 +8,5 @@ Metrics/BlockLength:
8
8
  - "*.gemspec"
9
9
  Metrics/ClassLength:
10
10
  Enabled: false
11
+ Metrics/MethodLength:
12
+ Max: 20
@@ -5,32 +5,34 @@ module Mihari
5
5
  class Base
6
6
  extend Dry::Initializer
7
7
 
8
- option :rule, default: proc {}
9
-
10
8
  include Mixins::Configurable
11
9
  include Mixins::Retriable
12
10
 
13
- # @return [Mihari::Structs::Rule, nil]
14
- attr_reader :rule
15
-
16
- def initialize(*args, **kwargs)
17
- super(*args, **kwargs)
18
-
19
- @base_time = Time.now.utc
11
+ # @return [Array<String>, Array<Mihari::Artifact>]
12
+ def artifacts
13
+ raise NotImplementedError, "You must implement #{self.class}##{__method__}"
20
14
  end
21
15
 
22
16
  #
23
- # Load/overwrite rule
17
+ # Normalize artifacts
18
+ # - Convert data (string) into an artifact
19
+ # - Reject an invalid artifact
24
20
  #
25
- # @param [String] path_or_id
21
+ # @return [Array<Mihari::Artifact>]
26
22
  #
27
- def load_rule(path_or_id)
28
- @rule = Structs::Rule.from_path_or_id path_or_id
29
- end
30
-
31
- # @return [Array<String>, Array<Mihari::Artifact>]
32
- def artifacts
33
- raise NotImplementedError, "You must implement #{self.class}##{__method__}"
23
+ def normalized_artifacts
24
+ retry_on_error do
25
+ @normalized_artifacts ||= artifacts.compact.sort.map do |artifact|
26
+ # No need to set data_type manually
27
+ # It is set automatically in #initialize
28
+ artifact = artifact.is_a?(Artifact) ? artifact : Artifact.new(data: artifact)
29
+ artifact
30
+ end.select(&:valid?).uniq(&:data).map do |artifact|
31
+ # set source
32
+ artifact.source = source
33
+ artifact
34
+ end
35
+ end
34
36
  end
35
37
 
36
38
  # @return [String]
@@ -43,109 +45,12 @@ module Mihari
43
45
  self.class.to_s.split("::").last
44
46
  end
45
47
 
46
- #
47
- # Set artifacts & run emitters in parallel
48
- #
49
- # @return [Mihari::Alert, nil]
50
- #
51
- def run
52
- raise ConfigurationError, "#{class_name} is not configured correctly" unless configured?
53
-
54
- alert_or_something = bulk_emit
55
- # returns Mihari::Alert created by the database emitter
56
- alert_or_something.find { |res| res.is_a?(Mihari::Alert) }
57
- end
58
-
59
- #
60
- # Bulk emit
61
- #
62
- # @return [Array<Mihari::Alert>]
63
- #
64
- def bulk_emit
65
- Parallel.map(valid_emitters) { |emitter| emit emitter }.compact
66
- end
67
-
68
- #
69
- # Emit an alert
70
- #
71
- # @param [Mihari::Emitters::Base] emitter
72
- #
73
- # @return [Mihari::Alert, nil]
74
- #
75
- def emit(emitter)
76
- return if enriched_artifacts.empty?
77
-
78
- alert_or_something = emitter.run(artifacts: enriched_artifacts, rule: rule)
79
-
80
- Mihari.logger.info "Emission by #{emitter.class} is succedded"
81
-
82
- alert_or_something
83
- rescue StandardError => e
84
- Mihari.logger.info "Emission by #{emitter.class} is failed: #{e}"
85
- end
86
-
87
48
  class << self
88
49
  def inherited(child)
89
50
  super
90
51
  Mihari.analyzers << child
91
52
  end
92
53
  end
93
-
94
- #
95
- # Normalize artifacts
96
- # - Convert data (string) into an artifact
97
- # - Set rule ID
98
- # - Reject an invalid artifact
99
- # - Uniquefy artifacts by data
100
- #
101
- # @return [Array<Mihari::Artifact>]
102
- #
103
- def normalized_artifacts
104
- @normalized_artifacts ||= artifacts.compact.sort.map do |artifact|
105
- # No need to set data_type manually
106
- # It is set automatically in #initialize
107
- artifact = artifact.is_a?(Artifact) ? artifact : Artifact.new(data: artifact, source: source)
108
- artifact.rule_id = rule&.id
109
- artifact
110
- end.select(&:valid?).uniq(&:data)
111
- end
112
-
113
- private
114
-
115
- #
116
- # Uniquefy artifacts (assure rule level uniqueness)
117
- #
118
- # @return [Array<Mihari::Artifact>]
119
- #
120
- def unique_artifacts
121
- @unique_artifacts ||= normalized_artifacts.select do |artifact|
122
- artifact.unique?(base_time: @base_time, artifact_lifetime: rule&.artifact_lifetime)
123
- end
124
- end
125
-
126
- #
127
- # Enriched artifacts
128
- #
129
- # @return [Array<Mihari::Artifact>]
130
- #
131
- def enriched_artifacts
132
- @enriched_artifacts ||= Parallel.map(unique_artifacts) do |artifact|
133
- artifact.enrich_all
134
- artifact
135
- end
136
- end
137
-
138
- #
139
- # Select valid emitters
140
- #
141
- # @return [Array<Mihari::Emitters::Base>]
142
- #
143
- def valid_emitters
144
- @valid_emitters ||= Mihari.emitters.filter_map do |klass|
145
- emitter = klass.new
146
- emitter.valid? ? emitter : nil
147
- end.compact
148
- end
149
54
  end
150
55
  end
151
56
  end
@@ -42,7 +42,6 @@ module Mihari
42
42
  #
43
43
  # Search with pagination
44
44
  #
45
- # @param [String] query
46
45
  # @param [Integer] page
47
46
  #
48
47
  # @return [Hash]
@@ -26,15 +26,23 @@ module Mihari
26
26
  @secret = kwargs[:secret] || Mihari.config.censys_secret
27
27
  end
28
28
 
29
+ #
30
+ # @return [Array<Mihari::Artifact>]
31
+ #
29
32
  def artifacts
30
33
  artifacts = []
31
34
 
32
35
  cursor = nil
33
36
  loop do
34
37
  response = client.search(query, cursor: cursor)
35
- artifacts << response.result.to_artifacts(source)
38
+ artifacts << response.result.to_artifacts
36
39
  cursor = response.result.links.next
37
- break if cursor == ""
40
+ # NOTE: Censys's search API is unstable recently
41
+ # it may returns empty links or empty string cursors
42
+ # - Empty links: "links": {}
43
+ # - Empty cursors: "links": { "next": "", "prev": "" }
44
+ # So it needs to check both cases
45
+ break if cursor.nil? || cursor.empty?
38
46
 
39
47
  # sleep #{interval} seconds to avoid the rate limitation (if it is set)
40
48
  sleep interval
@@ -43,24 +51,39 @@ module Mihari
43
51
  artifacts.flatten.uniq(&:data)
44
52
  end
45
53
 
54
+ #
55
+ # @return [Boolean]
56
+ #
46
57
  def configured?
47
- configuration_keys.all? { |key| Mihari.config.send(key) } || (id? && secret?)
58
+ configuration_keys? || (id? && secret?)
48
59
  end
49
60
 
50
61
  private
51
62
 
63
+ #
64
+ # @return [Array<String>]
65
+ #
52
66
  def configuration_keys
53
67
  %w[censys_id censys_secret]
54
68
  end
55
69
 
70
+ #
71
+ # @return [Mihari::Clients::Censys]
72
+ #
56
73
  def client
57
74
  @client ||= Clients::Censys.new(id: id, secret: secret)
58
75
  end
59
76
 
77
+ #
78
+ # @return [Boolean]
79
+ #
60
80
  def id?
61
81
  !id.nil?
62
82
  end
63
83
 
84
+ #
85
+ # @return [Boolean]
86
+ #
64
87
  def secret?
65
88
  !secret.nil?
66
89
  end
@@ -41,7 +41,7 @@ module Mihari
41
41
  end
42
42
 
43
43
  def configured?
44
- configuration_keys.all? { |key| Mihari.config.send(key) } || (username? && password?)
44
+ configuration_keys? || (username? && password?)
45
45
  end
46
46
 
47
47
  private
@@ -28,7 +28,7 @@ module Mihari
28
28
  responses = search
29
29
  return [] unless responses
30
30
 
31
- responses.map { |response| response.to_artifacts(source) }.flatten
31
+ responses.map(&:to_artifacts).flatten
32
32
  end
33
33
 
34
34
  private
@@ -43,7 +43,7 @@ module Mihari
43
43
  end
44
44
 
45
45
  def configured?
46
- configuration_keys.all? { |key| Mihari.config.send(key) } || (username? && api_key?)
46
+ configuration_keys? || (username? && api_key?)
47
47
  end
48
48
 
49
49
  private
@@ -34,59 +34,40 @@ module Mihari
34
34
  "webhook" => Emitters::Webhook
35
35
  }.freeze
36
36
 
37
- # @return [Mihari::Structs::Rule]
38
- attr_reader :rule
39
-
40
- class Rule < Base
37
+ class Rule
41
38
  include Mixins::FalsePositive
42
39
 
43
- def initialize(**kwargs)
44
- super(**kwargs)
40
+ # @return [Mihari::Structs::Rule]
41
+ attr_reader :rule
42
+
43
+ # @return [Time]
44
+ attr_reader :base_time
45
+
46
+ #
47
+ # @param [Mihari::Structs::Rule] rule
48
+ #
49
+ def initialize(rule:)
50
+ @rule = rule
51
+ @base_time = Time.now.utc
45
52
 
46
53
  validate_analyzer_configurations
47
54
  end
48
55
 
49
56
  #
50
- # Returns a list of artifacts matched with queries
57
+ # Returns a list of artifacts matched with queries/analyzers
51
58
  #
52
59
  # @return [Array<Mihari::Artifact>]
53
60
  #
54
61
  def artifacts
55
- artifacts = []
56
-
57
- rule.queries.each do |original_params|
58
- parmas = original_params.deep_dup
59
-
60
- analyzer_name = parmas[:analyzer]
61
- klass = get_analyzer_class(analyzer_name)
62
-
63
- query = parmas[:query]
64
-
65
- # set interval in the top level
66
- options = parmas[:options] || {}
67
- interval = options[:interval]
68
-
69
- parmas[:interval] = interval if interval
70
-
71
- # set rule
72
- parmas[:rule] = rule
73
-
74
- analyzer = klass.new(query, **parmas)
75
-
76
- # Use #normalized_artifacts method to get atrifacts as Array<Mihari::Artifact>
77
- # So Mihari::Artifact object has "source" attribute (e.g. "Shodan")
78
- artifacts << analyzer.normalized_artifacts
79
- end
80
-
81
- artifacts.flatten
62
+ analyzers.flat_map(&:normalized_artifacts)
82
63
  end
83
64
 
84
65
  #
85
66
  # Normalize artifacts
86
- # - Uniquefy artifacts by #uniq(&:data)
87
- # - Reject an invalid artifact (for just in case)
67
+ # - Reject invalid artifacts (for just in case)
88
68
  # - Select artifacts with allowed data types
89
- # - Reject artifacts with disallowed data values
69
+ # - Reject artifacts with false positive values
70
+ # - Set rule ID
90
71
  #
91
72
  # @return [Array<Mihari::Artifact>]
92
73
  #
@@ -95,6 +76,20 @@ module Mihari
95
76
  rule.data_types.include? artifact.data_type
96
77
  end.reject do |artifact|
97
78
  falsepositive? artifact.data
79
+ end.map do |artifact|
80
+ artifact.rule_id = rule.id
81
+ artifact
82
+ end
83
+ end
84
+
85
+ #
86
+ # Uniquify artifacts (assure rule level uniqueness)
87
+ #
88
+ # @return [Array<Mihari::Artifact>]
89
+ #
90
+ def unique_artifacts
91
+ @unique_artifacts ||= normalized_artifacts.select do |artifact|
92
+ artifact.unique?(base_time: base_time, artifact_lifetime: rule.artifact_lifetime)
98
93
  end
99
94
  end
100
95
 
@@ -105,39 +100,105 @@ module Mihari
105
100
  #
106
101
  def enriched_artifacts
107
102
  @enriched_artifacts ||= Parallel.map(unique_artifacts) do |artifact|
108
- rule.enrichers.each do |enricher|
109
- artifact.enrich_by_enricher(enricher[:enricher])
110
- end
111
-
103
+ rule.enrichers.each { |enricher| artifact.enrich_by_enricher enricher[:enricher] }
112
104
  artifact
113
105
  end
114
106
  end
115
107
 
116
108
  #
117
- # Normalized disallowed data values
109
+ # Bulk emit
118
110
  #
119
- # @return [Array<Regexp, String>]
111
+ # @return [Array<Mihari::Alert>]
120
112
  #
121
- def normalized_falsepositives
122
- @normalized_falsepositives ||= rule.falsepositives.map { |v| normalize_falsepositive v }
113
+ def bulk_emit
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
123
123
  end
124
124
 
125
+ #
126
+ # Set artifacts & run emitters in parallel
127
+ #
128
+ # @return [Mihari::Alert, nil]
129
+ #
130
+ def run
131
+ # memoize enriched artifacts
132
+ enriched_artifacts
133
+
134
+ alert_or_something = bulk_emit
135
+ # returns Mihari::Alert created by the database emitter
136
+ alert_or_something.find { |res| res.is_a?(Mihari::Alert) }
137
+ end
138
+
139
+ private
140
+
125
141
  #
126
142
  # Check whether a value is a falsepositive value or not
127
143
  #
128
144
  # @return [Boolean]
129
145
  #
130
146
  def falsepositive?(value)
131
- return true if normalized_falsepositives.include?(value)
147
+ return true if rule.falsepositives.include?(value)
132
148
 
133
- normalized_falsepositives.select do |falsepositive|
149
+ rule.falsepositives.select do |falsepositive|
134
150
  falsepositive.is_a?(Regexp)
135
151
  end.any? do |falseposistive|
136
152
  falseposistive.match?(value)
137
153
  end
138
154
  end
139
155
 
140
- private
156
+ #
157
+ # Deep copied queries
158
+ #
159
+ # @return [Array<Hash>]
160
+ #
161
+ def queries
162
+ rule.queries.map(&:deep_dup)
163
+ end
164
+
165
+ #
166
+ # Get analyzer class
167
+ #
168
+ # @param [String] analyzer_name
169
+ #
170
+ # @return [Class<Mihari::Analyzers::Base>] analyzer class
171
+ #
172
+ def get_analyzer_class(analyzer_name)
173
+ analyzer = ANALYZER_TO_CLASS[analyzer_name]
174
+ return analyzer if analyzer
175
+
176
+ raise ArgumentError, "#{analyzer_name} is not supported"
177
+ end
178
+
179
+ #
180
+ # @return [Array<Mihari::Analyzers::Base>] <description>
181
+ #
182
+ def analyzers
183
+ @analyzers ||= queries.map do |params|
184
+ analyzer_name = params[:analyzer]
185
+ klass = get_analyzer_class(analyzer_name)
186
+
187
+ # set interval in the top level
188
+ options = params[:options] || {}
189
+ interval = options[:interval]
190
+ params[:interval] = interval if interval
191
+
192
+ # set rule
193
+ params[:rule] = rule
194
+ query = params[:query]
195
+
196
+ analyzer = klass.new(query, **params)
197
+ raise ConfigurationError, "#{analyzer.source} is not configured correctly" unless analyzer.configured?
198
+
199
+ analyzer
200
+ end
201
+ end
141
202
 
142
203
  #
143
204
  # Get emitter class
@@ -153,48 +214,34 @@ module Mihari
153
214
  raise ArgumentError, "#{emitter_name} is not supported"
154
215
  end
155
216
 
156
- def valid_emitters
157
- @valid_emitters ||= rule.emitters.filter_map do |original_params|
158
- params = original_params.deep_dup
159
-
217
+ #
218
+ # Deep copied emitters
219
+ #
220
+ # @return [Array<Mihari::Emitters::Base>]
221
+ #
222
+ def emitters
223
+ rule.emitters.map(&:deep_dup).map do |params|
160
224
  name = params[:emitter]
161
225
  params.delete(:emitter)
162
226
 
163
227
  klass = get_emitter_class(name)
164
- emitter = klass.new(**params)
165
-
166
- emitter.valid? ? emitter : nil
228
+ klass.new(artifacts: enriched_artifacts, rule: rule, **params)
167
229
  end
168
230
  end
169
231
 
170
232
  #
171
- # Get analyzer class
172
- #
173
- # @param [String] analyzer_name
174
- #
175
- # @return [Class<Mihari::Analyzers::Base>] analyzer class
233
+ # @return [Array<Mihari::Emitters::Base>]
176
234
  #
177
- def get_analyzer_class(analyzer_name)
178
- analyzer = ANALYZER_TO_CLASS[analyzer_name]
179
- return analyzer if analyzer
180
-
181
- raise ArgumentError, "#{analyzer_name} is not supported"
235
+ def valid_emitters
236
+ @valid_emitters ||= emitters.select(&:valid?)
182
237
  end
183
238
 
184
239
  #
185
240
  # Validate configuration of analyzers
186
241
  #
187
242
  def validate_analyzer_configurations
188
- rule.queries.each do |params|
189
- analyzer_name = params[:analyzer]
190
- klass = get_analyzer_class(analyzer_name)
191
-
192
- instance = klass.new("dummy")
193
- unless instance.configured?
194
- klass_name = klass.to_s.split("::").last
195
- raise ConfigurationError, "#{klass_name} is not configured correctly"
196
- end
197
- end
243
+ # memoize analyzers & raise ConfigurationError if there is an analyzer which is not configured
244
+ analyzers
198
245
  end
199
246
  end
200
247
  end
@@ -26,7 +26,7 @@ module Mihari
26
26
  results = search
27
27
  return [] if results.empty?
28
28
 
29
- results.map { |result| result.to_artifacts(source) }.flatten.uniq(&:data)
29
+ results.map(&:to_artifacts).flatten.uniq(&:data)
30
30
  end
31
31
 
32
32
  private
@@ -36,15 +36,12 @@ module Mihari
36
36
 
37
37
  def artifacts
38
38
  responses = search
39
- results = responses.map(&:results).flatten
40
-
41
- allowed_data_types.map do |type|
42
- results.filter_map do |result|
43
- page = result.page
44
- data = page.send(type.to_sym)
45
- data.nil? ? nil : Artifact.new(data: data, source: source, metadata: result)
46
- end
47
- end.flatten
39
+ # @type [Array<Mihari::Artifact>]
40
+ artifacts = responses.map { |res| res.to_artifacts }.flatten
41
+
42
+ artifacts.select do |artifact|
43
+ allowed_data_types.include? artifact.data_type
44
+ end
48
45
  end
49
46
 
50
47
  private
@@ -25,12 +25,7 @@ module Mihari
25
25
  end
26
26
 
27
27
  def artifacts
28
- responses = search_with_cursor
29
- responses.map do |response|
30
- response.data.map do |datum|
31
- Artifact.new(data: datum.value, source: source, metadata: datum.metadata)
32
- end
33
- end.flatten
28
+ search_with_cursor.map(&:to_artifacts).flatten
34
29
  end
35
30
 
36
31
  private
@@ -3,7 +3,7 @@
3
3
  require "thor"
4
4
 
5
5
  # Commands
6
- require "mihari/commands/searcher"
6
+ require "mihari/commands/search"
7
7
  require "mihari/commands/version"
8
8
  require "mihari/commands/web"
9
9
  require "mihari/commands/database"
@@ -17,7 +17,7 @@ require "mihari/cli/rule"
17
17
  module Mihari
18
18
  module CLI
19
19
  class Main < Base
20
- include Mihari::Commands::Searcher
20
+ include Mihari::Commands::Search
21
21
  include Mihari::Commands::Version
22
22
  include Mihari::Commands::Web
23
23
 
@@ -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