mihari 5.2.2 → 5.2.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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({
@@ -121,11 +121,16 @@ module Mihari
121
121
  # @return [String]
122
122
  attr_reader :username
123
123
 
124
- def initialize(*args, **kwargs)
125
- super(*args, **kwargs)
124
+ #
125
+ # @param [Array<Mihari::Artifact>] artifacts
126
+ # @param [Mihari::Structs::Rule] rule
127
+ # @param [Hash] **_options
128
+ #
129
+ def initialize(artifacts:, rule:, **options)
130
+ super(artifacts: artifacts, rule: rule, **options)
126
131
 
127
- @webhook_url = kwargs[:webhook_url] || Mihari.config.slack_webhook_url
128
- @channel = kwargs[:channel] || Mihari.config.slack_channel || DEFAULT_CHANNEL
132
+ @webhook_url = options[:webhook_url] || Mihari.config.slack_webhook_url
133
+ @channel = options[:channel] || Mihari.config.slack_channel || DEFAULT_CHANNEL
129
134
  @username = DEFAULT_USERNAME
130
135
  end
131
136
 
@@ -152,13 +157,11 @@ module Mihari
152
157
  end
153
158
 
154
159
  #
155
- # Build attachements
156
- #
157
- # @param [Array<Mihari::Artifact>] artifacts
160
+ # Build attachments
158
161
  #
159
162
  # @return [Array<Mihari::Emitters::Attachment>]
160
163
  #
161
- def to_attachments(artifacts)
164
+ def attachments
162
165
  artifacts.map do |artifact|
163
166
  Attachment.new(data: artifact.data, data_type: artifact.data_type).to_a
164
167
  end.flatten
@@ -167,11 +170,9 @@ module Mihari
167
170
  #
168
171
  # Build a text
169
172
  #
170
- # @param [Mihari::Structs::Rule] rule
171
- #
172
173
  # @return [String]
173
174
  #
174
- def to_text(rule)
175
+ def text
175
176
  tags = rule.tags
176
177
  tags = ["N/A"] if tags.empty?
177
178
 
@@ -182,12 +183,9 @@ module Mihari
182
183
  ].join("\n")
183
184
  end
184
185
 
185
- def emit(rule:, artifacts:, **_options)
186
+ def emit
186
187
  return if artifacts.empty?
187
188
 
188
- attachments = to_attachments(artifacts)
189
- text = to_text(rule)
190
-
191
189
  notifier.post(text: text, attachments: attachments, mrkdwn: true)
192
190
  end
193
191
 
@@ -12,12 +12,17 @@ module Mihari
12
12
  # @return [String, nil]
13
13
  attr_reader :api_version
14
14
 
15
- def initialize(*args, **kwargs)
16
- super(*args, **kwargs)
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
+ super(artifacts: artifacts, rule: rule, **options)
17
22
 
18
- @url = kwargs[:url] || Mihari.config.thehive_url
19
- @api_key = kwargs[:api_key] || Mihari.config.thehive_api_key
20
- @api_version = kwargs[:api_version] || Mihari.config.thehive_api_version
23
+ @url = options[:url] || Mihari.config.thehive_url
24
+ @api_key = options[:api_key] || Mihari.config.thehive_api_key
25
+ @api_version = options[:api_version] || Mihari.config.thehive_api_version
21
26
  end
22
27
 
23
28
  # @return [Boolean]
@@ -39,15 +44,11 @@ module Mihari
39
44
  #
40
45
  # Create a Hive alert
41
46
  #
42
- # @param [Arra<Mihari::Artifact>] artifacts
43
- # @param [Mihari::Structs::Rule] rule
44
- #
45
47
  # @return [::MISP::Event]
46
48
  #
47
- def emit(rule:, artifacts:, **_options)
49
+ def emit
48
50
  return if artifacts.empty?
49
51
 
50
- payload = payload(rule: rule, artifacts: artifacts)
51
52
  client.alert(payload)
52
53
  end
53
54
 
@@ -102,18 +103,15 @@ module Mihari
102
103
  #
103
104
  # Build payload for alert
104
105
  #
105
- # @param [Arra<Mihari::Artifact>] artifacts
106
- # @param [Mihari::Structs::Rule] rule
107
- #
108
- # @return [<Type>] <description>
106
+ # @return [Hash]
109
107
  #
110
- def payload(rule:, artifacts:)
111
- return v4_payload(rule: rule, artifacts: artifacts) if normalized_api_version.nil?
108
+ def payload
109
+ return v4_payload if normalized_api_version.nil?
112
110
 
113
- v5_payload(rule: rule, artifacts: artifacts)
111
+ v5_payload
114
112
  end
115
113
 
116
- def v4_payload(rule:, artifacts:)
114
+ def v4_payload
117
115
  {
118
116
  title: rule.title,
119
117
  description: rule.description,
@@ -130,7 +128,7 @@ module Mihari
130
128
  }
131
129
  end
132
130
 
133
- def v5_payload(rule:, artifacts:)
131
+ def v5_payload
134
132
  {
135
133
  title: rule.title,
136
134
  description: rule.description,
@@ -55,30 +55,26 @@ module Mihari
55
55
  # @return [String, nil]
56
56
  attr_reader :template
57
57
 
58
- def initialize(*args, **kwargs)
59
- super(*args, **kwargs)
60
-
61
- url = kwargs[:url]
62
- headers = kwargs[:headers] || {}
63
- method = kwargs[:method] || "POST"
64
- template = kwargs[:template]
65
-
66
- @url = Addressable::URI.parse(url)
67
- @headers = headers
68
- @method = method
69
- @template = template
58
+ #
59
+ # @param [Array<Mihari::Artifact>] artifacts
60
+ # @param [Mihari::Structs::Rule] rule
61
+ # @param [Hash] **options
62
+ #
63
+ def initialize(artifacts:, rule:, **options)
64
+ super(artifacts: artifacts, rule: rule, **options)
65
+
66
+ @url = Addressable::URI.parse(options[:url])
67
+ @headers = options[:headers] || {}
68
+ @method = options[:method] || "POST"
69
+ @template = options[:template]
70
70
  end
71
71
 
72
- def emit(artifacts:, rule:)
72
+ def emit
73
73
  return if artifacts.empty?
74
74
 
75
- res = nil
76
-
77
- payload_ = payload_as_string(artifacts: artifacts, rule: rule)
78
- payload = JSON.parse(payload_)
79
-
80
75
  client = Mihari::HTTP.new(url, headers: headers)
81
76
 
77
+ res = nil
82
78
  case method
83
79
  when "GET"
84
80
  res = client.get
@@ -100,13 +96,10 @@ module Mihari
100
96
  #
101
97
  # Convert payload into string
102
98
  #
103
- # @param [Array<Mihari::Artifact>] artifacts
104
- # @param [Mihari::Structs::Rule] rule
105
- #
106
99
  # @return [String]
107
100
  #
108
- def payload_as_string(artifacts:, rule:)
109
- @payload_as_string ||= [].tap do |out|
101
+ def payload_as_string
102
+ [].tap do |out|
110
103
  options = {}
111
104
  options[:template] = File.read(template) unless template.nil?
112
105
 
@@ -118,6 +111,13 @@ module Mihari
118
111
  out << payload_template.result
119
112
  end.first
120
113
  end
114
+
115
+ #
116
+ # @return [Hash]
117
+ #
118
+ def payload
119
+ JSON.parse payload_as_string
120
+ end
121
121
  end
122
122
  end
123
123
  end
@@ -5,6 +5,7 @@ require "whois-parser"
5
5
  module Mihari
6
6
  module Enrichers
7
7
  class Whois < Base
8
+ # @type [Hash]
8
9
  @memo = {}
9
10
 
10
11
  # @return [Boolean]
@@ -5,6 +5,7 @@ require "jr/cli/core_ext"
5
5
  module Mihari
6
6
  module Feed
7
7
  class Parser
8
+ # @return [Array<Hash>, Array<Array<String>>]
8
9
  attr_reader :data
9
10
 
10
11
  #
@@ -6,7 +6,23 @@ require "insensitive_hash"
6
6
  module Mihari
7
7
  module Feed
8
8
  class Reader
9
- attr_reader :url, :headers, :params, :json, :data, :method
9
+ # @return [String]
10
+ attr_reader :url
11
+
12
+ # @return [Hash]
13
+ attr_reader :headers
14
+
15
+ # @return [Hash, nil]
16
+ attr_reader :params
17
+
18
+ # @return [Hash, nil]
19
+ attr_reader :json
20
+
21
+ # @return [Hash, nil]
22
+ attr_reader :data
23
+
24
+ # @return [String]
25
+ attr_reader :method
10
26
 
11
27
  def initialize(url, headers: {}, method: "GET", params: nil, json: nil, data: nil)
12
28
  @url = Addressable::URI.parse(url)
@@ -20,6 +36,9 @@ module Mihari
20
36
  headers["content-type"] = "application/json" unless json.nil?
21
37
  end
22
38
 
39
+ #
40
+ # @return [Array<Hash>]
41
+ #
23
42
  def read
24
43
  return read_file(url.path) if url.scheme == "file"
25
44
 
@@ -33,11 +52,9 @@ module Mihari
33
52
 
34
53
  body = res.body
35
54
  content_type = res["Content-Type"].to_s
36
- if content_type.include?("application/json")
37
- convert_as_json(body)
38
- else
39
- convert_as_csv(body)
40
- end
55
+ return convert_as_json(body) if content_type.include?("application/json")
56
+
57
+ convert_as_csv(body)
41
58
  end
42
59
 
43
60
  #
@@ -48,10 +65,10 @@ module Mihari
48
65
  # @return [Array<Hash>]
49
66
  #
50
67
  def convert_as_json(text)
51
- data = JSON.parse(text, symbolize_names: true)
52
- return data if data.is_a?(Array)
68
+ parsed = JSON.parse(text, symbolize_names: true)
69
+ return parsed if parsed.is_a?(Array)
53
70
 
54
- [data]
71
+ [parsed]
55
72
  end
56
73
 
57
74
  #
@@ -77,11 +94,9 @@ module Mihari
77
94
  def read_file(path)
78
95
  text = File.read(path)
79
96
 
80
- if path.end_with?(".json")
81
- convert_as_json text
82
- else
83
- convert_as_csv text
84
- end
97
+ return convert_as_json(text) if path.end_with?(".json")
98
+
99
+ convert_as_csv text
85
100
  end
86
101
  end
87
102
  end
@@ -4,14 +4,23 @@ module Mihari
4
4
  module Mixins
5
5
  module Configurable
6
6
  #
7
- # Check whether it is configured or not
7
+ # Check whether there are configuration key-values or not
8
8
  #
9
9
  # @return [Boolean]
10
10
  #
11
- def configured?
11
+ def configuration_keys?
12
12
  return true if configuration_keys.empty?
13
13
 
14
- configuration_keys.all? { |key| Mihari.config.send(key) } || api_key?
14
+ configuration_keys.all? { |key| Mihari.config.send(key) }
15
+ end
16
+
17
+ #
18
+ # Check whether it is configured or not
19
+ #
20
+ # @return [Boolean]
21
+ #
22
+ def configured?
23
+ configuration_keys? || api_key?
15
24
  end
16
25
 
17
26
  #
@@ -32,7 +41,7 @@ module Mihari
32
41
  #
33
42
  # Configuration keys
34
43
  #
35
- # @return [Array<String>] A list of cofiguration keys
44
+ # @return [Array<String>] A list of configuration keys
36
45
  #
37
46
  def configuration_keys
38
47
  []
@@ -22,16 +22,18 @@ module Mihari
22
22
  Mihari::AutonomousSystem.new(asn: normalize_asn(asn))
23
23
  end
24
24
 
25
- #
26
- # @param [Hash] d
27
- #
28
- # @return [AutonomousSystem]
29
- #
30
- def self.from_dynamic!(d)
31
- d = Types::Hash[d]
32
- new(
33
- asn: d.fetch("asn")
34
- )
25
+ class << self
26
+ #
27
+ # @param [Hash] d
28
+ #
29
+ # @return [AutonomousSystem]
30
+ #
31
+ def from_dynamic!(d)
32
+ d = Types::Hash[d]
33
+ new(
34
+ asn: d.fetch("asn")
35
+ )
36
+ end
35
37
  end
36
38
  end
37
39
 
@@ -67,17 +69,19 @@ module Mihari
67
69
  )
68
70
  end
69
71
 
70
- #
71
- # @param [Hash] d
72
- #
73
- # @return [Location]
74
- #
75
- def self.from_dynamic!(d)
76
- d = Types::Hash[d]
77
- new(
78
- country: d["country"],
79
- country_code: d["country_code"]
80
- )
72
+ class << self
73
+ #
74
+ # @param [Hash] d
75
+ #
76
+ # @return [Location]
77
+ #
78
+ def from_dynamic!(d)
79
+ d = Types::Hash[d]
80
+ new(
81
+ country: d["country"],
82
+ country_code: d["country_code"]
83
+ )
84
+ end
81
85
  end
82
86
  end
83
87
 
@@ -98,16 +102,18 @@ module Mihari
98
102
  Port.new(port: port)
99
103
  end
100
104
 
101
- #
102
- # @param [Hash] d
103
- #
104
- # @return [Service]
105
- #
106
- def self.from_dynamic!(d)
107
- d = Types::Hash[d]
108
- new(
109
- port: d.fetch("port")
110
- )
105
+ class << self
106
+ #
107
+ # @param [Hash] d
108
+ #
109
+ # @return [Service]
110
+ #
111
+ def from_dynamic!(d)
112
+ d = Types::Hash[d]
113
+ new(
114
+ port: d.fetch("port")
115
+ )
116
+ end
111
117
  end
112
118
  end
113
119
 
@@ -173,20 +179,22 @@ module Mihari
173
179
  )
