mihari 5.4.9 → 5.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/docs/analyzers/binaryedge.md +2 -2
  3. data/docs/analyzers/censys.md +3 -3
  4. data/docs/analyzers/circl.md +3 -3
  5. data/docs/analyzers/crtsh.md +2 -2
  6. data/docs/analyzers/dnstwister.md +1 -1
  7. data/docs/analyzers/feed.md +7 -7
  8. data/docs/analyzers/greynoise.md +2 -2
  9. data/docs/analyzers/hunterhow.md +4 -4
  10. data/docs/analyzers/index.md +13 -8
  11. data/docs/analyzers/onyphe.md +2 -2
  12. data/docs/analyzers/otx.md +2 -2
  13. data/docs/analyzers/passivetotal.md +7 -3
  14. data/docs/analyzers/pulsedive.md +2 -2
  15. data/docs/analyzers/securitytrails.md +6 -2
  16. data/docs/analyzers/shodan.md +2 -2
  17. data/docs/analyzers/urlscan.md +2 -2
  18. data/docs/analyzers/virustotal.md +6 -2
  19. data/docs/analyzers/virustotal_intelligence.md +6 -2
  20. data/docs/analyzers/zoomeye.md +3 -3
  21. data/docs/emitters/hive.md +4 -4
  22. data/docs/emitters/index.md +29 -0
  23. data/docs/emitters/misp.md +2 -2
  24. data/docs/emitters/slack.md +2 -7
  25. data/docs/emitters/webhook.md +4 -4
  26. data/docs/enrichers/index.md +29 -0
  27. data/docs/enrichers/ipinfo.md +7 -0
  28. data/docs/index.md +0 -2
  29. data/docs/installation.md +1 -1
  30. data/docs/rule.md +12 -15
  31. data/docs/usage.md +5 -2
  32. data/frontend/package-lock.json +294 -2772
  33. data/frontend/package.json +10 -10
  34. data/frontend/src/components/ErrorMessage.vue +0 -1
  35. data/frontend/src/components/alert/Alerts.vue +0 -1
  36. data/frontend/src/components/alert/AlertsWithPagination.vue +0 -1
  37. data/frontend/src/components/alert/AlertsWrapper.vue +0 -6
  38. data/frontend/src/components/alert/Form.vue +1 -3
  39. data/frontend/src/components/artifact/Artifact.vue +0 -17
  40. data/frontend/src/components/artifact/ArtifactWrapper.vue +0 -2
  41. data/frontend/src/components/artifact/WhoisRecord.vue +0 -3
  42. data/frontend/src/components/config/ConfigsWrapper.vue +0 -2
  43. data/frontend/src/components/rule/EditRule.vue +0 -3
  44. data/frontend/src/components/rule/EditRuleWrapper.vue +0 -2
  45. data/frontend/src/components/rule/Form.vue +1 -3
  46. data/frontend/src/components/rule/NewRule.vue +0 -3
  47. data/frontend/src/components/rule/Rule.vue +1 -7
  48. data/frontend/src/components/rule/RuleWrapper.vue +0 -2
  49. data/frontend/src/components/rule/RulesWrapper.vue +0 -6
  50. data/frontend/src/swagger.yaml +254 -254
  51. data/lib/mihari/analyzers/base.rb +7 -37
  52. data/lib/mihari/analyzers/binaryedge.rb +5 -1
  53. data/lib/mihari/analyzers/censys.rb +6 -1
  54. data/lib/mihari/analyzers/greynoise.rb +5 -1
  55. data/lib/mihari/analyzers/hunterhow.rb +5 -1
  56. data/lib/mihari/analyzers/onyphe.rb +5 -1
  57. data/lib/mihari/analyzers/passivetotal.rb +9 -0
  58. data/lib/mihari/analyzers/pulsedive.rb +1 -1
  59. data/lib/mihari/analyzers/rule.rb +55 -54
  60. data/lib/mihari/analyzers/securitytrails.rb +9 -0
  61. data/lib/mihari/analyzers/shodan.rb +5 -1
  62. data/lib/mihari/analyzers/urlscan.rb +5 -1
  63. data/lib/mihari/analyzers/virustotal.rb +11 -2
  64. data/lib/mihari/analyzers/virustotal_intelligence.rb +21 -1
  65. data/lib/mihari/analyzers/zoomeye.rb +7 -3
  66. data/lib/mihari/base.rb +69 -0
  67. data/lib/mihari/cli/main.rb +36 -0
  68. data/lib/mihari/clients/base.rb +7 -7
  69. data/lib/mihari/clients/binaryedge.rb +10 -4
  70. data/lib/mihari/clients/censys.rb +11 -4
  71. data/lib/mihari/clients/greynoise.rb +10 -4
  72. data/lib/mihari/clients/hunterhow.rb +10 -4
  73. data/lib/mihari/clients/misp.rb +3 -2
  74. data/lib/mihari/clients/onyphe.rb +10 -4
  75. data/lib/mihari/clients/shodan.rb +10 -4
  76. data/lib/mihari/clients/the_hive.rb +3 -2
  77. data/lib/mihari/clients/urlscan.rb +9 -3
  78. data/lib/mihari/clients/virustotal.rb +10 -4
  79. data/lib/mihari/clients/zoomeye.rb +11 -5
  80. data/lib/mihari/commands/alert.rb +6 -33
  81. data/lib/mihari/commands/rule.rb +7 -12
  82. data/lib/mihari/commands/search.rb +10 -38
  83. data/lib/mihari/config.rb +8 -0
  84. data/lib/mihari/constants.rb +3 -3
  85. data/lib/mihari/emitters/base.rb +22 -15
  86. data/lib/mihari/emitters/database.rb +1 -1
  87. data/lib/mihari/emitters/misp.rb +7 -6
  88. data/lib/mihari/emitters/slack.rb +24 -6
  89. data/lib/mihari/emitters/the_hive.rb +8 -7
  90. data/lib/mihari/emitters/webhook.rb +31 -29
  91. data/lib/mihari/enrichers/base.rb +25 -19
  92. data/lib/mihari/enrichers/google_public_dns.rb +38 -38
  93. data/lib/mihari/enrichers/ipinfo.rb +32 -34
  94. data/lib/mihari/enrichers/shodan.rb +18 -26
  95. data/lib/mihari/enrichers/whois.rb +121 -111
  96. data/lib/mihari/mixins/retriable.rb +4 -2
  97. data/lib/mihari/models/artifact.rb +37 -23
  98. data/lib/mihari/models/autonomous_system.rb +3 -2
  99. data/lib/mihari/models/cpe.rb +3 -2
  100. data/lib/mihari/models/dns.rb +3 -2
  101. data/lib/mihari/models/geolocation.rb +3 -2
  102. data/lib/mihari/models/port.rb +3 -2
  103. data/lib/mihari/models/reverse_dns.rb +3 -2
  104. data/lib/mihari/models/whois.rb +4 -3
  105. data/lib/mihari/schemas/analyzer.rb +24 -23
  106. data/lib/mihari/schemas/emitter.rb +32 -25
  107. data/lib/mihari/schemas/enricher.rb +21 -2
  108. data/lib/mihari/schemas/options.rb +27 -0
  109. data/lib/mihari/schemas/rule.rb +8 -4
  110. data/lib/mihari/services/alert_runner.rb +1 -1
  111. data/lib/mihari/services/rule_runner.rb +1 -11
  112. data/lib/mihari/types.rb +1 -14
  113. data/lib/mihari/version.rb +1 -1
  114. data/lib/mihari/web/endpoints/ip_addresses.rb +1 -1
  115. data/lib/mihari/web/public/assets/{index-33165282.css → index-56fc2187.css} +1 -1
  116. data/lib/mihari/web/public/assets/index-9cc489e6.js +1749 -0
  117. data/lib/mihari/web/public/index.html +2 -2
  118. data/lib/mihari/web/public/redoc-static.html +400 -400
  119. data/lib/mihari.rb +67 -37
  120. data/mihari.gemspec +3 -2
  121. data/mkdocs.yml +8 -6
  122. data/requirements.txt +1 -1
  123. metadata +24 -8
  124. data/lib/mihari/web/public/assets/index-a92abd57.js +0 -1740
