mihari 5.6.1 → 5.6.2

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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/frontend/package-lock.json +173 -176
  3. data/frontend/package.json +9 -9
  4. data/lib/mihari/{base.rb → actor.rb} +16 -2
  5. data/lib/mihari/analyzers/base.rb +5 -10
  6. data/lib/mihari/analyzers/censys.rb +1 -1
  7. data/lib/mihari/analyzers/hunterhow.rb +1 -1
  8. data/lib/mihari/analyzers/passivetotal.rb +1 -1
  9. data/lib/mihari/analyzers/pulsedive.rb +1 -1
  10. data/lib/mihari/analyzers/securitytrails.rb +1 -1
  11. data/lib/mihari/analyzers/urlscan.rb +1 -1
  12. data/lib/mihari/analyzers/virustotal.rb +5 -5
  13. data/lib/mihari/analyzers/zoomeye.rb +3 -3
  14. data/lib/mihari/clients/crtsh.rb +2 -2
  15. data/lib/mihari/clients/passivetotal.rb +4 -4
  16. data/lib/mihari/clients/securitytrails.rb +3 -3
  17. data/lib/mihari/commands/rule.rb +2 -11
  18. data/lib/mihari/commands/search.rb +1 -1
  19. data/lib/mihari/emitters/base.rb +13 -24
  20. data/lib/mihari/emitters/database.rb +7 -9
  21. data/lib/mihari/emitters/misp.rb +14 -38
  22. data/lib/mihari/emitters/slack.rb +14 -11
  23. data/lib/mihari/emitters/the_hive.rb +16 -44
  24. data/lib/mihari/emitters/webhook.rb +31 -21
  25. data/lib/mihari/enrichers/base.rb +1 -6
  26. data/lib/mihari/enrichers/whois.rb +1 -1
  27. data/lib/mihari/models/alert.rb +75 -73
  28. data/lib/mihari/models/artifact.rb +182 -180
  29. data/lib/mihari/models/autonomous_system.rb +22 -20
  30. data/lib/mihari/models/cpe.rb +21 -19
  31. data/lib/mihari/models/dns.rb +24 -22
  32. data/lib/mihari/models/geolocation.rb +22 -20
  33. data/lib/mihari/models/port.rb +21 -19
  34. data/lib/mihari/models/reverse_dns.rb +21 -19
  35. data/lib/mihari/models/rule.rb +67 -65
  36. data/lib/mihari/models/tag.rb +5 -3
  37. data/lib/mihari/models/tagging.rb +5 -3
  38. data/lib/mihari/models/whois.rb +18 -16
  39. data/lib/mihari/rule.rb +352 -0
  40. data/lib/mihari/schemas/analyzer.rb +94 -87
  41. data/lib/mihari/schemas/emitter.rb +9 -5
  42. data/lib/mihari/schemas/enricher.rb +8 -4
  43. data/lib/mihari/schemas/mixins.rb +15 -0
  44. data/lib/mihari/schemas/rule.rb +3 -10
  45. data/lib/mihari/services/alert_builder.rb +1 -1
  46. data/lib/mihari/services/alert_proxy.rb +10 -6
  47. data/lib/mihari/services/alert_runner.rb +4 -4
  48. data/lib/mihari/services/rule_builder.rb +3 -3
  49. data/lib/mihari/services/rule_runner.rb +5 -5
  50. data/lib/mihari/structs/binaryedge.rb +1 -1
  51. data/lib/mihari/structs/censys.rb +6 -6
  52. data/lib/mihari/structs/config.rb +1 -1
  53. data/lib/mihari/structs/greynoise.rb +5 -5
  54. data/lib/mihari/structs/hunterhow.rb +3 -3
  55. data/lib/mihari/structs/onyphe.rb +5 -5
  56. data/lib/mihari/structs/shodan.rb +6 -6
  57. data/lib/mihari/structs/urlscan.rb +3 -3
  58. data/lib/mihari/structs/virustotal_intelligence.rb +3 -3
  59. data/lib/mihari/version.rb +1 -1
  60. data/lib/mihari/web/endpoints/alerts.rb +4 -4
  61. data/lib/mihari/web/endpoints/artifacts.rb +6 -6
  62. data/lib/mihari/web/endpoints/rules.rb +10 -17
  63. data/lib/mihari/web/endpoints/tags.rb +2 -2
  64. data/lib/mihari/web/public/assets/{index-9cc489e6.js → index-28d4c79d.js} +48 -48
  65. data/lib/mihari/web/public/index.html +1 -1
  66. data/lib/mihari.rb +6 -8
  67. data/mihari.gemspec +1 -2
  68. data/requirements.txt +1 -1
  69. metadata +8 -22
  70. data/lib/mihari/analyzers/rule.rb +0 -232
  71. data/lib/mihari/services/rule_proxy.rb +0 -182
