mihari 5.1.4 → 5.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mihari/commands/web.rb +12 -8
  3. data/lib/mihari/entities/artifact.rb +3 -2
  4. data/lib/mihari/mixins/configurable.rb +3 -1
  5. data/lib/mihari/models/alert.rb +16 -12
  6. data/lib/mihari/models/rule.rb +1 -2
  7. data/lib/mihari/schemas/rule.rb +1 -0
  8. data/lib/mihari/structs/filters.rb +1 -15
  9. data/lib/mihari/structs/rule.rb +1 -1
  10. data/lib/mihari/version.rb +1 -1
  11. data/lib/mihari/web/app.rb +9 -7
  12. data/lib/mihari/web/endpoints/alerts.rb +7 -20
  13. data/lib/mihari/web/endpoints/rules.rb +4 -11
  14. data/lib/mihari/web/public/assets/{fa-brands-400-2ef6fdde.ttf → fa-brands-400-20c4a58b.ttf} +0 -0
  15. data/lib/mihari/web/public/assets/fa-brands-400-74833209.woff2 +0 -0
  16. data/lib/mihari/web/public/assets/{fa-regular-400-12dea17b.ttf → fa-regular-400-528d022d.ttf} +0 -0
  17. data/lib/mihari/web/public/assets/fa-regular-400-8e7e5ea1.woff2 +0 -0
  18. data/lib/mihari/web/public/assets/{fa-solid-900-67a880b4.ttf → fa-solid-900-67a65763.ttf} +0 -0
  19. data/lib/mihari/web/public/assets/fa-solid-900-7152a693.woff2 +0 -0
  20. data/lib/mihari/web/public/assets/{fa-v4compatibility-8d9500e8.ttf → fa-v4compatibility-0515a423.ttf} +0 -0
  21. data/lib/mihari/web/public/assets/fa-v4compatibility-694a17c3.woff2 +0 -0
  22. data/lib/mihari/web/public/assets/index-cbe1734c.js +50 -0
  23. data/lib/mihari/web/public/assets/index-eed1bcd8.css +5 -0
  24. data/lib/mihari/web/public/index.html +2 -2
  25. data/lib/mihari/web/public/redoc-static.html +7 -4
  26. data/lib/mihari.rb +2 -0
  27. data/mihari.gemspec +13 -13
  28. metadata +40 -40
  29. data/lib/mihari/web/public/assets/fa-brands-400-f4617423.woff2 +0 -0
  30. data/lib/mihari/web/public/assets/fa-regular-400-7ba24c41.woff2 +0 -0
  31. data/lib/mihari/web/public/assets/fa-solid-900-e2c5cf54.woff2 +0 -0
  32. data/lib/mihari/web/public/assets/fa-v4compatibility-7c377405.woff2 +0 -0
  33. data/lib/mihari/web/public/assets/index-625e95fe.css +0 -5
  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: 52425b89ab9cc2d25fcfd1391661446b938b35596fb5b3a68647f90038a3f848
4
- data.tar.gz: 3ae2917e1de1e89e08089e40629eb8652a173cf03dd790137b55c1eb80d35706
3
+ metadata.gz: 5ce7e22d3d54cc3e66fec62b494ce5f9def9107129513cd51803c71b8ae48b18
4
+ data.tar.gz: fd85f33a666a77a8b43d12dc3234ab2656cd843c8f7e23025c1a5f076947fbcf
5
5
  SHA512:
6
- metadata.gz: f06c60e30e815c2634c6309b86b323199393d3c6b02e416244d884c60916584fdbe2250a0366ddf002957b240eadce64c2dc25b740ce80ecce126a78eab29b87
7
- data.tar.gz: 2e2e71da88d3ba33b58861b97b3987fb8f64fe581cc7f15cf488227df4621ec03d3c91968e126f787c3b23c65141a81817d4b0785f5aa9065e6fcfa396a99ed5
6
+ metadata.gz: a17809e3c6f52e7d37f6df2fc92b0ad5a1d134556e6ef13cb64b3802074a44f2ecd0c2475ab632e5da0f57e7c68a44fca15f722f31124fbb9e8af512f46aa2ac
7
+ data.tar.gz: d777cac77ceeb2a98c8f37a8e001d9240403b47a1e2326a6f1bc42c3cf48e7813dd69ab2260ce5a29a34b7b9b3e0ef54fa541785f8b5dbc82b787381e549525e
@@ -11,17 +11,21 @@ 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"
16
+ method_option :open, type: :boolean, default: true, desc: "Whether to open the app in browser or not"
14
17
  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
-
18
+ Mihari.config.hide_config_values = options["hide_config_values"]
21
19
  # set rack env as production
22
20
  ENV["RACK_ENV"] ||= "production"
23
-
24
- Mihari::App.run!(port: port, host: host, threads: threads, verbose: verbose, worker_timeout: worker_timeout)
21
+ Mihari::App.run!(
22
+ port: options["port"],
23
+ host: options["host"],
24
+ threads: options["threads"],
25
+ verbose: options["verbose"],
26
+ worker_timeout: options["worker_timeout"],
27
+ open: options["open"]
28
+ )
25
29
  end
26
30
  end
27
31
  end
@@ -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.4"
4
+ VERSION = "5.2.1"
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: ["/"]
@@ -42,7 +44,7 @@ module Mihari
42
44
  end.to_app
43
45
  end
44
46
 
45
- def run!(port: 9292, host: "localhost", threads: "0:5", verbose: false, worker_timeout: 60)
47
+ def run!(port: 9292, host: "localhost", threads: "0:5", verbose: false, worker_timeout: 60, open: true)
46
48
  url = "http://#{host}:#{port}"
47
49
 
48
50
  # set maximum number of threads to use as PARALLEL_PROCESSOR_COUNT (if it is not set)
@@ -50,15 +52,15 @@ 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|
61
- Launchy.open(url) if ENV["RACK_ENV"] != "development"
62
+ ) do |_|
63
+ Launchy.open(url) if ENV["RACK_ENV"] != "development" && open
62
64
  rescue Launchy::CommandNotFoundError
63
65
  # ref. https://github.com/ninoseki/mihari/issues/477
64
66
  # do nothing
@@ -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