mihari 4.7.1 → 4.7.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +1 -0
  3. data/lib/mihari/analyzers/rule.rb +0 -1
  4. data/lib/mihari/commands/init.rb +25 -2
  5. data/lib/mihari/commands/search.rb +2 -7
  6. data/lib/mihari/commands/validator.rb +10 -5
  7. data/lib/mihari/errors.rb +2 -0
  8. data/lib/mihari/models/alert.rb +6 -1
  9. data/lib/mihari/models/geolocation.rb +2 -4
  10. data/lib/mihari/models/port.rb +1 -1
  11. data/lib/mihari/models/rule.rb +7 -2
  12. data/lib/mihari/schemas/rule.rb +5 -0
  13. data/lib/mihari/structs/filters.rb +71 -0
  14. data/lib/mihari/structs/ipinfo.rb +4 -4
  15. data/lib/mihari/structs/rule.rb +188 -144
  16. data/lib/mihari/version.rb +1 -1
  17. data/lib/mihari/web/endpoints/alerts.rb +1 -1
  18. data/lib/mihari/web/endpoints/rules.rb +13 -4
  19. data/lib/mihari/web/public/index.html +1 -1
  20. data/lib/mihari/web/public/redoc-static.html +796 -763
  21. data/lib/mihari/web/public/static/css/chunk-vendors.5013d549.css +7 -0
  22. data/lib/mihari/web/public/static/js/app.524d9ed2.js +2 -0
  23. data/lib/mihari/web/public/static/js/app.524d9ed2.js.map +1 -0
  24. data/lib/mihari/web/public/static/js/{chunk-vendors.dde2116c.js → chunk-vendors.64580a1f.js} +7 -7
  25. data/lib/mihari/web/public/static/js/chunk-vendors.64580a1f.js.map +1 -0
  26. data/lib/mihari.rb +1 -2
  27. data/mihari.gemspec +16 -16
  28. data/sig/lib/mihari/cli/base.rbs +0 -2
  29. data/sig/lib/mihari/models/alert.rbs +3 -3
  30. data/sig/lib/mihari/models/rule.rbs +2 -2
  31. data/sig/lib/mihari/structs/filters.rbs +40 -0
  32. data/sig/lib/mihari/structs/ipinfo.rbs +2 -2
  33. data/sig/lib/mihari/structs/rule.rbs +36 -43
  34. metadata +41 -47
  35. data/lib/mihari/mixins/rule.rb +0 -84
  36. data/lib/mihari/structs/alert.rb +0 -44
  37. data/lib/mihari/web/public/static/css/chunk-vendors.06251949.css +0 -7
  38. data/lib/mihari/web/public/static/js/app-legacy.9d5c9c3d.js +0 -2
  39. data/lib/mihari/web/public/static/js/app-legacy.9d5c9c3d.js.map +0 -1
  40. data/lib/mihari/web/public/static/js/app.823b5af7.js +0 -2
  41. data/lib/mihari/web/public/static/js/app.823b5af7.js.map +0 -1
  42. data/lib/mihari/web/public/static/js/chunk-vendors-legacy.b110c129.js +0 -25
  43. data/lib/mihari/web/public/static/js/chunk-vendors-legacy.b110c129.js.map +0 -1
  44. data/lib/mihari/web/public/static/js/chunk-vendors.dde2116c.js.map +0 -1
  45. data/sig/lib/mihari/mixins/rule.rbs +0 -36
  46. data/sig/lib/mihari/structs/alert.rbs +0 -27
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 38f68ec091b2469095f321f104c1e238599c56afa7f55763920cfeef0df03ca0
4
- data.tar.gz: 625bbd99f92e4af768b76db15ab72cd53dba6a04150acc3ddfe7feb7d1a82bf7
3
+ metadata.gz: 4afac31be7c26f9357097db69a0f5e8e175b9d7bb14f66adc85d38f8d5606dad
4
+ data.tar.gz: c6259db1f4f4ed657993e817d3f8fafec5632370f0d21e761ee1dba5fb490321
5
5
  SHA512:
6
- metadata.gz: fe847e37658ede26d1910e8c73ee1726e2b32335571e03c97c891b2d799380397ed1d32de7e4ed31ae57c861b4a6aa74433454d4871e38e2137100ce342894a1
7
- data.tar.gz: 688c3adcf6ebb754d02b0fecde033602b47fe23ae11fffaff329ce53b93c268ab274319fdb630c819163856ce9708f2658bfe6fe7790e54ab1488e8d2af90245
6
+ metadata.gz: 0dff43154e317cc31dc13a85f3d7ae14c362ea23ccec2f0d48cff1f12ffb5a0f8f292465888ca9044234963bf5ba5fe5c4ce920111d9b0a8f083c067896e2efa
7
+ data.tar.gz: 46e9e1a2fd39d1bb61d8f3db73362074df83d80c5b37c39923a71ed9388059eef249b257116944016c33199c17dd283bbae41325ea29e74dd9759f0acfd6a14c
@@ -56,6 +56,7 @@ jobs:
56
56
  uses: ruby/setup-ruby@v1
