mihari 0.4.2 → 0.5.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d2122f13495ae81a98410887ee84dd41cbe5dbea0cf70b28afac5943d899a543
4
- data.tar.gz: '08b3eb468a8bb767990b3bea8d2b18950d4240611fec189a9ed4d6272ba84aab'
3
+ metadata.gz: 0a78aa76416da5dc139e1fe11ce2d2475a45deda3a82caa1fa21eecd6e291711
4
+ data.tar.gz: 9958ca0873d55c20525c3fa9186b266bbfba68c957997ae14f73a1d136d2032e
5
5
  SHA512:
6
- metadata.gz: b98c1c679ab601b40f918be6eb3bb4ea1c80761abe79944b1c2111abb6eec87d430ffb5e7571591875ecabf631753cb428dab98ae11dfe7708ac87ea1f6c50f8
7
- data.tar.gz: 4d6a0b87a14dbb6de755f623e6544743966b26101a49b7dc71a7c4604fb662a052c52071c638e7fdedfcd918bf783cb90cd46567e0f31a30e93322181942af4a
6
+ metadata.gz: 4d80e8026601ca12ed90b45d21ef0eb88a53cd1312d6f29f910ded76d7fa76e72c3295051e59a91e159d88cc54b3abec627837bbb6e53eda8a75c4acc52edcb2
7
+ data.tar.gz: f70660b7da90c4ae9a13cfba050d775283072fbd135d30b58df6d17ae41e6799532038bef16fe76a183e81a07ab188aada365f418a733540ee15436df0b6295f
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mihari
4
+ class AlertViewer
5
+ attr_reader :limit
6
+ attr_reader :the_hive
7
+
8
+ ALERT_KEYS = %w(title description artifacts tags createdAt status).freeze
9
+
10
+ def initialize(limit: 5)
11
+ @limit = limit
12
+ validate_limit
13
+
14
+ @the_hive = TheHive.new
15
+ raise Error, "Cannot connect to the TheHive instance" unless the_hive.valid?
16
+ end
17
+
18
+ def list
19
+ range = limit == "all" ? "all" : "0-#{limit}"
20
+ alerts = the_hive.alert.list(range: range)
21
+ alerts.map { |alert| convert alert }
22
+ end
23
+
24
+ private
25
+
26
+ def validate_limit
27
+ return true if limit == "all"
28
+
29
+ raise ArgumentError, "limit should be bigger than zero" unless limit.to_i.positive?
30
+ end
31
+
32
+ def convert(alert)
33
+ attributes = alert.select { |k, _v| ALERT_KEYS.include? k }
34
+ attributes["createdAt"] = Time.at(attributes["createdAt"] / 1000).to_s
35
+ attributes["artifacts"] = (attributes.dig("artifacts") || []).map do |artifact|
36
+ artifact.dig("data")
37
+ end.sort
38
+ attributes
39
+ end
40
+ end
41
+ end
@@ -57,7 +57,7 @@ module Mihari
57
57
  def unique_artifacts
58
58
  return normalized_artifacts unless the_hive.valid?
59
59
 
60
- the_hive.find_non_existing_artifacts(normalized_artifacts)
60
+ the_hive.artifact.find_non_existing_artifacts(normalized_artifacts)
61
61
  end
62
62
  end
63
63
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "hachi"
4
-
5
3
  module Mihari
6
4
  class Artifact
7
5
  attr_reader :data
data/lib/mihari/cli.rb CHANGED
@@ -62,10 +62,20 @@ module Mihari
62
62
  run_analyzer basic
63
63
  end
64
64
 
65
+ desc "alerts", "Show the alerts on TheHive"
66
+ method_option :limit, default: 5, desc: "Number of alerts to show (or 'all' to show all the alerts)"
67
+ def alerts
68
+ with_error_handling do
69
+ viewer = AlertViewer.new(limit: options["limit"])
70
+ alerts = viewer.list
71
+ puts JSON.pretty_generate(alerts)
72
+ end
73
+ end
74
+
65
75
  no_commands do
66
76
  def with_error_handling
67
77
  yield
68
- rescue ArgumentError, Hachi::Error, Censys::ResponseError => e
78
+ rescue ArgumentError, Hachi::Error, Censys::ResponseError, Error => e
69
79
  puts "Warning: #{e}"
70
80
  rescue StandardError => e
71
81
  puts "Warning: #{e}"
@@ -3,21 +3,21 @@
3
3
  module Mihari
4
4
  module Emitters
5
5
  class TheHive < Base
6
- attr_reader :api
6
+ attr_reader :the_hive
7
7
 
8
8
  def initialize
9
- @api = Mihari::TheHive.new
9
+ @the_hive = Mihari::TheHive.new
10
10
  end
11
11
 
12
12
  # @return [true, false]
13
13
  def valid?
14
- api.valid?
14
+ the_hive.valid?
15
15
  end
16
16
 
17
17
  def emit(title:, description:, artifacts:, tags: [])
18
18
  return if artifacts.empty?
19
19
 
