mihari 5.1.3 → 5.2.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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mihari/commands/web.rb +5 -8
  3. data/lib/mihari/emitters/misp.rb +4 -4
  4. data/lib/mihari/entities/artifact.rb +3 -2
  5. data/lib/mihari/mixins/configurable.rb +3 -1
  6. data/lib/mihari/models/alert.rb +16 -12
  7. data/lib/mihari/models/rule.rb +1 -2
  8. data/lib/mihari/schemas/rule.rb +1 -0
  9. data/lib/mihari/structs/filters.rb +1 -15
  10. data/lib/mihari/structs/rule.rb +1 -1
  11. data/lib/mihari/version.rb +1 -1
  12. data/lib/mihari/web/app.rb +7 -5
  13. data/lib/mihari/web/endpoints/alerts.rb +7 -20
  14. data/lib/mihari/web/endpoints/rules.rb +4 -11
  15. data/lib/mihari/web/public/assets/{fa-brands-400-2ef6fdde.ttf → fa-brands-400-20c4a58b.ttf} +0 -0
  16. data/lib/mihari/web/public/assets/fa-brands-400-74833209.woff2 +0 -0
  17. data/lib/mihari/web/public/assets/{fa-regular-400-12dea17b.ttf → fa-regular-400-528d022d.ttf} +0 -0
  18. data/lib/mihari/web/public/assets/fa-regular-400-8e7e5ea1.woff2 +0 -0
  19. data/lib/mihari/web/public/assets/{fa-solid-900-67a880b4.ttf → fa-solid-900-67a65763.ttf} +0 -0
  20. data/lib/mihari/web/public/assets/fa-solid-900-7152a693.woff2 +0 -0
  21. data/lib/mihari/web/public/assets/{fa-v4compatibility-8d9500e8.ttf → fa-v4compatibility-0515a423.ttf} +0 -0
  22. data/lib/mihari/web/public/assets/fa-v4compatibility-694a17c3.woff2 +0 -0
  23. data/lib/mihari/web/public/assets/index-9948ee35.js +50 -0
  24. data/lib/mihari/web/public/assets/{index-625e95fe.css → index-d88cc3f1.css} +2 -2
  25. data/lib/mihari/web/public/index.html +2 -2
  26. data/lib/mihari/web/public/redoc-static.html +7 -4
  27. data/lib/mihari.rb +2 -0
  28. data/mihari.gemspec +8 -8
  29. metadata +30 -30
  30. data/lib/mihari/web/public/assets/fa-brands-400-f4617423.woff2 +0 -0
  31. data/lib/mihari/web/public/assets/fa-regular-400-7ba24c41.woff2 +0 -0
  32. data/lib/mihari/web/public/assets/fa-solid-900-e2c5cf54.woff2 +0 -0
  33. data/lib/mihari/web/public/assets/fa-v4compatibility-7c377405.woff2 +0 -0
  34. data/lib/mihari/web/public/assets/index-7d0fb8c4.js +0 -50
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dd278975834369b3274b38b590f12d4f2fed97d7626363ff3cfbc9845195b386
4
- data.tar.gz: 864ee1deae0bdb0390d9e330eb76c993860c37189dce0d23c9793b012f338275
3
+ metadata.gz: 0c98bfd494945e88b92bbf939e9ea94d5d4161cc35ae7d7a0d01ead7a30a1f09
4
+ data.tar.gz: 4794020ea3ad71fe70016030abd52063d523c7bbf7a31d9129fe8a2b76f43bc5
5
5
  SHA512:
6
- metadata.gz: 258731713400cb40f6da8f24e6ce0213b883eeff98a22a16a88c84568633ad02ad3e18f4e70e6faea428a29bdcdc257b6ec3c86ebea7979065ec2e42ab445c47
7
- data.tar.gz: 6c8beea0c3f4aa42f8fb8217a05c6256a6a8ea10daceb05d05ed2884d8b53c86195c765fa70cf35ea19c41d959fd6df114fa2b609893e828def028f5b16b5fc4
6
+ metadata.gz: aff9062a772dd86fd202e9112d92c923c815dba5d15b83b567c8ff00a72803ece1674201daeffc2934beba54f7d9ff2e929e1d570de9fe21c503bd89c6648c13
7
+ data.tar.gz: 8ab4db7d0385ac7301c158c95cba0855cc002c9e115911e497011e0705c97fdc5205a6f47e07917d867ba5270f3e4b24f80c38a407c34a2a3db717b9fd5ef732
@@ -11,17 +11,14 @@ module Mihari
11
11
  method_option :threads, type: :string, default: "0:5", desc: "min:max threads to use"