57
57
  with:
58
58
  ruby-version: ${{ matrix.ruby }}
59
+ bundler: latest
59
60
  bundler-cache: true
60
61
 
61
62
  - name: Test with PostgreSQL
@@ -39,7 +39,6 @@ module Mihari
39
39
 
40
40
  class Rule < Base
41
41
  include Mixins::DisallowedDataValue
42
- include Mixins::Rule
43
42
 
44
43
  option :title
45
44
  option :description
@@ -3,8 +3,6 @@
3
3
  module Mihari
4
4
  module Commands
5
5
  module Initialization
6
- include Mixins::Rule
7
-
8
6
  def self.included(thor)
9
7
  thor.class_eval do
10
8
  desc "rule", "Create a rule file"
@@ -21,6 +19,31 @@ module Mihari
21
19
 
22
20
  Mihari.logger.info "The rule file is initialized as #{filename}."
23
21
  end
22
+
23
+ no_commands do
24
+ #
25
+ # Returns a template for rule
26
+ #
27
+ # @return [String] A template for rule
28
+ #
29
+ def rule_template
30
+ rule = Structs::Rule.from_path_or_id File.expand_path("../templates/rule.yml.erb", __dir__)
31
+ rule.yaml
32
+ end
33
+
34
+ #
35
+ # Create (blank) rule file
36
+ #
37
+ # @param [String] filename
38
+ # @param [Dry::Files] files
39
+ # @param [String] template
40
+ #
41
+ # @return [nil]
42
+ #
43
+ def initialize_rule_yaml(filename, files = Dry::Files.new, template: rule_template)
44
+ files.write(filename, template)
45
+ end
46
+ end
24
47
  end
25
48
  end
26
49
  end
@@ -4,7 +4,6 @@ module Mihari
4
4
  module Commands
5
5
  module Search
6
6
  include Mixins::Database
7
- include Mixins::Rule
8
7
  include Mixins::ErrorNotification
9
8
 
10
9
  def self.included(thor)
@@ -12,14 +11,10 @@ module Mihari
12
11
  desc "search [RULE]", "Search by a rule"
13
12
  method_option :yes, type: :boolean, aliases: "-y", desc: "yes to overwrite the rule in the database"
14
13
  def search_by_rule(path_or_id)
15
- rule = load_rule(path_or_id)
14
+ rule = Structs::Rule.from_path_or_id path_or_id
16
15
 
17
16
  # validate
18
- begin
19
- validate_rule! rule
20
- rescue RuleValidationError => e
21
- raise e
22
- end
17
+ rule.validate!
23
18
 
24
19
  # check update
25
20
  id = rule.id
@@ -3,16 +3,21 @@
3
3
  module Mihari
4
4
  module Commands
5
5
  module Validator
6
- include Mixins::Rule
7
-
8
6
  def self.included(thor)
9
7
  thor.class_eval do
10
- desc "rule [PATH]", "Validate format of a rule file"
8
+ desc "rule [PATH]", "Validate rule file format"
9
+ #
10
+ # Validate format of a rule
11
+ #
12
+ # @param [String] path
13
+ #
14
+ # @return [nil]
15
+ #
11
16
  def rule(path)
12
- rule = load_rule(path)
17
+ rule = Structs::Rule.from_path_or_id(path)
13
18
 
14
19
  begin
15
- validate_rule! rule
20
+ rule.validate!
16
21
  Mihari.logger.info "Valid format. The input is parsed as the following:\n#{rule.data.to_yaml}"
17
22
  rescue RuleValidationError
18
23
  nil
data/lib/mihari/errors.rb CHANGED
@@ -15,6 +15,8 @@ module Mihari
15
15
 
16
16
  class RuleValidationError < Error; end
17
17
 
18
+ class YAMLSyntaxError < Error; end
19
+
18
20
  class ConfigurationError < Error; end
19
21
 
20
22
  class HTTPError < Error; end
@@ -12,7 +12,7 @@ module Mihari
12
12
  #
13
13
  # Search alerts
14
14
  #
15
- # @param [Structs::Alert::SearchFilterWithPagination] filter
15
+ # @param [Structs::Filters::Alert::SearchFilterWithPagination] filter
16
16
  #
17
17
  # @return [Array<Alert>]
18
18
  #
@@ -58,6 +58,11 @@ module Mihari
58
58
 
59
59
  private
60
60
 
61
+ #
62
+ # @param [Structs::Filters::Alert::SearchFilter] filter
63
+ #
64
+ # @return [Mihari::Alert]
65
+ #
61
66
  def build_relation(filter)
62
67
  artifact_ids = []
63
68
  artifact = Artifact.includes(:autonomous_system, :dns_records, :reverse_dns_names)
@@ -17,11 +17,9 @@ module Mihari
17
17
  def build_by_ip(ip)