@@ -18,13 +18,14 @@ module Mihari
18
18
  #
19
19
  # @param [Array<Mihari::Artifact>] artifacts
20
20
  # @param [Mihari::Services::Rule] rule
21
- # @param [Hash] **options
21
+ # @param [Hash, nil] options
22
+ # @param [Hash] **params
22
23
  #
23
- def initialize(artifacts:, rule:, **options)
24
- super(artifacts: artifacts, rule: rule, **options)
24
+ def initialize(artifacts:, rule:, options: nil, **params)
25
+ super(artifacts: artifacts, rule: rule, options: options)
25
26
 
26
- @url = options[:url] || Mihari.config.misp_url
27
- @api_key = options[:api_key] || Mihari.config.misp_api_key
27
+ @url = params[:url] || Mihari.config.misp_url
28
+ @api_key = params[:api_key] || Mihari.config.misp_api_key
28
29
  end
29
30
 
30
31
  # @return [Boolean]
@@ -70,7 +71,7 @@ module Mihari
70
71
  end
71
72
 
72
73
  def client
73
- @client ||= Clients::MISP.new(url, api_key: api_key)
74
+ @client ||= Clients::MISP.new(url, api_key: api_key, timeout: timeout)
74
75
  end
75
76
 
76
77
  #
@@ -134,13 +134,14 @@ module Mihari
134
134
  #