@@ -12,45 +12,42 @@ module Mihari
12
12
  # @return [String, nil]
13
13
  attr_reader :api_version
14
14
 
15
+ # @return [Array<Mihari::Models::Artifact>]
16
+ attr_accessor :artifacts
17
+
15
18
  #
16
- # @param [Array<Mihari::Artifact>] artifacts
17
19
  # @param [Mihari::Services::Rule] rule
18
20
  # @param [Hash, nil] options
19
21
  # @param [Hash] **params
20
22
  #
21
- def initialize(artifacts:, rule:, options: nil, **params)
22
- super(artifacts: artifacts, rule: rule, options: options)
23
+ def initialize(rule:, options: nil, **params)
24
+ super(rule: rule, options: options)
23
25
 
24
26
  @url = params[:url] || Mihari.config.thehive_url
25
27
  @api_key = params[:api_key] || Mihari.config.thehive_api_key
26
28
  @api_version = params[:api_version] || Mihari.config.thehive_api_version
29
+
30
+ @artifacts = []
27
31
  end
28
32
 
33
+ #
29
34
  # @return [Boolean]
30
- def valid?
31
- unless url? && api_key?
32
- Mihari.logger.info("TheHive URL is not set") unless url?
33
- Mihari.logger.info("TheHive API key is not set") unless api_key?
34
- return false
35
- end
36
-
37
- unless ping?
38
- Mihari.logger.info("TheHive URL (#{url}) is not reachable")
39
- return false
40
- end
41
-
42
- true
35
+ #
36
+ def configured?
37
+ api_key? && url?
43
38
  end
44
39
 
45
40
  #
46
41
  # Create a Hive alert
47
42
  #
48
- # @return [::MISP::Event]
43
+ # @param [Array<Mihari::Models::Artifact>] artifacts
49
44
  #
50
- def emit
45
+ def emit(artifacts)
51
46
  return if artifacts.empty?
52
47
 
53
- client.alert(payload)
48
+ @artifacts = artifacts
49
+
50
+ client.alert payload
54
51
  end
55
52
 
56
53
  #
@@ -146,31 +143,6 @@ module Mihari
146
143
  source_ref: "1"
147
144
  }
148
145
  end
149
-
150
- #
151
- # Check whether a URL is reachable or not
152
- #
153
- # @return [Boolean]
154
- #
155
- def ping?
156
- base_url = url.end_with?("/") ? url[0..-2] : url
157
-
158
- if normalized_api_version.nil?
159
- # for v4
160
- base_url = url.end_with?("/") ? url[0..-2] : url
161
- public_url = "#{base_url}/index.html"
162
- else
163
- # for v5
164
- public_url = "#{base_url}/api/v1/status/public"
165
- end
166
-
167
- http = Net::Ping::HTTP.new(public_url)
168
-
169
- # use GET for v5
170
- http.get_request = true if normalized_api_version
171
-
172
- http.ping?
173
- end
174
146
  end
175
147
  end
176
148
  end
@@ -57,23 +57,41 @@ module Mihari
57
57
  # @return [String, nil]
58
58
  attr_reader :template
59
59
 
60
+ # @return [Array<Mihari::Models::Artifact>]
61
+ attr_accessor :artifacts
62
+
60
63
  #
61
- # @param [Array<Mihari::Artifact>] artifacts
62
64
  # @param [Mihari::Services::Rule] rule
63
65
  # @param [Hash] **options
64
66
  #
65
- def initialize(artifacts:, rule:, options: nil, **params)
66
- super(artifacts: artifacts, rule: rule, options: options)
67
+ def initialize(rule:, options: nil, **params)
68
+ super(rule: rule, options: options)
67
69
 
68
70
  @url = Addressable::URI.parse(params[:url])