12
12
  method_option :verbose, type: :boolean, default: true, desc: "Report each request"
13
13
  method_option :worker_timeout, type: :numeric, default: 60, desc: "Worker timeout value (in seconds)"
14
+ method_option :hide_config_values, type: :boolean, default: false,
15
+ desc: "Whether to hide config values or not"
14
16
  def web
15
- port = options["port"]
16
- host = options["host"]
17
- threads = options["threads"]
18
- verbose = options["verbose"]
19
- worker_timeout = options["worker_timeout"]
20
-
17
+ Mihari.config.hide_config_values = options["hide_config_values"]
21
18
  # set rack env as production
22
19
  ENV["RACK_ENV"] ||= "production"
23
-
24
- Mihari::App.run!(port: port, host: host, threads: threads, verbose: verbose, worker_timeout: worker_timeout)
20
+ Mihari::App.run!(port: options["port"], host: options["host"], threads: options["threads"],
21
+ verbose: options["verbose"], worker_timeout: options["worker_timeout"])
25
22
  end
26
23
  end
27
24
  end
@@ -45,10 +45,10 @@ module Mihari
45
45
 
46
46
  client.create_event({
47
47
  Event: {
48
- info: rule.title
49
- },
50
- Attribute: artifacts.map { |artifact| build_attribute(artifact) },
51
- Tag: rule.tags.map { |tag| { name: tag } }
48
+ info: rule.title,
49
+ Attribute: artifacts.map { |artifact| build_attribute(artifact) },
50
+ Tag: rule.tags.map { |tag| { name: tag } }
51
+ }
52
52
  })
53
53
  end
54
54
 
@@ -12,11 +12,12 @@ module Mihari
12
12
  expose :data_type, documentation: { type: String, required: true }, as: :dataType
13
13
  expose :source, documentation: { type: String, required: true }
14
14
  expose :tags, documentation: { type: String, is_array: true }
15
-
16
- expose :metadata, documentation: { type: Hash }
17
15
  end
18
16
 
19
17
  class Artifact < BaseArtifact
18
+ # NOTE: do not define metadata in BaseArtifact since metadata can be relatively big
19
+ expose :metadata, documentation: { type: Hash }
20
+
20
21
  expose :autonomous_system, using: Entities::AutonomousSystem,
21
22
  documentation: { type: Entities::AutonomousSystem, required: false }, as: :autonomousSystem
22
23
  expose :geolocation, using: Entities::Geolocation, documentation: { type: Entities::Geolocation, required: false }
@@ -23,7 +23,9 @@ module Mihari
23
23
  return nil if configuration_keys.empty?
24
24
 
25
25
  configuration_keys.map do |key|
26
- { key: key.upcase, value: Mihari.config.send(key) }
26
+ value = Mihari.config.send(key)
27
+ value = "REDACTED" if value && Mihari.config.hide_config_values
28
+ { key: key.upcase, value: value }
27
29
  end
28
30
  end
29
31
 
@@ -48,22 +48,29 @@ module Mihari
48
48
  #
49
49
  # @param [Structs::Filters::Alert::SearchFilter] filter
50
50
  #
51
- # @return [Mihari::Alert]
51
+ # @return [Array<Integer>]
52
52
  #
53
- def build_relation(filter)
53
+ def get_artifact_ids_by_filter(filter)
54
54
  artifact_ids = []
55
- artifact = Artifact.includes(:autonomous_system, :dns_records, :reverse_dns_names)
56
- artifact = artifact.where(data: filter.artifact_data) if filter.artifact_data
57
- artifact = artifact.where(autonomous_system: { asn: filter.asn }) if filter.asn
58
- artifact = artifact.where(dns_records: { value: filter.dns_record }) if filter.dns_record
59
- artifact = artifact.where(reverse_dns_names: { name: filter.reverse_dns_name }) if filter.reverse_dns_name
60
- # get artifact ids if there is any valid filter for artifact
61
- if filter.valid_artifact_filters?
55
+
56
+ if filter.artifact_data
57
+ artifact = Artifact.where(data: filter.artifact_data)
62
58
  artifact_ids = artifact.pluck(:id)
63
59
  # set invalid ID if nothing is matched with the filters
64
60
  artifact_ids = [-1] if artifact_ids.empty?
65
61
  end