135
135
  # @param [Array<Mihari::Artifact>] artifacts
136
136
  # @param [Mihari::Services::Rule] rule
137
- # @param [Hash] **_options
137
+ # @param [Hash, nil] options
138
+ # @param [Hash] **params
138
139
  #
139
- def initialize(artifacts:, rule:, **options)
140
- super(artifacts: artifacts, rule: rule, **options)
140
+ def initialize(artifacts:, rule:, options: nil, **params)
141
+ super(artifacts: artifacts, rule: rule, options: options)
141
142
 
142
- @webhook_url = options[:webhook_url] || Mihari.config.slack_webhook_url
143
- @channel = options[:channel] || Mihari.config.slack_channel || DEFAULT_CHANNEL
143
+ @webhook_url = params[:webhook_url] || Mihari.config.slack_webhook_url
144
+ @channel = params[:channel] || Mihari.config.slack_channel || DEFAULT_CHANNEL
144
145
  @username = DEFAULT_USERNAME
145
146
  end
146
147
 
@@ -162,8 +163,25 @@ module Mihari
162
163
  webhook_url?
163
164
  end
164
165
 
166
+ #
167
+ # @return [::Slack::Notifier]
168
+ #
165
169
  def notifier
166
- @notifier ||= ::Slack::Notifier.new(webhook_url, channel: channel, username: username)
170
+ @notifier ||= [].tap do |out|
171
+ out << if timeout.nil?
172
+ ::Slack::Notifier.new(
173
+ webhook_url,
174
+ channel: channel, username: username
175
+ )
176
+ else
177
+ ::Slack::Notifier.new(
178
+ webhook_url,
179
+ channel: channel,
180
+ username: username,
181
+ http_options: { timeout: timeout }
182
+ )
183
+ end
184
+ end.first
167
185
  end
168
186
 
169
187
  #
@@ -15,14 +15,15 @@ module Mihari
15
15
  #
16
16
  # @param [Array<Mihari::Artifact>] artifacts
17
17
  # @param [Mihari::Services::Rule] rule
18
- # @param [Hash] **options
18
+ # @param [Hash, nil] options
19
+ # @param [Hash] **params
19
20
  #
20
- def initialize(artifacts:, rule:, **options)
21
- super(artifacts: artifacts, rule: rule, **options)
21
+ def initialize(artifacts:, rule:, options: nil, **params)
22
+ super(artifacts: artifacts, rule: rule, options: options)
22
23
 
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
24
+ @url = params[:url] || Mihari.config.thehive_url
25
+ @api_key = params[:api_key] || Mihari.config.thehive_api_key
26
+ @api_version = params[:api_version] || Mihari.config.thehive_api_version
26
27
  end