174
180
  end
175
181
 
176
- #
177
- # @param [Hash] d
178
- #
179
- # @return [Hit]
180
- #
181
- def self.from_dynamic!(d)
182
- d = Types::Hash[d]
183
- new(
184
- ip: d.fetch("ip"),
185
- location: Location.from_dynamic!(d.fetch("location")),
186
- autonomous_system: AutonomousSystem.from_dynamic!(d.fetch("autonomous_system")),
187
- metadata: d,
188
- services: d.fetch("services", []).map { |x| Service.from_dynamic!(x) }
189
- )
182
+ class << self
183
+ #
184
+ # @param [Hash] d
185
+ #
186
+ # @return [Hit]
187
+ #
188
+ def from_dynamic!(d)
189
+ d = Types::Hash[d]
190
+ new(
191
+ ip: d.fetch("ip"),
192
+ location: Location.from_dynamic!(d.fetch("location")),
193
+ autonomous_system: AutonomousSystem.from_dynamic!(d.fetch("autonomous_system")),
194
+ metadata: d,
195
+ services: d.fetch("services", []).map { |x| Service.from_dynamic!(x) }
196
+ )
197
+ end
190
198
  end
191
199
  end
192
200
 
@@ -208,17 +216,19 @@ module Mihari
208
216
  attributes[:prev]
