mihari 5.6.0 → 5.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. data/docs/analyzers/binaryedge.md +1 -1
  3. data/docs/analyzers/censys.md +1 -1
  4. data/docs/analyzers/circl.md +1 -1
  5. data/docs/analyzers/crtsh.md +1 -1
  6. data/docs/analyzers/dnstwister.md +1 -1
  7. data/docs/analyzers/greynoise.md +1 -1
  8. data/docs/analyzers/hunterhow.md +1 -1
  9. data/docs/analyzers/index.md +29 -15
  10. data/docs/analyzers/onyphe.md +1 -1
  11. data/docs/analyzers/otx.md +2 -2
  12. data/docs/analyzers/passivetotal.md +2 -2
  13. data/docs/analyzers/pulsedive.md +2 -2
  14. data/docs/analyzers/securitytrails.md +2 -2
  15. data/docs/analyzers/shodan.md +1 -1
  16. data/docs/analyzers/urlscan.md +3 -3
  17. data/docs/analyzers/virustotal.md +2 -2
  18. data/docs/analyzers/virustotal_intelligence.md +4 -4
  19. data/docs/analyzers/zoomeye.md +5 -0
  20. data/docs/enrichers/google_public_dns.md +1 -1
  21. data/docs/enrichers/ipinfo.md +2 -2
  22. data/docs/enrichers/shodan.md +4 -4
  23. data/docs/enrichers/whois.md +1 -1
  24. data/frontend/package-lock.json +176 -179
  25. data/frontend/package.json +9 -9
  26. data/lib/mihari/{base.rb → actor.rb} +16 -2
  27. data/lib/mihari/analyzers/base.rb +5 -10
  28. data/lib/mihari/analyzers/censys.rb +1 -1
  29. data/lib/mihari/analyzers/circl.rb +1 -1
  30. data/lib/mihari/analyzers/crtsh.rb +1 -1
  31. data/lib/mihari/analyzers/dnstwister.rb +1 -1
  32. data/lib/mihari/analyzers/hunterhow.rb +1 -1
  33. data/lib/mihari/analyzers/otx.rb +1 -1
  34. data/lib/mihari/analyzers/passivetotal.rb +2 -2
  35. data/lib/mihari/analyzers/pulsedive.rb +2 -2
  36. data/lib/mihari/analyzers/securitytrails.rb +2 -2
  37. data/lib/mihari/analyzers/urlscan.rb +1 -1
  38. data/lib/mihari/analyzers/virustotal.rb +5 -5
  39. data/lib/mihari/analyzers/zoomeye.rb +3 -3
  40. data/lib/mihari/clients/base.rb +2 -2
  41. data/lib/mihari/clients/binaryedge.rb +3 -5
  42. data/lib/mihari/clients/censys.rb +3 -3
  43. data/lib/mihari/clients/circl.rb +5 -4
  44. data/lib/mihari/clients/crtsh.rb +5 -4
  45. data/lib/mihari/clients/dnstwister.rb +3 -2
  46. data/lib/mihari/clients/greynoise.rb +2 -2
  47. data/lib/mihari/clients/hunterhow.rb +2 -2
  48. data/lib/mihari/clients/misp.rb +1 -1
  49. data/lib/mihari/clients/onyphe.rb +2 -2
  50. data/lib/mihari/clients/otx.rb +4 -3
  51. data/lib/mihari/clients/passivetotal.rb +9 -8
  52. data/lib/mihari/clients/publsedive.rb +4 -3
  53. data/lib/mihari/clients/securitytrails.rb +8 -6
  54. data/lib/mihari/clients/shodan.rb +2 -2
  55. data/lib/mihari/clients/the_hive.rb +1 -1
  56. data/lib/mihari/clients/urlscan.rb +4 -4
  57. data/lib/mihari/clients/virustotal.rb +2 -2
  58. data/lib/mihari/clients/zoomeye.rb +2 -2
  59. data/lib/mihari/commands/rule.rb +2 -11
  60. data/lib/mihari/commands/search.rb +1 -1
  61. data/lib/mihari/emitters/base.rb +13 -24
  62. data/lib/mihari/emitters/database.rb +7 -9
  63. data/lib/mihari/emitters/misp.rb +14 -38
  64. data/lib/mihari/emitters/slack.rb +14 -11
  65. data/lib/mihari/emitters/the_hive.rb +16 -44
  66. data/lib/mihari/emitters/webhook.rb +31 -21
  67. data/lib/mihari/enrichers/base.rb +1 -6
  68. data/lib/mihari/enrichers/whois.rb +1 -1
  69. data/lib/mihari/models/alert.rb +75 -73
  70. data/lib/mihari/models/artifact.rb +182 -180
  71. data/lib/mihari/models/autonomous_system.rb +22 -20
  72. data/lib/mihari/models/cpe.rb +21 -19
  73. data/lib/mihari/models/dns.rb +24 -22
  74. data/lib/mihari/models/geolocation.rb +22 -20
  75. data/lib/mihari/models/port.rb +21 -19
  76. data/lib/mihari/models/reverse_dns.rb +21 -19
  77. data/lib/mihari/models/rule.rb +67 -65
  78. data/lib/mihari/models/tag.rb +5 -3
  79. data/lib/mihari/models/tagging.rb +5 -3
  80. data/lib/mihari/models/whois.rb +18 -16
  81. data/lib/mihari/rule.rb +352 -0
  82. data/lib/mihari/schemas/analyzer.rb +94 -87
  83. data/lib/mihari/schemas/emitter.rb +9 -5
  84. data/lib/mihari/schemas/enricher.rb +8 -4
  85. data/lib/mihari/schemas/mixins.rb +15 -0
  86. data/lib/mihari/schemas/rule.rb +3 -10
  87. data/lib/mihari/services/alert_builder.rb +1 -1
  88. data/lib/mihari/services/alert_proxy.rb +10 -6
  89. data/lib/mihari/services/alert_runner.rb +4 -4
  90. data/lib/mihari/services/rule_builder.rb +3 -3
  91. data/lib/mihari/services/rule_runner.rb +5 -5
  92. data/lib/mihari/structs/binaryedge.rb +1 -1
  93. data/lib/mihari/structs/censys.rb +6 -6
  94. data/lib/mihari/structs/config.rb +1 -1
  95. data/lib/mihari/structs/greynoise.rb +5 -5
  96. data/lib/mihari/structs/hunterhow.rb +3 -3
  97. data/lib/mihari/structs/onyphe.rb +5 -5
  98. data/lib/mihari/structs/shodan.rb +6 -6
  99. data/lib/mihari/structs/urlscan.rb +3 -3
  100. data/lib/mihari/structs/virustotal_intelligence.rb +3 -3
  101. data/lib/mihari/version.rb +1 -1
  102. data/lib/mihari/web/endpoints/alerts.rb +4 -4
  103. data/lib/mihari/web/endpoints/artifacts.rb +6 -6
  104. data/lib/mihari/web/endpoints/rules.rb +10 -17
  105. data/lib/mihari/web/endpoints/tags.rb +2 -2
  106. data/lib/mihari/web/public/assets/{index-9cc489e6.js → index-28d4c79d.js} +48 -48
  107. data/lib/mihari/web/public/index.html +1 -1
  108. data/lib/mihari.rb +6 -8
  109. data/mihari.gemspec +1 -2
  110. data/mkdocs.yml +0 -3
  111. data/requirements.txt +1 -1
  112. metadata +8 -22
  113. data/lib/mihari/analyzers/rule.rb +0 -232
  114. data/lib/mihari/services/rule_proxy.rb +0 -182
