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 +4 -4
- data/lib/mihari/alert_viewer.rb +41 -0
- data/lib/mihari/analyzers/base.rb +1 -1
- data/lib/mihari/artifact.rb +0 -2
- data/lib/mihari/cli.rb +11 -1
- data/lib/mihari/emitters/the_hive.rb +4 -4
- data/lib/mihari/the_hive/alert.rb +25 -0
- data/lib/mihari/the_hive/artifact.rb +33 -0
- data/lib/mihari/the_hive/base.rb +14 -0
- data/lib/mihari/the_hive.rb +11 -46
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari.rb +5 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0a78aa76416da5dc139e1fe11ce2d2475a45deda3a82caa1fa21eecd6e291711
|
4
|
+
data.tar.gz: 9958ca0873d55c20525c3fa9186b266bbfba68c957997ae14f73a1d136d2032e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/lib/mihari/artifact.rb
CHANGED
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 :
|
6
|
+
attr_reader :the_hive
|
7
7
|
|
8
8
|
def initialize
|
9
|
-
@
|
9
|
+
@the_hive = Mihari::TheHive.new
|
10
10
|
end
|
11
11
|
|
12
12
|
# @return [true, false]
|
13
13
|
def valid?
|
14
|
-
|
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
|
-
|
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
|
data/lib/mihari/the_hive.rb
CHANGED
@@ -5,14 +5,12 @@ require "uri"
|
|
5
5
|
|
6
6
|
module Mihari
|
7
7
|
class TheHive
|
8
|
-
|
9
|
-
|
10
|
-
ENV.key? "THEHIVE_API_ENDPOINT"
|
11
|
-
end
|
8
|
+
attr_reader :artifact
|
9
|
+
attr_reader :alert
|
12
10
|
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
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
|
40
|
-
|
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 [
|
55
|
-
def
|
56
|
-
|
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
|
data/lib/mihari/version.rb
CHANGED
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
|
+
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-
|
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
|