209
217
  end
210
218
 
211
- #
212
- # @param [Hash] d
213
- #
214
- # @return [Links]
215
- #
216
- def self.from_dynamic!(d)
217
- d = Types::Hash[d]
218
- new(
219
- next: d["next"],
220
- prev: d["prev"]
221
- )
219
+ class << self
220
+ #
221
+ # @param [Hash] d
222
+ #
223
+ # @return [Links]
224
+ #
225
+ def from_dynamic!(d)
226
+ d = Types::Hash[d]
227
+ new(
228
+ next: d["next"],
229
+ prev: d["prev"]
230
+ )
231
+ end
222
232
  end
223
233
  end
224
234
 
@@ -260,22 +270,24 @@ module Mihari
260
270
  # @return [Array<Mihari::Artifact>]
261
271
  #
262
272
  def to_artifacts
263
- hits.map { |hit| hit.to_artifact }
273
+ hits.map(&:to_artifact)
264
274
  end
265
275
 
266
- #
267
- # @param [Hash] d
268
- #
269
- # @return [Result]
270
- #
271
- def self.from_dynamic!(d)
272
- d = Types::Hash[d]
273
- new(
274
- query: d.fetch("query"),
275
- total: d.fetch("total"),
276
- hits: d.fetch("hits", []).map { |x| Hit.from_dynamic!(x) },
277
- links: Links.from_dynamic!(d.fetch("links"))
278
- )
276
+ class << self
277
+ #
278
+ # @param [Hash] d
279
+ #
280
+ # @return [Result]
281
+ #
282
+ def from_dynamic!(d)
283
+ d = Types::Hash[d]
284
+ new(
285
+ query: d.fetch("query"),
286
+ total: d.fetch("total"),
287
+ hits: d.fetch("hits", []).map { |x| Hit.from_dynamic!(x) },
288
+ links: Links.from_dynamic!(d.fetch("links"))
289
+ )
290
+ end
279
291
  end
280
292
  end
281
293
 
@@ -305,18 +317,20 @@ module Mihari
305
317
  attributes[:result]
306
318
  end
307
319
 
308
- #
309
- # @param [Hash] d
310
- #
311
- # @return [Response]
312
- #
313
- def self.from_dynamic!(d)
314
- d = Types::Hash[d]
315
- new(
316
- code: d.fetch("code"),
317
- status: d.fetch("status"),
318
- result: Result.from_dynamic!(d.fetch("result"))
319
- )
320
+ class << self
321
+ #
322
+ # @param [Hash] d
323
+ #
324
+ # @return [Response]
325
+ #
326
+ def from_dynamic!(d)
327
+ d = Types::Hash[d]
328
+ new(
329
+ code: d.fetch("code"),
330
+ status: d.fetch("status"),
331
+ result: Result.from_dynamic!(d.fetch("result"))
332
+ )
333
+ end
320
334
  end
321
335
  end
322
336
  end