@@ -1,29 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- class CPE < ActiveRecord::Base
5
- belongs_to :artifact
4
+ module Models
5
+ class CPE < ActiveRecord::Base
6
+ belongs_to :artifact
6
7
 
7
- class << self
8
- include Dry::Monads[:result]
8
+ class << self
9
+ include Dry::Monads[:result]
9
10
 
10
- #
11
- # Build CPEs
12
- #
13
- # @param [String] ip
14
- # @param [Mihari::Enrichers::Shodan] enricher
15
- #
16
- # @return [Array<Mihari::CPE>]
17
- #
18
- def build_by_ip(ip, enricher: Enrichers::Shodan.new)
19
- result = enricher.query_result(ip).bind do |res|
20
- if res.nil?
21
- Success []
22
- else
23
- Success(res.cpes.map { |cpe| new(cpe: cpe) })
11
+ #
12
+ # Build CPEs
13
+ #
14
+ # @param [String] ip
15
+ # @param [Mihari::Enrichers::Shodan] enricher
16
+ #
17
+ # @return [Array<Mihari::CPE>]
18
+ #
19
+ def build_by_ip(ip, enricher: Enrichers::Shodan.new)
20
+ result = enricher.query_result(ip).bind do |res|
21
+ if res.nil?
22
+ Success []
23
+ else
24
+ Success(res.cpes.map { |cpe| new(cpe: cpe) })
25
+ end
24
26
  end
27
+ result.value_or []
25
28
  end