27
28
 
28
29
  # @return [Boolean]
@@ -79,7 +80,7 @@ module Mihari
79
80
  end
80
81
 
81
82
  def client
82
- @client ||= Clients::TheHive.new(url, api_key: api_key, api_version: normalized_api_version)
83
+ @client ||= Clients::TheHive.new(url, api_key: api_key, api_version: normalized_api_version, timeout: timeout)
83
84
  end
84
85
 
85
86
  #
@@ -5,28 +5,30 @@ require "erb"
5
5
  module Mihari
6
6
  module Emitters
7
7
  class PayloadTemplate < ERB
8
- def self.template
9
- %{
10
- {
11
- "rule": {
12
- "id": "<%= @rule.id %>",
13
- "title": "<%= @rule.title %>",
14
- "description": "<%= @rule.description %>"
15
- },
16
- "artifacts": [
17
- <% @artifacts.each_with_index do |artifact, idx| %>
18
- "<%= artifact.data %>"
19
- <%= ',' if idx < (@artifacts.length - 1) %>
20
- <% end %>
21
- ],
22
- "tags": [
23
- <% @rule.tags.each_with_index do |tag, idx| %>
24
- "<%= tag %>"
25
- <%= ',' if idx < (@rule.tags.length - 1) %>
26
- <% end %>
27
- ]
28
- }
29
- }
8
+ class << self
9
+ def template
10
+ %{
11
+ {
12
+ "rule": {
13
+ "id": "<%= @rule.id %>",
14
+ "title": "<%= @rule.title %>",
15
+ "description": "<%= @rule.description %>"
16
+ },
17
+ "artifacts": [
18
+ <% @artifacts.each_with_index do |artifact, idx| %>
19
+ "<%= artifact.data %>"
20
+ <%= ',' if idx < (@artifacts.length - 1) %>
21
+ <% end %>
22
+ ],
23
+ "tags": [
24
+ <% @rule.tags.each_with_index do |tag, idx| %>
25
+ "<%= tag %>"
26
+ <%= ',' if idx < (@rule.tags.length - 1) %>
27
+ <% end %>
28
+ ]
29
+ }
30
+ }
31
+ end
30
32
  end
31
33
 
32
34
  def initialize(artifacts:, rule:, options: {})
@@ -60,13 +62,13 @@ module Mihari
60
62
  # @param [Mihari::Services::Rule] rule
61
63
  # @param [Hash] **options
62
64
  #
63
- def initialize(artifacts:, rule:, **options)
64
- super(artifacts: artifacts, rule: rule, **options)
65
+ def initialize(artifacts:, rule:, options: nil, **params)
66
+ super(artifacts: artifacts, rule: rule, options: options)
65
67
 
66
- @url = Addressable::URI.parse(options[:url])
67
- @headers = options[:headers] || {}
68
- @method = options[:method] || "POST"
69
- @template = options[:template]
68
+ @url = Addressable::URI.parse(params[:url])
69
+ @headers = params[:headers] || {}
70
+ @method = params[:method] || "POST"
71
+ @template = params[:template]
70
72
  end
71
73
 
72
74
  def emit
@@ -90,7 +92,7 @@ module Mihari
90
92
  private
91
93
 
92
94
  def http
93
- HTTP::Factory.build headers: headers
95
+ HTTP::Factory.build headers: headers, timeout: timeout
94
96
  end
95
97
 
96
98
  #
@@ -2,33 +2,39 @@
2
2
 
3
3
  module Mihari
4
4
  module Enrichers
5
- class Base
5
+ class Base < Mihari::Base
6
6
  include Mixins::Configurable
7
+ include Mixins::Retriable
7
8
 
8
- class << self
9
- include Dry::Monads[:result, :try]
10
-
11
- def inherited(child)
12
- super
13
- Mihari.enrichers << child
14
- end
9
+ include Dry::Monads[:result, :try]
15
10
 
