mihari 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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