26
- result.value_or []
27
29
  end
28
30
  end
29
31
  end
@@ -1,31 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- class DnsRecord < ActiveRecord::Base
5
- belongs_to :artifact
4
+ module Models
5
+ class DnsRecord < ActiveRecord::Base
6
+ belongs_to :artifact
6
7
 
7
- class << self
8
- include Dry::Monads[:result]
8
+ class << self
9
+ include Dry::Monads[:result]
9
10
 
10
- #
11
- # Build DNS records
12
- #
13
- # @param [String] domain
14
- # @param [Mihari::Enrichers::Shodan] enricher
15
- #
16
- # @return [Array<Mihari::DnsRecord>]
17
- #
18
- def build_by_domain(domain, enricher: Enrichers::GooglePublicDNS.new)
19
- result = enricher.query_result(domain).bind do |responses|
20
- Success(
21
- responses.map do |res|
22
- res.answers.map do |answer|
23
- new(resource: answer.resource_type, value: answer.data)
24
- end
25
- end.flatten
26
- )
11
+ #
12
+ # Build DNS records
13
+ #
14
+ # @param [String] domain
15
+ # @param [Mihari::Enrichers::Shodan] enricher
16
+ #
17
+ # @return [Array<Mihari::Models::DnsRecord>]
18
+ #
19
+ def build_by_domain(domain, enricher: Enrichers::GooglePublicDNS.new)
20
+ result = enricher.query_result(domain).bind do |responses|
21
+ Success(
22
+ responses.map do |res|
23
+ res.answers.map do |answer|
24
+ new(resource: answer.resource_type, value: answer.data)
25
+ end
26
+ end.flatten
27
+ )
28
+ end
29
+ result.value_or []
27
30
  end
28
- result.value_or []
29
31
  end
30
32
  end
31
33
  end
@@ -3,30 +3,32 @@
3
3
  require "normalize_country"
4
4
 
5
5
  module Mihari
6
- class Geolocation < ActiveRecord::Base
7
- belongs_to :artifact
6
+ module Models
7
+ class Geolocation < ActiveRecord::Base
8
+ belongs_to :artifact
8
9
 
9
- class << self
10
- include Dry::Monads[:result]
10
+ class << self
11
+ include Dry::Monads[:result]
11
12
 
12
- #
13
- # Build Geolocation
14
- #
15
- # @param [String] ip
16
- # @param [Mihari::Enrichers::IPinfo] enricher
17
- #
18
- # @return [Mihari::Geolocation, nil]
19
- #
20
- def build_by_ip(ip, enricher: Enrichers::IPInfo.new)
21
- result = enricher.query_result(ip).bind do |res|
22
- value = res&.country_code
23
- if value.nil?
24
- Success nil
25
- else
26
- Success new(country: NormalizeCountry(value, to: :short), country_code: value)
13
+ #
14
+ # Build Geolocation
15
+ #
16
+ # @param [String] ip
17
+ # @param [Mihari::Enrichers::IPinfo] enricher
18
+ #
19
+ # @return [Mihari::Geolocation, nil]
20
+ #
21
+ def build_by_ip(ip, enricher: Enrichers::IPInfo.new)
22
+ result = enricher.query_result(ip).bind do |res|
23
+ value = res&.country_code
24
+ if value.nil?
25
+ Success nil
26
+ else
27
+ Success new(country: NormalizeCountry(value, to: :short), country_code: value)
28
+ end
27
29
  end
30
+ result.value_or nil
28
31
  end
29
- result.value_or nil
30
32
  end
31
33
  end
32
34
  end
@@ -1,29 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- class Port < ActiveRecord::Base
5
- belongs_to :artifact
4
+ module Models
5
+ class Port < ActiveRecord::Base
6
+ belongs_to :artifact
6
7
 
7
- class << self
8
- include Dry::Monads[:result]
8
+ class << self
9
+ include Dry::Monads[:result]
9
10
 
10
- #
11
- # Build ports
12
- #
13
- # @param [String] ip
14
- # @param [Mihari::Enrichers::Shodan] enricher
15
- #
16
- # @return [Array<Mihari::Port>]
17
- #
18
- def build_by_ip(ip, enricher: Enrichers::Shodan.new)
19
- result = enricher.query_result(ip).bind do |res|
20
- if res.nil?
21
- Success []
22
- else
23
- Success(res.ports.map { |port| new(port: port) })
11
+ #
12
+ # Build ports
13
+ #
14
+ # @param [String] ip
15
+ # @param [Mihari::Enrichers::Shodan] enricher
16
+ #
17
+ # @return [Array<Mihari::Port>]
18
+ #
19
+ def build_by_ip(ip, enricher: Enrichers::Shodan.new)
20
+ result = enricher.query_result(ip).bind do |res|
21
+ if res.nil?
22
+ Success []
23
+ else
24
+ Success(res.ports.map { |port| new(port: port) })
25
+ end
24
26
  end