20
- api.create_alert(
20
+ the_hive.alert.create(
21
21
  title: title,
22
22
  description: description,
23
23
  artifacts: artifacts.map(&:to_h),
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mihari
4
+ class TheHive
5
+ class Alert < Base
6
+ # @return [Array]
7
+ def list(range: "all", sort: "-date")
8
+ alerts = api.alert.search({ source: "mihari" }, range: range, sort: sort)
9
+ alerts.sort_by { |alert| -alert.dig("createdAt") }
10
+ end
11
+
12
+ # @return [Hash]
13
+ def create(title:, description:, artifacts:, tags: [])
14
+ api.alert.create(
15
+ title: title,
16
+ description: description,
17
+ artifacts: artifacts,
18
+ tags: tags,
19
+ type: "external",
20
+ source: "mihari"
21
+ )
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mihari
4
+ class TheHive
5
+ class Artifact < Base
6
+ # @return [Array]
7
+ def search(data:, data_type:, range: "all")
8
+ api.artifact.search({ data: data, data_type: data_type }, range: range)
9
+ end
10
+
11
+ # @return [Array]
12
+ def search_all(data:, range: "all")
13
+ api.artifact.search({ data: data }, range: range)
14
+ end
15
+
16
+ # @return [true, false]
17
+ def exists?(data:, data_type:)
18
+ res = search(data: data, data_type: data_type, range: "0-1")
19
+ !res.empty?
20
+ end
21
+
22
+ # @return [Array<Mihari::Artifact>]
23
+ def find_non_existing_artifacts(artifacts)
24
+ data = artifacts.map(&:data)
25
+ results = search_all(data: data)
26
+ keys = results.map { |result| result.dig("data") }.compact.uniq
27
+ artifacts.reject do |artifact|
28
+ keys.include? artifact.data
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hachi"
4
+
5
+ module Mihari
6
+ class TheHive
7
+ class Base
8
+ # @return [Hachi::API]
9
+ def api
10
+ @api ||= Hachi::API.new
11
+ end
12
+ end
13
+ end
14
+ end
@@ -5,14 +5,12 @@ require "uri"
5
5
 
6
6
  module Mihari
7
7
  class TheHive
8
- # @return [true, false]
9
- def api_endpont?
10
- ENV.key? "THEHIVE_API_ENDPOINT"
11
- end
8
+ attr_reader :artifact
9
+ attr_reader :alert
12
10
 
13
- # @return [true, false]
14
- def api_key?
15
- ENV.key? "THEHIVE_API_KEY"
11
+ def initialize
12
+ @artifact = Artifact.new
13
+ @alert = Alert.new
16
14
  end
17
15
 
18
16
  # @return [true, false]
@@ -20,51 +18,18 @@ module Mihari
20
18
  api_endpont? && api_key? && ping?
21
19
  end
22
20
 
23
- # @return [Hachi::API]
24
- def api
25
- @api ||= Hachi::API.new
26
- end
27
-
28
- # @return [Array]
29
- def search(data:, data_type:, range: "all")
30
- api.artifact.search({ data: data, data_type: data_type }, range: range)
31
- end
32
-
33
- # @return [Array]
34
- def search_all(data:, range: "all")
35
- api.artifact.search({ data: data }, range: range)
36
- end
21
+ private
37
22
 
38
23
  # @return [true, false]
39
- def exists?(data:, data_type:)
40
- res = search(data: data, data_type: data_type, range: "0-1")
41
- !res.empty?
42
- end
43
-
44
- # @return [Array<Mihari::Artifact>]
45
- def find_non_existing_artifacts(artifacts)
46
- data = artifacts.map(&:data)
47
- results = search_all(data: data)
48
- keys = results.map { |result| result.dig("data") }.compact.uniq
49
- artifacts.reject do |artifact|
50
- keys.include? artifact.data
51
- end
24
+ def api_endpont?
25
+ ENV.key? "THEHIVE_API_ENDPOINT"
52
26
  end
53
27
 
54
- # @return [Hash]
55
- def create_alert(title:, description:, artifacts:, tags: [])
56
- api.alert.create(
57
- title: title,
58
- description: description,
59
- artifacts: artifacts,
60
- tags: tags,
61
- type: "external",
62
- source: "mihari"
63
- )
28
+ # @return [true, false]
29
+ def api_key?
30
+ ENV.key? "THEHIVE_API_KEY"
64
31
  end
65
32
 
66
- private
67
-
68
33
  def ping?
69
34
  base_url = ENV.fetch("THEHIVE_API_ENDPOINT")
70
35
  base_url = base_url.end_with?("/") ? base_url[0..-2] : base_url
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- VERSION = "0.4.2"
4
+ VERSION = "0.5.0"
5
5
  end
data/lib/mihari.rb CHANGED
@@ -20,6 +20,9 @@ require "mihari/errors"
20
20
  require "mihari/type_checker"
21
21
  require "mihari/artifact"
22
22
 
23
+ require "mihari/the_hive/base"
24
+ require "mihari/the_hive/alert"
25
+ require "mihari/the_hive/artifact"
23
26
  require "mihari/the_hive"
24
27
 
25
28
  require "mihari/analyzers/base"
@@ -35,4 +38,6 @@ require "mihari/emitters/slack"
35
38
  require "mihari/emitters/stdout"
36
39
  require "mihari/emitters/the_hive"
37
40
 
41
+ require "mihari/alert_viewer"
42
+
38
43
  require "mihari/cli"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mihari
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Manabu Niseki
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-08-10 00:00:00.000000000 Z
11
+ date: 2019-08-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -297,6 +297,7 @@ files:
297
297
  - examples/vt_passive_dns.rb
298
298
  - exe/mihari
299
299
  - lib/mihari.rb
300
+ - lib/mihari/alert_viewer.rb
300
301
  - lib/mihari/analyzers/base.rb
301
302
  - lib/mihari/analyzers/basic.rb
302
303
  - lib/mihari/analyzers/censys.rb
@@ -312,6 +313,9 @@ files:
312
313
  - lib/mihari/emitters/the_hive.rb
313
314
  - lib/mihari/errors.rb
314
315
  - lib/mihari/the_hive.rb
316
+ - lib/mihari/the_hive/alert.rb
317
+ - lib/mihari/the_hive/artifact.rb
318
+ - lib/mihari/the_hive/base.rb
315
319
  - lib/mihari/type_checker.rb
316
320
  - lib/mihari/version.rb
317
321
  - mihari.gemspec