66
62
 
63
+ artifact_ids
64
+ end
65
+
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)
73
+
67
74
  relation = self
68
75
  relation = relation.includes(:artifacts, :tags)
69
76
 
@@ -71,9 +78,6 @@ module Mihari
71
78
  relation = relation.where(tags: { name: filter.tag_name }) if filter.tag_name
72
79
 
73
80
  relation = relation.where(rule_id: filter.rule_id) if filter.rule_id
74
- relation = relation.where(title: filter.title) if filter.title
75
-
76
- relation = relation.where("description LIKE ?", "%#{filter.description}%") if filter.description
77
81
 
78
82
  relation = relation.where("alerts.created_at >= ?", filter.from_at) if filter.from_at
79
83
  relation = relation.where("alerts.created_at <= ?", filter.to_at) if filter.to_at
@@ -67,8 +67,7 @@ module Mihari
67
67
 
68
68
  relation = relation.where(alerts: { tags: { name: filter.tag_name } }) if filter.tag_name
69
69
 
70
- relation = relation.where(title: filter.title) if filter.title
71
-
70
+ relation = relation.where("rules.title LIKE ?", "%#{filter.title}%") if filter.title
72
71
  relation = relation.where("rules.description LIKE ?", "%#{filter.description}%") if filter.description
73
72
 
74
73
  relation = relation.where("rules.created_at >= ?", filter.from_at) if filter.from_at
@@ -33,6 +33,7 @@ module Mihari
33
33
  optional(:falsepositives).value(array[:string]).default([])
34
34
 
35
35
  optional(:artifact_lifetime).value(:integer)
36
+ optional(:artifact_ttl).value(:integer)
36
37
 
37
38
  before(:key_coercer) do |result|
38
39
  # it looks like that dry-schema v1.9.1 has an issue with setting an array of schemas as a default value
@@ -6,19 +6,10 @@ module Mihari
6
6
  module Alert
7
7
  class SearchFilter < Dry::Struct
8
8
  attribute? :artifact_data, Types::String.optional
9
- attribute? :description, Types::String.optional
10
9
  attribute? :rule_id, Types::String.optional
11
10
  attribute? :tag_name, Types::String.optional
12
- attribute? :title, Types::String.optional
13
11
  attribute? :from_at, Types::DateTime.optional
14
12
  attribute? :to_at, Types::DateTime.optional
15
- attribute? :asn, Types::Int.optional
16
- attribute? :dns_record, Types::String.optional
17
- attribute? :reverse_dns_name, Types::String.optional
18
-
19
- def valid_artifact_filters?
20
- !(artifact_data || asn || dns_record || reverse_dns_name).nil?
21
- end
22
13
  end
23
14
 
24
15
  class SearchFilterWithPagination < SearchFilter
@@ -28,15 +19,10 @@ module Mihari
28
19
  def without_pagination
29
20
  SearchFilter.new(
30
21
  artifact_data: artifact_data,
31
- description: description,
32
22
  from_at: from_at,
33
23
  rule_id: rule_id,
34
24
  tag_name: tag_name,
35
- title: title,
36
- to_at: to_at,
37
- asn: asn,
38
- dns_record: dns_record,
39
- reverse_dns_name: reverse_dns_name
25
+ to_at: to_at
40
26
  )
41
27
  end
42
28
  end
@@ -133,7 +133,7 @@ module Mihari
133
133
  # @return [Integer, nil]
134
134
  #
135
135
  def artifact_lifetime
136
- @artifact_lifetime ||= data[:artifact_lifetime]
136
+ @artifact_lifetime ||= data[:artifact_lifetime] || data[:artifact_ttl]
137
137
  end
138
138
 
139
139
  #
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- VERSION = "5.1.3"
4
+ VERSION = "5.2.0"
5
5
  end
@@ -1,11 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "launchy"
4
+
4
5
  require "rack"
5
- require "rack/contrib"
6
- require "rack/handler/puma"
6
+ require "rackup"
7
7
  require "rack/cors"
8
8
 
9
+ require "rack/handler/puma"
10
+
9
11
  require "grape-swagger"
10
12
  require "grape-swagger-entity"
11
13
 
@@ -18,7 +20,7 @@ module Mihari
18
20
  class App
19
21
  def initialize
20
22
  @filenames = ["", ".html", "index.html", "/index.html"]