27
+ result.value_or []
25
28
  end
26
- result.value_or []
27
29
  end
28
30
  end
29
31
  end
@@ -1,29 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- class ReverseDnsName < ActiveRecord::Base
5
- belongs_to :artifact
4
+ module Models
5
+ class ReverseDnsName < ActiveRecord::Base
6
+ belongs_to :artifact
6
7
 
7
- class << self
8
- include Dry::Monads[:result]
8
+ class << self
9
+ include Dry::Monads[:result]
9
10
 
10
- #
11
- # Build reverse DNS names
12
- #
13
- # @param [String] ip
14
- # @param [Mihari::Enrichers::Shodan] enricher
15
- #
16
- # @return [Array<Mihari::ReverseDnsName>]
17
- #
18
- def build_by_ip(ip, enricher: Enrichers::Shodan.new)
19
- result = enricher.query_result(ip).bind do |res|
20
- if res.nil?
21
- Success []
22
- else
23
- Success(res.hostnames.map { |name| new(name: name) })
11
+ #
12
+ # Build reverse DNS names
13
+ #
14
+ # @param [String] ip
15
+ # @param [Mihari::Enrichers::Shodan] enricher
16
+ #
17
+ # @return [Array<Mihari::Models::ReverseDnsName>]
18
+ #
19
+ def build_by_ip(ip, enricher: Enrichers::Shodan.new)
20
+ result = enricher.query_result(ip).bind do |res|
21
+ if res.nil?
22
+ Success []
23
+ else
24
+ Success(res.hostnames.map { |name| new(name: name) })
25
+ end
24
26
  end
27
+ result.value_or []
25
28
  end
26
- result.value_or []
27
29
  end
28
30
  end
29
31
  end
@@ -3,77 +3,79 @@
3
3
  require "yaml"
4
4
 
5
5
  module Mihari
6
- class Rule < ActiveRecord::Base
7
- has_many :alerts, dependent: :destroy
6
+ module Models
7
+ class Rule < ActiveRecord::Base
8
+ has_many :alerts, dependent: :destroy
8
9
 
9
- def symbolized_data
10
- @symbolized_data ||= data.deep_symbolize_keys
11
- end
12
-
13
- def yaml
14
- data.to_yaml
15
- end
16
-
17
- def tags
18
- (data["tags"] || []).map { |tag| { name: tag } }
19
- end
20
-
21
- class << self
22
- #
23
- # Search rules
24
- #
25
- # @param [Structs::Filters::Rule::SearchFilterWithPagination] filter
26
- #
27
- # @return [Array<Rule>]
28
- #
29
- def search(filter)
30
- limit = filter.limit.to_i
31
- raise ArgumentError, "limit should be bigger than zero" unless limit.positive?
32
-
33
- page = filter.page.to_i
34
- raise ArgumentError, "page should be bigger than zero" unless page.positive?
35
-
36
- offset = (page - 1) * limit
37
-
38
- relation = build_relation(filter.without_pagination)
39
-
40
- # TODO: improve queires
41
- rule_ids = relation.limit(limit).offset(offset).order(created_at: :desc).pluck(:id).uniq
42
- where(id: [rule_ids]).order(created_at: :desc)
10
+ def symbolized_data
11
+ @symbolized_data ||= data.deep_symbolize_keys
43
12
  end
44
13
 
45
- #
46
- # Count alerts
47
- #
48
- # @param [Structs::Filters::Rule::SearchFilterWithPagination] filter
49
- #
50
- # @return [Integer]
51
- #
52
- def count(filter)
53
- relation = build_relation(filter)
54
- relation.distinct("rules.id").count
14
+ def yaml
15
+ data.to_yaml
55
16
  end
56
17
 
57
- private
58
-
59
- #
60
- # @param [Structs::Filters::Rule::SearchFilter] filter
61
- #
62
- # @return [Mihari::Rule]
63
- #
64
- def build_relation(filter)
65
- relation = self
66
- relation = relation.includes(alerts: :tags)
67
-
68
- relation = relation.where(alerts: { tags: { name: filter.tag_name } }) if filter.tag_name
69
-
70
- relation = relation.where("rules.title LIKE ?", "%#{filter.title}%") if filter.title
71
- relation = relation.where("rules.description LIKE ?", "%#{filter.description}%") if filter.description
72
-
73
- relation = relation.where("rules.created_at >= ?", filter.from_at) if filter.from_at
74
- relation = relation.where("rules.created_at <= ?", filter.to_at) if filter.to_at
18
+ def tags
19
+ (data["tags"] || []).map { |tag| { name: tag } }
20
+ end
75
21
 