16
- def query_result(value)
17
- Try[StandardError] { query(value) }.to_result
18
- end
11
+ def initialize(options: nil)
12
+ super(options: options)
13
+ end
19
14
 
20
- #
21
- # @param [String] value
22
- #
23
- def query(value)
24
- raise NotImplementedError, "You must implement #{self.class}##{__method__}"
25
- end
15
+ def query_result(value)
16
+ Try[StandardError] do
17
+ retry_on_error(
18
+ times: retry_times,
19
+ interval: retry_interval,
20
+ exponential_backoff: retry_exponential_backoff
21
+ ) { query value }
22
+ end.to_result
26
23
  end
27
24
 
28
- # @return [Boolean]
29
- def valid?
25
+ #
26
+ # @param [String] value
27
+ #
28
+ def query(value)
30
29
  raise NotImplementedError, "You must implement #{self.class}##{__method__}"
31
30
  end
31
+
32
+ class << self
33
+ def inherited(child)
34
+ super
35
+ Mihari.enrichers << child
36
+ end
37
+ end
32
38
  end
33
39
  end
34
40
  end
@@ -5,52 +5,52 @@ require "net/https"
5
5
  module Mihari
6
6
  module Enrichers
7
7
  class GooglePublicDNS < Base
8
- # @return [Boolean]
9
- def valid?
10
- true
8
+ #
9
+ # Query Google Public DNS
10
+ #
11
+ # @param [String] name
12
+ #
13
+ # @return [Array<Mihari::Structs::Shodan::GooglePublicDNS::Response>]
14
+ #
15
+ def query(name)
16
+ %w[A AAAA CNAME TXT NS].filter_map do |resource_type|
17
+ query_by_type(name, resource_type)
18
+ end
11
19
  end
12
20
 
13
- class << self
14
- include Dry::Monads[:result]
15
-
16
- #
17
- # Query Google Public DNS
18
- #
19
- # @param [String] name
20
- #
21
- # @return [Array<Mihari::Structs::Shodan::GooglePublicDNS::Response>]
22
- #
23
- def query(name)
24
- %w[A AAAA CNAME TXT NS].filter_map do |resource_type|
25
- query_by_type(name, resource_type)
26
- end
27
- end
21
+ #
22
+ # Query Google Public DNS by resource type
23
+ #
24
+ # @param [String] name
25
+ # @param [String] resource_type
26
+ #
27
+ # @return [Mihari::Structs::Shodan::GooglePublicDNS::Response, nil]
28
+ #
29
+ def query_by_type(name, resource_type)
30
+ url = "https://dns.google/resolve"
31
+ params = { name: name, type: resource_type }
32
+ res = http.get(url, params: params)
33
+
34
+ data = JSON.parse(res.body.to_s)
35
+
36
+ Structs::GooglePublicDNS::Response.from_dynamic! data
37
+ rescue HTTPError
38
+ nil
39
+ end
28
40
 
41
+ class << self
29
42
  #
30
- # Query Google Public DNS by resource type
31
- #
32
- # @param [String] name
33
- # @param [String] resource_type
34
- #
35
- # @return [Mihari::Structs::Shodan::GooglePublicDNS::Response, nil]
43
+ # @return [String]
36
44
  #
37
- def query_by_type(name, resource_type)
38
- url = "https://dns.google/resolve"
39
- params = { name: name, type: resource_type }
40
- res = http.get(url, params: params)
41
-
42
- data = JSON.parse(res.body.to_s)
43
-
44
- Structs::GooglePublicDNS::Response.from_dynamic! data
45
- rescue HTTPError
46
- nil
45
+ def class_key
46
+ "google_public_dns"
47
47
  end
48
+ end
48
49
 
49
- private
50
+ private
50
51
 
51
- def http
52
- HTTP::Factory.build
53
- end
52
+ def http
53
+ HTTP::Factory.build timeout: timeout
54
54
  end
55
55
  end
56
56
  end
@@ -5,9 +5,15 @@ require "net/https"
5
5
  module Mihari