21
- @rack_static = ::Rack::Static.new(
23
+ @rack_static = Rack::Static.new(
22
24
  -> { [404, {}, []] },
23
25
  root: File.expand_path("./public", __dir__),
24
26
  urls: ["/"]
@@ -50,14 +52,14 @@ module Mihari
50
52
  # TODO: is this the best way?
51
53
  _min_thread, max_thread = threads.split(":")
52
54
  ENV["PARALLEL_PROCESSOR_COUNT"] = max_thread if ENV["PARALLEL_PROCESSOR_COUNT"].nil?
53
- Rack::Handler::Puma.run(
55
+ Rackup::Handler::Puma.run(
54
56
  instance,
55
57
  Port: port,
56
58
  Host: host,
57
59
  Threads: threads,
58
60
  Verbose: verbose,
59
61
  worker_timeout: worker_timeout
60
- ) do |_launcher|
62
+ ) do |_|
61
63
  Launchy.open(url) if ENV["RACK_ENV"] != "development"
62
64
  rescue Launchy::CommandNotFoundError
63
65
  # ref. https://github.com/ninoseki/mihari/issues/477
@@ -11,48 +11,35 @@ module Mihari
11
11
  summary: "Search alerts"
12
12
  }
13
13
  params do
14
- optional :page, type: Integer
14
+ optional :page, type: Integer, default: 1
15
+ optional :limit, type: Integer, default: 10
15
16
 
16
17
  optional :artifact, type: String
17
- optional :description, type: String
18
18
  optional :rule_id, type: String
19
19
  optional :tag, type: String
20
- optional :title, type: String
21
20
 
22
21
  optional :fromAt, type: DateTime
23
22
  optional :toAt, type: DateTime
24
-
25
- optional :asn, type: Integer
26
- optional :dnsRecord, type: String
27
- optional :reverseDnsName, type: String
28
23
  end
29
24
  get "/" do
30
25
  filter = params.to_h.to_snake_keys
31
26
 
32
- # set page & limit
33
- page = filter["page"] || 1
34
- filter["page"] = page.to_i
35
-
36
- limit = 10
37
- filter["limit"] = 10
38
-
39
27
  # normalize keys
40
28
  filter["artifact_data"] = filter["artifact"]
41
29
  filter["tag_name"] = filter["tag"]
42
-
43
30
  # symbolize hash keys
44
31
  filter = filter.to_h.symbolize_keys
45
32
 
46
- search_filter_with_pagenation = Structs::Filters::Alert::SearchFilterWithPagination.new(**filter)
47
- alerts = Mihari::Alert.search(search_filter_with_pagenation)
48
- total = Mihari::Alert.count(search_filter_with_pagenation.without_pagination)
33
+ search_filter_with_pagination = Structs::Filters::Alert::SearchFilterWithPagination.new(**filter)
34
+ alerts = Mihari::Alert.search(search_filter_with_pagination)
35
+ total = Mihari::Alert.count(search_filter_with_pagination.without_pagination)
49
36
 
50
37
  present(
51
38
  {
52
39
  alerts: alerts,
53
40
  total: total,
54
- current_page: page,
55
- page_size: limit
41
+ current_page: filter[:page].to_i,
42
+ page_size: filter[:limit].to_i
56
43
  },
57
44
  with: Entities::AlertsWithPagination
58
45
  )
@@ -21,7 +21,8 @@ module Mihari
21
21
  summary: "Search rules"
22
22
  }
23
23
  params do
24
- optional :page, type: Integer
24
+ optional :page, type: Integer, default: 1
25
+ optional :limit, type: Integer, default: 10
25
26
 
26
27
  optional :title, type: String
27
28
  optional :description, type: String
@@ -33,16 +34,8 @@ module Mihari
33
34
  get "/" do
34
35
  filter = params.to_h.to_snake_keys
35
36
 
36
- # set page & limit
37
- page = filter["page"] || 1
38
- filter["page"] = page.to_i
39
-
40
- limit = 10
41
- filter["limit"] = 10
42
-
43
37
  # normalize keys
44
38
  filter["tag_name"] = filter["tag"]
45
-
46
39
  # symbolize hash keys
47
40
  filter = filter.to_h.symbolize_keys
48
41
 
@@ -53,8 +46,8 @@ module Mihari
53
46
  present(
54
47
  { rules: rules,
55
48
  total: total,
56
- current_page: page,
57
- page_size: limit },
49
+ current_page: filter[:page].to_i,
50
+ page_size: filter[:limit].to_i },
58
51
  with: Entities::RulesWithPagination
59
52
  )
60
53
  end