18
18
  res = Enrichers::IPInfo.query(ip)
19
19
 
20
- unless res.nil?
21
- return new(country: NormalizeCountry(res.country_code, to: :short), country_code: res.country_code)
22
- end
20
+ return nil if res&.country_code.nil?
23
21
 
24
- nil
22
+ new(country: NormalizeCountry(res.country_code, to: :short), country_code: res.country_code)
25
23
  end
26
24
  end
27
25
  end
@@ -14,7 +14,7 @@ module Mihari
14
14
  #
15
15
  def build_by_ip(ip)
16
16
  res = Enrichers::Shodan.query(ip)
17
- return if res.nil?
17
+ return [] if res.nil?
18
18
 
19
19
  res.ports.map { |port| new(port: port) }
20
20
  end
@@ -28,7 +28,7 @@ module Mihari
28
28
  #
29
29
  # Search rules
30
30
  #
31
- # @param [Structs::Rule::SearchFilterWithPagination] filter
31
+ # @param [Structs::Filters::Rule::SearchFilterWithPagination] filter
32
32
  #
33
33
  # @return [Array<Rule>]
34
34
  #
@@ -51,7 +51,7 @@ module Mihari
51
51
  #
52
52
  # Count alerts
53
53
  #
54
- # @param [Structs::Rule::SearchFilterWithPagination] filter
54
+ # @param [Structs::Filters::Rule::SearchFilterWithPagination] filter
55
55
  #
56
56
  # @return [Integer]
57
57
  #
@@ -62,6 +62,11 @@ module Mihari
62
62
 
63
63
  private
64
64
 
65
+ #
66
+ # @param [Structs::Filters::Rule::SearchFilter] filter
67
+ #
68
+ # @return [Mihari::Rule]
69
+ #
65
70
  def build_relation(filter)
66
71
  relation = self
67
72
  relation = relation.includes(alerts: :tags)
@@ -13,6 +13,11 @@ module Mihari
13
13
  optional(:tags).value(array[:string]).default([])
14
14
  optional(:id).value(:string)
15
15
 
16
+ optional(:status).value(:string)
17
+
18
+ optional(:related).value(array[:string])
19
+ optional(:references).value(array[:string])
20
+
16
21
  optional(:author).value(:string)
17
22
  optional(:created_on).value(:date)
18
23
  optional(:updated_on).value(:date)
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mihari
4
+ module Structs
5
+ module Filters
6
+ module Alert
7
+ class SearchFilter < Dry::Struct
8
+ attribute? :artifact_data, Types::String.optional
9
+ attribute? :description, Types::String.optional
10
+ attribute? :source, Types::String.optional
11
+ attribute? :tag_name, Types::String.optional
12
+ attribute? :title, Types::String.optional
13
+ attribute? :from_at, Types::DateTime.optional
14
+ 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
+ end
23
+
24
+ class SearchFilterWithPagination < SearchFilter
25
+ attribute? :page, Types::Int.default(1)
26
+ attribute? :limit, Types::Int.default(10)
27
+
28
+ def without_pagination
29
+ SearchFilter.new(
30
+ artifact_data: artifact_data,
31
+ description: description,
32
+ from_at: from_at,
33
+ source: source,
34
+ 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
40
+ )
41
+ end
42
+ end
43
+ end
44
+
45
+ module Rule
46
+ class SearchFilter < Dry::Struct
47
+ attribute? :description, Types::String.optional
48
+ attribute? :tag_name, Types::String.optional
49
+ attribute? :title, Types::String.optional
50
+ attribute? :from_at, Types::DateTime.optional
51
+ attribute? :to_at, Types::DateTime.optional
52
+ end
53
+
54
+ class SearchFilterWithPagination < SearchFilter
55
+ attribute? :page, Types::Int.default(1)
56
+ attribute? :limit, Types::Int.default(10)
57
+
58
+ def without_pagination
59
+ SearchFilter.new(
60
+ description: description,
61
+ from_at: from_at,
62
+ tag_name: tag_name,
63
+ title: title,
64
+ to_at: to_at
65
+ )
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -6,8 +6,8 @@ module Mihari
6
6
  class Response < Dry::Struct
7
7
  attribute :ip, Types::String
8
8
  attribute :hostname, Types::String.optional
9
- attribute :loc, Types::String
10
- attribute :country_code, Types::String
9
+ attribute :loc, Types::String.optional
10
+ attribute :country_code, Types::String.optional
11
11
  attribute :asn, Types::Integer.optional
12
12
 
13
13
  class << self
@@ -23,9 +23,9 @@ module Mihari
23
23
 
24
24
  new(
25
25
  ip: d.fetch("ip"),
26
- loc: d.fetch("loc"),
26
+ loc: d["loc"],
27
27
  hostname: d["hostname"],
28
- country_code: d.fetch("country"),
28
+ country_code: d["country"],
29
29
  asn: asn
30
30
  )
31
31
  end