6
6
  module Enrichers
7
7
  class IPInfo < Base
8
- # @return [Boolean]
9
- def valid?
10
- Mihari.config.ipinfo_api_key.nil?
8
+ include Memist::Memoizable
9
+
10
+ # @return [String, nil]
11
+ attr_reader :api_key
12
+
13
+ def initialize(options: nil, api_key: nil)
14
+ @api_key = api_key || Mihari.config.ipinfo_api_key
15
+
16
+ super(options: options)
11
17
  end
12
18
 
13
19
  private
@@ -16,37 +22,29 @@ module Mihari
16
22
  %w[ipinfo_api_key]
17
23
  end
18
24
 
19
- class << self
20
- include Dry::Monads[:result]
21
- include Memist::Memoizable
22
-
23
- #
24
- # Query IPInfo
25
- #
26
- # @param [String] ip
27
- #
28
- # @return [Mihari::Structs::IPInfo::Response, nil]
29
- #
30
- def query(ip)
31
- url = "https://ipinfo.io/#{ip}/json"
32
- res = http.get(url)
33
- data = JSON.parse(res.body.to_s)
34
-
35
- Structs::IPInfo::Response.from_dynamic! data
36
- end
37
- memoize :query
38
-
39
- private
40
-
41
- def headers
42
- token = Mihari.config.ipinfo_api_key
43
- authorization = token.nil? ? nil : "Bearer #{token}"
44
- { authorization: authorization }.compact
45
- end
46
-
47
- def http
48
- HTTP::Factory.build headers: headers
49
- end
25
+ #
26
+ # Query IPInfo
27
+ #
28
+ # @param [String] ip
29
+ #
30
+ # @return [Mihari::Structs::IPInfo::Response, nil]
31
+ #
32
+ def query(ip)
33
+ url = "https://ipinfo.io/#{ip}/json"
34
+ res = http.get(url)
35
+ data = JSON.parse(res.body.to_s)
36
+
37
+ Structs::IPInfo::Response.from_dynamic! data
38
+ end
39
+ memoize :query
40
+
41
+ def headers
42
+ authorization = api_key.nil? ? nil : "Bearer #{api_key}"
43
+ { authorization: authorization }.compact
44
+ end
45
+
46
+ def http
47
+ HTTP::Factory.build headers: headers, timeout: timeout
50
48
  end
51
49
  end
52
50
  end
@@ -5,36 +5,28 @@ require "net/https"
5
5
  module Mihari
6
6
  module Enrichers
7
7
  class Shodan < Base
8
- # @return [Boolean]
9
- def valid?
10
- true
11
- end
12
-
13
- class << self
14
- include Dry::Monads[:result]
15
- include Memist::Memoizable
8
+ include Memist::Memoizable
16
9
 
17
- #
18
- # Query Shodan Internet DB
19
- #
20
- # @param [String] ip
21
- #
22
- # @return [Mihari::Structs::Shodan::InternetDBResponse, nil]
23
- #
24
- def query(ip)
25
- url = "https://internetdb.shodan.io/#{ip}"
26
- res = http.get(url)
27
- data = JSON.parse(res.body.to_s)
10
+ #
11
+ # Query Shodan Internet DB
12
+ #
13
+ # @param [String] ip
14
+ #
15
+ # @return [Mihari::Structs::Shodan::InternetDBResponse, nil]
16
+ #
17
+ def query(ip)
18
+ url = "https://internetdb.shodan.io/#{ip}"
19
+ res = http.get(url)
20
+ data = JSON.parse(res.body.to_s)
28
21
 
29
- Structs::Shodan::InternetDBResponse.from_dynamic! data
30
- end
31
- memoize :query
22
+ Structs::Shodan::InternetDBResponse.from_dynamic! data
23
+ end
24
+ memoize :query
32
25
 
33
- private
26
+ private
34
27
 
35
- def http
36
- HTTP::Factory.build
37
- end
28
+ def http
29
+ HTTP::Factory.build timeout: timeout
38
30
  end
39
31
  end
40
32
  end