69
71
  @headers = params[:headers] || {}
70
72
  @method = params[:method] || "POST"
71
73
  @template = params[:template]
74
+
75
+ @artifacts = []
76
+ end
77
+
78
+ #
79
+ # @return [Boolean]
80
+ #
81
+ def configured?
82
+ return false if url.nil?
83
+
84
+ %w[http https].include? url.scheme.downcase
72
85
  end
73
86
 
74
- def emit
87
+ #
88
+ # @param [Array<Mihari::Models::Artifact>] artifacts
89
+ #
90
+ def emit(artifacts)
75
91
  return if artifacts.empty?
76
92
 
93
+ @artifacts = artifacts
94
+
77
95
  # returns body to prevent Parallel issue (Parallel fails to handle HTTP:Response object)
78
96
  case method
79
97
  when "GET"
@@ -83,12 +101,6 @@ module Mihari
83
101
  end
84
102
  end
85
103
 
86
- def valid?
87
- return false if url.nil?
88
-
89
- %w[http https].include? url.scheme.downcase
90
- end
91
-
92
104
  private
93
105
 
94
106
  def http
@@ -101,17 +113,15 @@ module Mihari
101
113
  # @return [String]
102
114
  #
103
115
  def rendered_template
104
- [].tap do |out|
105
- options = {}
106
- options[:template] = File.read(template) unless template.nil?
107
-
108
- payload_template = PayloadTemplate.new(
109
- artifacts: artifacts,
110
- rule: rule,
111
- options: options
112
- )
113
- out << payload_template.result
114
- end.first
116
+ options = {}
117
+ options[:template] = File.read(template) unless template.nil?
118
+
119
+ payload_template = PayloadTemplate.new(
120
+ artifacts: artifacts,
121
+ rule: rule,
122
+ options: options
123
+ )
124
+ payload_template.result
115
125
  end
116
126
 
117
127
  #
@@ -2,12 +2,7 @@
2
2
 
3
3
  module Mihari
4
4
  module Enrichers
5
- class Base < Mihari::Base
6
- include Mixins::Configurable
7
- include Mixins::Retriable
8
-
9
- include Dry::Monads[:result, :try]
10
-
5
+ class Base < Actor
11
6
  def initialize(options: nil)
12
7
  super(options: options)
13
8
  end
@@ -34,7 +34,7 @@ module Mihari
34
34
  parser = record.parser
35
35
  return nil if parser.available?
36
36
 