76
- relation
22
+ class << self
23
+ #
24
+ # Search rules
25
+ #
26
+ # @param [Structs::Filters::Rule::SearchFilterWithPagination] filter
27
+ #
28
+ # @return [Array<Rule>]
29
+ #
30
+ def search(filter)
31
+ limit = filter.limit.to_i
32
+ raise ArgumentError, "limit should be bigger than zero" unless limit.positive?
33
+
34
+ page = filter.page.to_i
35
+ raise ArgumentError, "page should be bigger than zero" unless page.positive?
36
+
37
+ offset = (page - 1) * limit
38
+
39
+ relation = build_relation(filter.without_pagination)
40
+
41
+ # TODO: improve queires
42
+ rule_ids = relation.limit(limit).offset(offset).order(created_at: :desc).pluck(:id).uniq
43
+ where(id: [rule_ids]).order(created_at: :desc)
44
+ end
45
+
46
+ #
47
+ # Count alerts
48
+ #
49
+ # @param [Structs::Filters::Rule::SearchFilterWithPagination] filter
50
+ #
51
+ # @return [Integer]
52
+ #
53
+ def count(filter)
54
+ relation = build_relation(filter)
55
+ relation.distinct("rules.id").count
56
+ end
57
+
58
+ private
59
+
60
+ #
61
+ # @param [Structs::Filters::Rule::SearchFilter] filter
62
+ #
63
+ # @return [Mihari::Models::Rule]
64
+ #
65
+ def build_relation(filter)
66
+ relation = self
67
+ relation = relation.includes(alerts: :tags)
68
+
69
+ relation = relation.where(alerts: { tags: { name: filter.tag_name } }) if filter.tag_name
70
+
71
+ relation = relation.where("rules.title LIKE ?", "%#{filter.title}%") if filter.title
72
+ relation = relation.where("rules.description LIKE ?", "%#{filter.description}%") if filter.description
73
+
74
+ relation = relation.where("rules.created_at >= ?", filter.from_at) if filter.from_at
75
+ relation = relation.where("rules.created_at <= ?", filter.to_at) if filter.to_at
76
+
77
+ relation
78
+ end
77
79
  end
78
80
  end
79
81
  end
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- class Tag < ActiveRecord::Base
5
- has_many :taggings, dependent: :destroy
6
- has_many :tags, through: :taggings
4
+ module Models
5
+ class Tag < ActiveRecord::Base
6
+ has_many :taggings, dependent: :destroy
7
+ has_many :tags, through: :taggings
8
+ end
7
9
  end
8
10
  end
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- class Tagging < ActiveRecord::Base
5
- belongs_to :alert
6
- belongs_to :tag
4
+ module Models
5
+ class Tagging < ActiveRecord::Base
6
+ belongs_to :alert
7
+ belongs_to :tag
8
+ end
7
9
  end
8
10
  end
@@ -1,25 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- class WhoisRecord < ActiveRecord::Base
5
- belongs_to :artifact
4
+ module Models
5
+ class WhoisRecord < ActiveRecord::Base
6
+ belongs_to :artifact
6
7
 
7
- @memo = {}
8
+ @memo = {}
8
9
 
9
- class << self
10
- include Dry::Monads[:result]
10
+ class << self
11
+ include Dry::Monads[:result]
11
12
 
12
- #
13
- # Build whois record
14
- #
15
- # @param [String] domain
16
- # @param [Mihari::Enrichers::Whois] enricher
17
- #
18
- # @return [WhoisRecord, nil]
19
- #
20
- def build_by_domain(domain, enricher: Enrichers::Whois.new)
21
- result = enricher.query_result(domain)
22
- result.value_or nil
13
+ #
14
+ # Build whois record
15
+ #
16
+ # @param [String] domain
17
+ # @param [Mihari::Enrichers::Whois] enricher
18
+ #
19
+ # @return [WhoisRecord, nil]
20
+ #
21
+ def build_by_domain(domain, enricher: Enrichers::Whois.new)
22
+ result = enricher.query_result(domain)
23
+ result.value_or nil
24
+ end
23
25
  end
24
26
  end
25
27
  end