mihari 5.6.1 → 5.6.2

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