mihari 3.6.1 → 3.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -3
  3. data/lib/mihari/analyzers/base.rb +2 -17
  4. data/lib/mihari/analyzers/rule.rb +1 -0
  5. data/lib/mihari/analyzers/virustotal_intelligence.rb +63 -0
  6. data/lib/mihari/cli/analyzer.rb +2 -0
  7. data/lib/mihari/commands/passivetotal.rb +1 -0
  8. data/lib/mihari/commands/virustotal.rb +1 -0
  9. data/lib/mihari/commands/virustotal_intelligence.rb +22 -0
  10. data/lib/mihari/database.rb +13 -0
  11. data/lib/mihari/enrichers/base.rb +18 -0
  12. data/lib/mihari/enrichers/ipinfo.rb +49 -0
  13. data/lib/mihari/mixins/autonomous_system.rb +19 -0
  14. data/lib/mihari/models/artifact.rb +42 -3
  15. data/lib/mihari/models/autonomous_system.rb +18 -1
  16. data/lib/mihari/models/dns.rb +2 -0
  17. data/lib/mihari/models/geolocation.rb +21 -1
  18. data/lib/mihari/models/reverse_dns.rb +2 -0
  19. data/lib/mihari/models/whois.rb +1 -1
  20. data/lib/mihari/status.rb +7 -2
  21. data/lib/mihari/structs/ipinfo.rb +39 -0
  22. data/lib/mihari/structs/virustotal_intelligence.rb +75 -0
  23. data/lib/mihari/types.rb +13 -3
  24. data/lib/mihari/version.rb +1 -1
  25. data/lib/mihari/web/controllers/artifacts_controller.rb +27 -1
  26. data/lib/mihari/web/controllers/ip_address_controller.rb +4 -19
  27. data/lib/mihari/web/public/index.html +1 -1
  28. data/lib/mihari/web/public/redoc-static.html +7 -6
  29. data/lib/mihari/web/public/static/js/app.06d5cf1c.js +36 -0
  30. data/lib/mihari/web/public/static/js/app.06d5cf1c.js.map +1 -0
  31. data/lib/mihari.rb +42 -27
  32. data/mihari.gemspec +8 -6
  33. data/sig/lib/mihari/analyzers/base.rbs +1 -10
  34. data/sig/lib/mihari/analyzers/virustotal_intelligence.rbs +32 -0
  35. data/sig/lib/mihari/enrichers/base.rbs +12 -0
  36. data/sig/lib/mihari/enrichers/ipinfo.rbs +16 -0
  37. data/sig/lib/mihari/mixins/autonomous_system.rbs +14 -0
  38. data/sig/lib/mihari/models/artifact.rbs +11 -0
  39. data/sig/lib/mihari/models/autonomous_system.rbs +9 -0
  40. data/sig/lib/mihari/models/geolocation.rbs +9 -0
  41. data/sig/lib/mihari/structs/ipinfo.rbs +17 -0
  42. data/sig/lib/mihari/structs/virustotal_intelligence.rbs +33 -0
  43. data/sig/lib/mihari.rbs +2 -0
  44. metadata +57 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 14d0c74e85fbf6ef624afefe7e948595586d6c00fa8bc32f211e60caee581fc3
4
- data.tar.gz: 9d5fde6d69f664efac0d6c56e6a0ba60adcad0edcfe45c69f285ffcaba8d11f0
3
+ metadata.gz: aac91d43689cb53dc0570bfed3cec57a07cbe88de0716530f2ea8bfac8f8d39d
4
+ data.tar.gz: 0f59bdc53cfa75e56884dd3497fa0492d3a41a3b7540cbdab1345ec5b301c69c
5
5
  SHA512:
6
- metadata.gz: 30597743e91f124388fbdf426199d97a00f92db9e525fe5725643d529d319ca670d1e9aa6eccff86c0844802157ca3d5c7db9b9afd925d6f0ac4d8b881c44949
7
- data.tar.gz: 9d199a3c2f6c7794214730de7c8db812e939c5ddd11ab06d1c6f28c3b8764b2881958b0684adb59e8516d0ac4b6a1dc66b19c5a593efaac9695cb6e317ce8105
6
+ metadata.gz: 30aef30fb14c7c1a50e75162141d1266b1ec5b847f6329935a221a90f59d37a2bed97c6a8aa4371962ab85b18c5ff23cf0417f08f9dc3320c737721ac1a07602
7
+ data.tar.gz: 1029878ec85cbdbe0a2c800b6b68cce99d45510818dde6b1d84a826022379cbe21fc57f490e3a77521136dbc22e8112b804d891892ea0a351e0d1db935b28b4b
data/README.md CHANGED
@@ -46,7 +46,7 @@ Mihari supports the following services by default.
46
46
  - [Shodan](https://shodan.io)
47
47
  - [Spyse](https://spyse.com)
48
48
  - [urlscan.io](https://urlscan.io)
49
- - [VirusTotal](http://virustotal.com)
49
+ - [VirusTotal](http://virustotal.com) & [VirusTotal Intelligence](https://www.virustotal.com/gui/intelligence-overview)
50
50
  - [ZoomEye](https://zoomeye.org)
51
51
 
52
52
  ## Docs
@@ -64,5 +64,3 @@ The gem is available as open source under the terms of the [MIT License](https:/
64
64
  ## Acknowledgement
65
65
 
66
66
  Mihari is proudly supported by [Tines.io](https://tines.io?utm_source=github&utm_medium=sponsorship&utm_campaign=ninoseki), The SOAR Platform for Enterprise Security Teams.
67
-
68
- $ bundle exec rbs -rpathname --repo=gem_rbs/gems -ractivesupport -ractionpack -ractivejob -ractivemodel -ractionview -ractiverecord -rrailties -I sig validate
@@ -8,6 +8,7 @@ module Mihari
8
8
  class Base
9
9
  extend Dry::Initializer
10
10
 
11
+ include Mixins::AutonomousSystem
11
12
  include Mixins::Configurable
12
13
  include Mixins::Retriable
13
14
 
@@ -111,9 +112,7 @@ module Mihari
111
112
  #
112
113
  def enriched_artifacts
113
114
  @enriched_artifacts ||= unique_artifacts.map do |artifact|
114
- artifact.enrich_whois
115
- artifact.enrich_dns
116
- artifact.enrich_reverse_dns
115
+ artifact.enrich_all
117
116
  artifact
118
117
  end
119
118
  end
@@ -141,20 +140,6 @@ module Mihari
141
140
  emitter.valid? ? emitter : nil
142
141
  end.compact
143
142
  end
144
-
145
- #
146
- # Normalize ASN value
147
- #
148
- # @param [String, Integer] asn
149
- #
150
- # @return [Integer]
151
- #
152
- def normalize_asn(asn)
153
- return asn if asn.is_a?(Integer)
154
- return asn.to_i unless asn.start_with?("AS")
155
-
156
- asn.delete_prefix("AS").to_i
157
- end
158
143
  end
159
144
  end
160
145
  end
@@ -42,6 +42,7 @@ module Mihari
42
42
  "spyse" => Spyse,
43
43
  "urlscan" => Urlscan,
44
44
  "virustotal" => VirusTotal,
45
+ "virustotal_intelligence" => VirusTotalIntelligence,
45
46
  "zoomeye" => ZoomEye
46
47
  }.freeze
47
48
 
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "virustotal"
4
+
5
+ module Mihari
6
+ module Analyzers
7
+ class VirusTotalIntelligence < Base
8
+ param :query
9
+ option :title, default: proc { "VirusTotal Intelligence search" }
10
+ option :description, default: proc { "query = #{query}" }
11
+ option :tags, default: proc { [] }
12
+
13
+ def initialize(*args, **kwargs)
14
+ super
15
+
16
+ @query = query
17
+ end
18
+
19
+ def artifacts
20
+ responses = search_witgh_cursor
21
+ responses.map do |response|
22
+ response.data.map(&:value)
23
+ end.flatten.compact.uniq
24
+ end
25
+
26
+ private
27
+
28
+ def configuration_keys
29
+ %w[virustotal_api_key]
30
+ end
31
+
32
+ #
33
+ # VT API
34
+ #
35
+ # @return [::VirusTotal::API]
36
+ #
37
+ def api
38
+ @api = ::VirusTotal::API.new(key: Mihari.config.virustotal_api_key)
39
+ end
40
+
41
+ #
42
+ # Search with cursor
43
+ #
44
+ # @return [Array<Structs::VirusTotalIntelligence::Response>]
45
+ #
46
+ def search_witgh_cursor
47
+ cursor = nil
48
+ responses = []
49
+
50
+ loop do
51
+ response = Structs::VirusTotalIntelligence::Response.from_dynamic!(api.intelligence.search(query, cursor: cursor))
52
+ responses << response
53
+
54
+ break if response.meta.cursor.nil?
55
+
56
+ cursor = response.meta.cursor
57
+ end
58
+
59
+ responses
60
+ end
61
+ end
62
+ end
63
+ end
@@ -14,6 +14,7 @@ require "mihari/commands/securitytrails"
14
14
  require "mihari/commands/shodan"
15
15
  require "mihari/commands/spyse"
16
16
  require "mihari/commands/urlscan"
17
+ require "mihari/commands/virustotal_intelligence"
17
18
  require "mihari/commands/virustotal"
18
19
  require "mihari/commands/zoomeye"
19
20
 
@@ -42,6 +43,7 @@ module Mihari
42
43
  include Mihari::Commands::Spyse
43
44
  include Mihari::Commands::Urlscan
44
45
  include Mihari::Commands::VirusTotal
46
+ include Mihari::Commands::VirusTotalIntelligence
45
47
  include Mihari::Commands::ZoomEye
46
48
  end
47
49
  end
@@ -14,6 +14,7 @@ module Mihari
14
14
  run_analyzer Analyzers::PassiveTotal, query: indicator, options: options
15
15
  end
16
16
  end
17
+ map "pt" => :passivetotal
17
18
  end
18
19
  end
19
20
  end
@@ -14,6 +14,7 @@ module Mihari
14
14
  run_analyzer Analyzers::VirusTotal, query: indiactor, options: options
15
15
  end
16
16
  end
17
+ map "vt" => :virustotal
17
18
  end
18
19
  end
19
20
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mihari
4
+ module Commands
5
+ module VirusTotalIntelligence
6
+ def self.included(thor)
7
+ thor.class_eval do
8
+ desc "virustotal_intelligence [QUERY]", "VirusTotal Intelligence search"
9
+ method_option :title, type: :string, desc: "title"
10
+ method_option :description, type: :string, desc: "description"
11
+ method_option :tags, type: :array, desc: "tags"
12
+ def virustotal_intelligence(query)
13
+ with_error_handling do
14
+ run_analyzer Analyzers::VirusTotalIntelligence, query: query, options: options
15
+ end
16
+ end
17
+ map "vt_intel" => :virustotal_intelligence
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -74,6 +74,17 @@ class EnrichmentsSchema < ActiveRecord::Migration[6.1]
74
74
  end
75
75
  end
76
76
 
77
+ class EnrichmentCreatedAtSchema < ActiveRecord::Migration[6.1]
78
+ def change
79
+ # Add created_at column because now it is able to enrich an atrifact after the creation
80
+ add_column :autonomous_systems, :created_at, :datetime, if_not_exists: true
81
+ add_column :geolocations, :created_at, :datetime, if_not_exists: true
82
+ add_column :whois_records, :created_at, :datetime, if_not_exists: true
83
+ add_column :dns_records, :created_at, :datetime, if_not_exists: true
84
+ add_column :reverse_dns_names, :created_at, :datetime, if_not_exists: true
85
+ end
86
+ end
87
+
77
88
  def adapter
78
89
  return "postgresql" if Mihari.config.database.start_with?("postgresql://", "postgres://")
79
90
  return "mysql2" if Mihari.config.database.start_with?("mysql2://")
@@ -101,6 +112,7 @@ module Mihari
101
112
  InitialSchema.migrate(:up)
102
113
  AddeSourceToArtifactSchema.migrate(:up)
103
114
  EnrichmentsSchema.migrate(:up)
115
+ EnrichmentCreatedAtSchema.migrate(:up)
104
116
  rescue StandardError
105
117
  # Do nothing
106
118
  end
@@ -116,6 +128,7 @@ module Mihari
116
128
  InitialSchema.migrate(:down)
117
129
  AddeSourceToArtifactSchema.migrate(:down)
118
130
  EnrichmentsSchema.migrate(:down)
131
+ EnrichmentCreatedAtSchema.migrate(:down)
119
132
  end
120
133
  end
121
134
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mihari
4
+ module Enrichers
5
+ class Base
6
+ include Mixins::Configurable
7
+
8
+ def self.inherited(child)
9
+ Mihari.enrichers << child
10
+ end
11
+
12
+ # @return [Boolean]
13
+ def valid?
14
+ raise NotImplementedError, "You must implement #{self.class}##{__method__}"
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,49 @@
1
+ require "http"
2
+ require "json"
3
+ require "memist"
4
+
5
+ module Mihari
6
+ module Enrichers
7
+ class IPInfo < Base
8
+ # @return [Boolean]
9
+ def valid?
10
+ Mihari.config.ipinfo_api_key.nil?
11
+ end
12
+
13
+ private
14
+
15
+ def configuration_keys
16
+ %w[ipinfo_api_key]
17
+ end
18
+
19
+ class << self
20
+ include Memist::Memoizable
21
+
22
+ #
23
+ # Query IPInfo
24
+ #
25
+ # @param [String] ip
26
+ #
27
+ # @return [Mihari::Structs::IPInfo::Response, nil]
28
+ #
29
+ def query(ip)
30
+ headers = {}
31
+ token = Mihari.config.ipinfo_api_key
32
+ unless token.nil?
33
+ headers[:authorization] = "Bearer #{token}"
34
+ end
35
+
36
+ begin
37
+ res = HTTP.headers(headers).get("https://ipinfo.io/#{ip}/json")
38
+ data = JSON.parse(res.body.to_s)
39
+
40
+ Structs::IPInfo::Response.from_dynamic! data
41
+ rescue HTTP::Error
42
+ nil
43
+ end
44
+ end
45
+ memoize :query
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,19 @@
1
+ module Mihari
2
+ module Mixins
3
+ module AutonomousSystem
4
+ #
5
+ # Normalize ASN value
6
+ #
7
+ # @param [String, Integer] asn
8
+ #
9
+ # @return [Integer]
10
+ #
11
+ def normalize_asn(asn)
12
+ return asn if asn.is_a?(Integer)
13
+ return asn.to_i unless asn.start_with?("AS")
14
+
15
+ asn.delete_prefix("AS").to_i
16
+ end
17
+ end
18
+ end
19
+ end
@@ -16,6 +16,8 @@ end
16
16
 
17
17
  module Mihari
18
18
  class Artifact < ActiveRecord::Base
19
+ belongs_to :alert
20
+
19
21
  has_one :autonomous_system, dependent: :destroy
20
22
  has_one :geolocation, dependent: :destroy
21
23
  has_one :whois_record, dependent: :destroy
@@ -80,6 +82,35 @@ module Mihari
80
82
  self.reverse_dns_names = ReverseDnsName.build_by_ip(data)
81
83
  end
82
84
 
85
+ #
86
+ # Enrich(add) geolocation
87
+ #
88
+ def enrich_geolocation
89
+ return unless can_enrich_geolocation?
90
+
91
+ self.geolocation = Geolocation.build_by_ip(data)
92
+ end
93
+
94
+ #
95
+ # Enrich(add) geolocation
96
+ #
97
+ def enrich_autonomous_system
98
+ return unless can_enrich_autonomous_system?
99
+
100
+ self.autonomous_system = AutonomousSystem.build_by_ip(data)
101
+ end
102
+
103
+ #
104
+ # Enrich all the enrichable relationships of the artifact
105
+ #
106
+ def enrich_all
107
+ enrich_autonomous_system
108
+ enrich_dns
109
+ enrich_geolocation
110
+ enrich_reverse_dns
111
+ enrich_whois
112
+ end
113
+
83
114
  private
84
115
 
85
116
  def normalize_as_domain(url_or_domain)
@@ -89,15 +120,23 @@ module Mihari
89
120
  end
90
121
 
91
122
  def can_enrich_whois?
92
- %w[domain url].include? data_type
123
+ %w[domain url].include?(data_type) && whois_record.nil?
93
124
  end
94
125
 
95
126
  def can_enrich_dns?
96
- %w[domain url].include? data_type
127
+ %w[domain url].include?(data_type) && dns_records.empty?
97
128
  end
98
129
 
99
130
  def can_enrich_revese_dns?
100
- data_type == "ip"
131
+ data_type == "ip" && reverse_dns_names.empty?
132
+ end
133
+
134
+ def can_enrich_geolocation?
135
+ data_type == "ip" && geolocation.nil?
136
+ end
137
+
138
+ def can_enrich_autonomous_system?
139
+ data_type == "ip" && autonomous_system.nil?
101
140
  end
102
141
  end
103
142
  end
@@ -4,6 +4,23 @@ require "active_record"
4
4
 
5
5
  module Mihari
6
6
  class AutonomousSystem < ActiveRecord::Base
7
- has_one :artifact, dependent: :destroy
7
+ belongs_to :artifact
8
+
9
+ class << self
10
+ #
11
+ # Build AS
12
+ #
13
+ # @param [String] ip
14
+ #
15
+ # @return [Mihari::AutonomousSystem, nil]
16
+ #
17
+ def build_by_ip(ip)
18
+ res = Enrichers::IPInfo.query(ip)
19
+
20
+ return nil if res.nil? || res.asn.nil?
21
+
22
+ new(asn: res.asn)
23
+ end
24
+ end
8
25
  end
9
26
  end
@@ -5,6 +5,8 @@ require "resolv"
5
5
 
6
6
  module Mihari
7
7
  class DnsRecord < ActiveRecord::Base
8
+ belongs_to :artifact
9
+
8
10
  class << self
9
11
  #
10
12
  # Build DNS records
@@ -1,9 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_record"
4
+ require "normalize_country"
4
5
 
5
6
  module Mihari
6
7
  class Geolocation < ActiveRecord::Base
7
- has_one :artifact, dependent: :destroy
8
+ belongs_to :artifact
9
+
10
+ class << self
11
+ #
12
+ # Build Geolocation
13
+ #
14
+ # @param [String] ip
15
+ #
16
+ # @return [Mihari::Geolocation, nil]
17
+ #
18
+ def build_by_ip(ip)
19
+ res = Enrichers::IPInfo.query(ip)
20
+
21
+ unless res.nil?
22
+ return new(country: NormalizeCountry(res.country_code, to: :short), country_code: res.country_code)
23
+ end
24
+
25
+ nil
26
+ end
27
+ end
8
28
  end
9
29
  end
@@ -5,6 +5,8 @@ require "resolv"
5
5
 
6
6
  module Mihari
7
7
  class ReverseDnsName < ActiveRecord::Base
8
+ belongs_to :artifact
9
+
8
10
  class << self
9
11
  #
10
12
  # Build reverse DNS names
@@ -6,7 +6,7 @@ require "public_suffix"
6
6
 
7
7
  module Mihari
8
8
  class WhoisRecord < ActiveRecord::Base
9
- has_one :artifact, dependent: :destroy
9
+ belongs_to :artifact
10
10
 
11
11
  @memo = {}
12
12
 
data/lib/mihari/status.rb CHANGED
@@ -18,7 +18,7 @@ module Mihari
18
18
  # @return [Array<Hash>]
19
19
  #
20
20
  def statuses
21
- (Mihari.analyzers + Mihari.emitters).map do |klass|
21
+ (Mihari.analyzers + Mihari.emitters + Mihari.enrichers).map do |klass|
22
22
  name = klass.to_s.split("::").last.to_s
23
23
 
24
24
  [name, build_status(klass)]
@@ -36,11 +36,16 @@ module Mihari
36
36
  return nil if klass == Mihari::Analyzers::Rule
37
37
 
38
38
  is_analyzer = klass.ancestors.include?(Mihari::Analyzers::Base)
39
+ is_emitter = klass.ancestors.include?(Mihari::Emitters::Base)
40
+ is_enricher = klass.ancestors.include?(Mihari::Enrichers::Base)
39
41
 
40
42
  instance = is_analyzer ? klass.new("dummy") : klass.new
41
43
  is_configured = instance.configured?
42
44
  values = instance.configuration_values
43
- type = is_analyzer ? "Analyzer" : "Emitter"
45
+
46
+ type = "Analyzer"
47
+ type = "Emitter" if is_emitter
48
+ type = "Enricher" if is_enricher
44
49
 
45
50
  values ? { is_configured: is_configured, values: values, type: type } : nil
46
51
  rescue ArgumentError => _e
@@ -0,0 +1,39 @@
1
+ require "json"
2
+ require "dry/struct"
3
+
4
+ module Mihari
5
+ module Structs
6
+ module IPInfo
7
+ class Response < Dry::Struct
8
+ attribute :ip, Types::String
9
+ attribute :hostname, Types::String.optional
10
+ attribute :loc, Types::String
11
+ attribute :country_code, Types::String
12
+ attribute :asn, Types::Integer.optional
13
+
14
+ class << self
15
+ include Mixins::AutonomousSystem
16
+
17
+ def from_dynamic!(d)
18
+ d = Types::Hash[d]
19
+
20
+ asn = nil
21
+ org = d["org"]
22
+ unless org.nil?
23
+ asn = org.split.first
24
+ asn = normalize_asn(asn)
25
+ end
26
+
27
+ new(
28
+ ip: d.fetch("ip"),
29
+ loc: d.fetch("loc"),
30
+ hostname: d["hostname"],
31
+ country_code: d.fetch("country"),
32
+ asn: asn
33
+ )
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,75 @@
1
+ require "json"
2
+ require "dry/struct"
3
+
4
+ module Mihari
5
+ module Structs
6
+ module VirusTotalIntelligence
7
+ class ContextAttributes < Dry::Struct
8
+ attribute :url, Types.Array(Types::String).optional
9
+
10
+ def self.from_dynamic!(d)
11
+ d = Types::Hash[d]
12
+ new(
13
+ url: d["url"]
14
+ )
15
+ end
16
+ end
17
+
18
+ class Datum < Dry::Struct
19
+ attribute :type, Types::String
20
+ attribute :id, Types::String
21
+ attribute :context_attributes, ContextAttributes.optional
22
+
23
+ def value
24
+ case type
25
+ when "file"
26
+ id
27
+ when "url"
28
+ (context_attributes.url || []).first
29
+ when "domain"
30
+ id
31
+ when "ip_address"
32
+ id
33
+ end
34
+ end
35
+
36
+ def self.from_dynamic!(d)
37
+ d = Types::Hash[d]
38
+
39
+ context_attributes = nil
40
+ context_attributes = ContextAttributes.from_dynamic!(d.fetch("context_attributes")) if d.key?("context_attributes")
41
+
42
+ new(
43
+ type: d.fetch("type"),
44
+ id: d.fetch("id"),
45
+ context_attributes: context_attributes
46
+ )
47
+ end
48
+ end
49
+
50
+ class Meta < Dry::Struct
51
+ attribute :cursor, Types::String.optional
52
+
53
+ def self.from_dynamic!(d)
54
+ d = Types::Hash[d]
55
+ new(
56
+ cursor: d["cursor"]
57
+ )
58
+ end
59
+ end
60
+
61
+ class Response < Dry::Struct
62
+ attribute :meta, Meta
63
+ attribute :data, Types.Array(Datum)
64
+
65
+ def self.from_dynamic!(d)
66
+ d = Types::Hash[d]
67
+ new(
68
+ meta: Meta.from_dynamic!(d.fetch("meta")),
69
+ data: d.fetch("data").map { |x| Datum.from_dynamic!(x) }
70
+ )
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
data/lib/mihari/types.rb CHANGED
@@ -13,9 +13,19 @@ module Mihari
13
13
  DataTypes = Types::String.enum(*ALLOWED_DATA_TYPES)
14
14
 
15
15
  AnalyzerTypes = Types::String.enum(
16
- "binaryedge", "censys", "circl", "dnpedia", "dnstwister",
17
- "onyphe", "otx", "passivetotal", "pulsedive", "securitytrails",
18
- "shodan", "virustotal"
16
+ "binaryedge",
17
+ "censys",
18
+ "circl",
19
+ "dnpedia",
20
+ "dnstwister",
21
+ "onyphe",
22
+ "otx",
23
+ "passivetotal",
24
+ "pulsedive",
25
+ "securitytrails",
26
+ "shodan",
27
+ "virustotal_intelligence",
28
+ "virustotal"
19
29
  )
20
30
  end
21
31
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- VERSION = "3.6.1"
4
+ VERSION = "3.8.0"
5
5
  end