mihari 5.2.2 → 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.
@@ -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