37
- whois_record = WhoisRecord.new(
37
+ whois_record = Models::WhoisRecord.new(
38
38
  domain: domain,
39
39
  created_on: get_created_on(parser),
40
40
  updated_on: get_updated_on(parser),
@@ -1,88 +1,90 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- class Alert < ActiveRecord::Base
5
- has_many :taggings, dependent: :destroy
6
- has_many :artifacts, dependent: :destroy
7
- has_many :tags, through: :taggings
8
-
9
- belongs_to :rule
10
-
11
- class << self
12
- #
13
- # Search alerts
14
- #
15
- # @param [Structs::Filters::Alert::SearchFilterWithPagination] filter
16
- #
17
- # @return [Array<Alert>]
18
- #
19
- def search(filter)
20
- limit = filter.limit.to_i
21
- raise ArgumentError, "limit should be bigger than zero" unless limit.positive?
22
-
23
- page = filter.page.to_i
24
- raise ArgumentError, "page should be bigger than zero" unless page.positive?
25
-
26
- offset = (page - 1) * limit
27
-
28
- relation = build_relation(filter.without_pagination)
29
-
30
- alert_ids = relation.limit(limit).offset(offset).order(id: :desc).pluck(:id).uniq
31
- eager_load(:artifacts, :tags).where(id: [alert_ids]).order(id: :desc)
32
- end
33
-
34
- #
35
- # Count alerts
36
- #
37
- # @param [String, nil] artifact_data
38
- #
39
- # @return [Integer]
40
- #
41
- def count(filter)
42
- relation = build_relation(filter)
43
- relation.distinct("alerts.id").count
44
- end
4
+ module Models
5
+ class Alert < ActiveRecord::Base
6
+ has_many :taggings, dependent: :destroy
7
+ has_many :artifacts, dependent: :destroy
8
+ has_many :tags, through: :taggings
9
+
10
+ belongs_to :rule
11
+
12
+ class << self
13
+ #
14
+ # Search alerts
15
+ #
16
+ # @param [Structs::Filters::Alert::SearchFilterWithPagination] filter
17
+ #
18
+ # @return [Array<Alert>]
19
+ #
20
+ def search(filter)
21
+ limit = filter.limit.to_i
22
+ raise ArgumentError, "limit should be bigger than zero" unless limit.positive?
23
+
24
+ page = filter.page.to_i
25
+ raise ArgumentError, "page should be bigger than zero" unless page.positive?
26
+
27
+ offset = (page - 1) * limit
28
+
29
+ relation = build_relation(filter.without_pagination)
30
+
31
+ alert_ids = relation.limit(limit).offset(offset).order(id: :desc).pluck(:id).uniq
32
+ eager_load(:artifacts, :tags).where(id: [alert_ids]).order(id: :desc)
33
+ end
45
34
 
46
- private
47
-
48
- #
49
- # @param [Structs::Filters::Alert::SearchFilter] filter
50
- #
51
- # @return [Array<Integer>]
52
- #
53
- def get_artifact_ids_by_filter(filter)
54
- artifact_ids = []
55
-
56
- if filter.artifact_data
57
- artifact = Artifact.where(data: filter.artifact_data)
58
- artifact_ids = artifact.pluck(:id)
59
- # set invalid ID if nothing is matched with the filters
60
- artifact_ids = [-1] if artifact_ids.empty?
35
+ #
36
+ # Count alerts
37
+ #
38
+ # @param [String, nil] artifact_data
39
+ #
40
+ # @return [Integer]
41
+ #
42
+ def count(filter)
43
+ relation = build_relation(filter)
44
+ relation.distinct("alerts.id").count
61
45
  end
62
46
 
63
- artifact_ids
64
- end
47
+ private
65
48
 
66
- #
67
- # @param [Structs::Filters::Alert::SearchFilter] filter
68
- #
69
- # @return [Mihari::Alert]
70
- #
71
- def build_relation(filter)
72
- artifact_ids = get_artifact_ids_by_filter(filter)
49
+ #
50
+ # @param [Structs::Filters::Alert::SearchFilter] filter
51
+ #
52
+ # @return [Array<Integer>]
53
+ #
54
+ def get_artifact_ids_by_filter(filter)
55
+ artifact_ids = []
73
56
 
74
- relation = self
75
- relation = relation.includes(:artifacts, :tags)
57
+ if filter.artifact_data
58
+ artifact = Artifact.where(data: filter.artifact_data)
59
+ artifact_ids = artifact.pluck(:id)
60
+ # set invalid ID if nothing is matched with the filters
61
+ artifact_ids = [-1] if artifact_ids.empty?
62
+ end
76
63
 
77
- relation = relation.where(artifacts: { id: artifact_ids }) unless artifact_ids.empty?
78
- relation = relation.where(tags: { name: filter.tag_name }) if filter.tag_name
64
+ artifact_ids
65
+ end
66
+
67
+ #
68
+ # @param [Structs::Filters::Alert::SearchFilter] filter
69
+ #
70
+ # @return [Mihari::Models::Alert]
71
+ #
72
+ def build_relation(filter)
73
+ artifact_ids = get_artifact_ids_by_filter(filter)
74
+
75
+ relation = self
76
+ relation = relation.includes(:artifacts, :tags)
79
77
 
80
- relation = relation.where(rule_id: filter.rule_id) if filter.rule_id
78
+ relation = relation.where(artifacts: { id: artifact_ids }) unless artifact_ids.empty?
79
+ relation = relation.where(tags: { name: filter.tag_name }) if filter.tag_name
81
80
 
82
- relation = relation.where("alerts.created_at >= ?", filter.from_at) if filter.from_at
83
- relation = relation.where("alerts.created_at <= ?", filter.to_at) if filter.to_at
81
+ relation = relation.where(rule_id: filter.rule_id) if filter.rule_id
84
82
 
85
- relation
83
+ relation = relation.where("alerts.created_at >= ?", filter.from_at) if filter.from_at
84
+ relation = relation.where("alerts.created_at <= ?", filter.to_at) if filter.to_at
85
+
86
+ relation
87
+ end
86
88
  end
87
89
  end
88
90
  end