mihari 4.5.2 → 4.6.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 266aaff37fdb3453ed52ba4391f25e2fc9d26c37996a4d00c462d2c9cb9a2771
4
- data.tar.gz: 5d3397886e6c63199f49233668772a38ac508b9e32f3cd64f8925c3bf8d391f7
3
+ metadata.gz: 491cb9d0477c4101251c1f3e5705328008a196920efb41c239048247937f21f4
4
+ data.tar.gz: 14bcfd11b8871a759e70f6658cf2773dadc6afe7ffde44dbbffaef99961b8e00
5
5
  SHA512:
6
- metadata.gz: 6df709c192e0a533d40604ee4fd076eb9d5f776d13165e60fe1687cf2fc2d008decbfd80c48cc809ab6667946e86b7a74490109e2bff9bf6c15d7c5767f490dc
7
- data.tar.gz: 3350eeaa0d82da0322d463c30d69cfb2d09378449d37e681c34ec082d1d6505bb8bf1838c201779c3f3e618d716e029c25bbf6e0ac1329ad6c2291eef7c38a3a
6
+ metadata.gz: d833915545629ade609ec994e6139387fd66247fa1c48fdbe4e3eab01679fb42820e3b07cb95fdbb8cff092753394f1dfa9aaa4459259837c2c0d7d5f2f855ed
7
+ data.tar.gz: dcbb112faa805b27157ec72d0f3544031c7e7789e855eb082a69a76216d0fcd644013ade95aea28ae138ebfb3bc8eb90d8fb922c83999d292c12b78d15acfe5c
@@ -127,9 +127,12 @@ module Mihari
127
127
  # @return [Boolean]
128
128
  #
129
129
  def disallowed_data_value?(value)
130
- normalized_disallowed_data_values.any? do |disallowed_data_value|
131
- return value == disallowed_data_value if disallowed_data_value.is_a?(String)
132
- return disallowed_data_value.match?(value) if disallowed_data_value.is_a?(Regexp)
130
+ return true if normalized_disallowed_data_values.include?(value)
131
+
132
+ normalized_disallowed_data_values.select do |disallowed_data_value|
133
+ disallowed_data_value.is_a?(Regexp)
134
+ end.any? do |disallowed_data_value|
135
+ disallowed_data_value.match?(value)
133
136
  end
134
137
  end
135
138
 
@@ -11,11 +11,15 @@ module Mihari
11
11
  # @return [String, nil]
12
12
  attr_reader :api_key
13
13
 
14
+ # @return [String, nil]
15
+ attr_reader :api_version
16
+
14
17
  def initialize(*args, **kwargs)
15
18
  super(*args, **kwargs)
16
19
 
17
20
  @api_endpoint = kwargs[:api_endpoint] || Mihari.config.thehive_api_endpoint
18
21
  @api_key = kwargs[:api_key] || Mihari.config.thehive_api_key
22
+ @api_version = kwargs[:api_version] || Mihari.config.thehive_api_version
19
23
  end
20
24
 
21
25
  # @return [Boolean]
@@ -26,14 +30,28 @@ module Mihari
26
30
  def emit(title:, description:, artifacts:, tags: [], **_options)
27
31
  return if artifacts.empty?
28
32
 
29
- api.alert.create(
30
- title: title,
31
- description: description,
32
- artifacts: artifacts.map { |artifact| { data: artifact.data, data_type: artifact.data_type, message: description } },
33
- tags: tags,
34
- type: "external",
35
- source: "mihari"
36
- )
33
+ payload = payload(title: title, description: description, artifacts: artifacts, tags: tags)
34
+ api.alert.create(**payload)
35
+ end
36
+
37
+ #
38
+ # Normalize API version for API client
39
+ #
40
+ # @param [String] version
41
+ #
42
+ # @return [String, nil]
43
+ #
44
+ def normalized_api_version
45
+ @normalized_api_version ||= [].tap do |out|
46
+ # v4 does not have version prefix in path (/api/)
47
+ # v5 has version prefix in path (/api/v1/)
48
+ table = {
49
+ "" => nil,
50
+ "v4" => nil,
51
+ "v5" => "v1"
52
+ }
53
+ out << table[api_version.to_s.downcase]
54
+ end.first
37
55
  end
38
56
 
39
57
  private
@@ -43,7 +61,7 @@ module Mihari
43
61
  end
44
62
 
45
63
  def api
46
- @api ||= Hachi::API.new(api_endpoint: api_endpoint, api_key: api_key)
64
+ @api ||= Hachi::API.new(api_endpoint: api_endpoint, api_key: api_key, api_version: normalized_api_version)
47
65
  end
48
66
 
49
67
  #
@@ -64,6 +82,35 @@ module Mihari
64
82
  !api_key.nil?
65
83
  end
66
84
 
85
+ def payload(title:, description:, artifacts:, tags: [])
86
+ return v4_payload(title: title, description: description, artifacts: artifacts, tags: tags) if normalized_api_version.nil?
87
+
88
+ v5_payload(title: title, description: description, artifacts: artifacts, tags: tags)
89
+ end
90
+
91
+ def v4_payload(title:, description:, artifacts:, tags: [])
92
+ {
93
+ title: title,
94
+ description: description,
95
+ artifacts: artifacts.map { |artifact| { data: artifact.data, data_type: artifact.data_type, message: description } },
96
+ tags: tags,
97
+ type: "external",
98
+ source: "mihari"
99
+ }
100
+ end
101
+
102
+ def v5_payload(title:, description:, artifacts:, tags: [])
103
+ {
104
+ title: title,
105
+ description: description,
106
+ observables: artifacts.map { |artifact| { data: artifact.data, data_type: artifact.data_type, message: description } },
107
+ tags: tags,
108
+ type: "external",
109
+ source: "mihari",
110
+ source_ref: "1"
111
+ }
112
+ end
113
+
67
114
  #
68
115
  # Check whether an API endpoint is reachable or not
69
116
  #
@@ -71,9 +118,21 @@ module Mihari
71
118
  #
72
119
  def ping?
73
120
  base_url = api_endpoint.end_with?("/") ? api_endpoint[0..-2] : api_endpoint
74
- url = "#{base_url}/index.html"
121
+
122
+ if normalized_api_version.nil?
123
+ # for v4
124
+ base_url = api_endpoint.end_with?("/") ? api_endpoint[0..-2] : api_endpoint
125
+ url = "#{base_url}/index.html"
126
+ else
127
+ # for v5
128
+ url = "#{base_url}/api/v1/status/public"
129
+ end
75
130
 
76
131
  http = Net::Ping::HTTP.new(url)
132
+
133
+ # use GET for v5
134
+ http.get_request = true if normalized_api_version
135
+
77
136
  http.ping?
78
137
  end
79
138
  end
@@ -11,7 +11,7 @@ module Mihari
11
11
  def emit(title:, description:, artifacts:, source:, tags:)
12
12
  return if artifacts.empty?
13
13
 
14
- headers = { 'content-type': "application/x-www-form-urlencoded" }
14
+ headers = { "content-type": "application/x-www-form-urlencoded" }
15
15
  headers["content-type"] = "application/json" if use_json_body?
16
16
 
17
17
  emitter = Emitters::HTTP.new(uri: Mihari.config.webhook_url)
@@ -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.cpes.map { |cpe| new(cpe: cpe) }
20
20
  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.hostnames.map { |name| new(name: name) }
20
20
  end
@@ -16,6 +16,7 @@ module Mihari
16
16
  required(:emitter).value(Types::String.enum("the_hive"))
17
17
  optional(:api_endpoint).value(:string)
18
18
  optional(:api_key).value(:string)
19
+ optional(:api_version).value(Types::String.enum("v4", "v5")).default("v4")
19
20
  end
20
21
 
21
22
  Slack = Dry::Schema.Params do
@@ -4,7 +4,7 @@ module Mihari
4
4
  module Structs
5
5
  module VirusTotalIntelligence
6
6
  class ContextAttributes < Dry::Struct
7
- attribute :url, Types.Array(Types::String).optional
7
+ attribute :url, Types::String.optional
8
8
 
9
9
  def self.from_dynamic!(d)
10
10
  d = Types::Hash[d]
@@ -25,7 +25,7 @@ module Mihari
25
25
  when "file"
26
26
  id
27
27
  when "url"
28
- (context_attributes.url || []).first
28
+ context_attributes&.url
29
29
  when "domain"
30
30
  id
31
31
  when "ip_address"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- VERSION = "4.5.2"
4
+ VERSION = "4.6.1"
5
5
  end
@@ -53,6 +53,9 @@ module Mihari
53
53
 
54
54
  Rack::Handler::Puma.run(instance, Port: port, Host: host, Threads: threads, Verbose: verbose) do |_launcher|
55
55
  Launchy.open(url) if ENV["RACK_ENV"] != "development"
56
+ rescue Launchy::CommandNotFoundError
57
+ # ref. https://github.com/ninoseki/mihari/issues/477
58
+ # do nothing
56
59
  end
57
60
  end
58
61
  end
data/lib/mihari.rb CHANGED
@@ -71,34 +71,55 @@ end
71
71
  module Mihari
72
72
  extend Dry::Configurable
73
73
 
74
- setting :binaryedge_api_key, default: ENV["BINARYEDGE_API_KEY"]
75
- setting :censys_id, default: ENV["CENSYS_ID"]
76
- setting :censys_secret, default: ENV["CENSYS_SECRET"]
77
- setting :circl_passive_password, default: ENV["CIRCL_PASSIVE_PASSWORD"]
78
- setting :circl_passive_username, default: ENV["CIRCL_PASSIVE_USERNAME"]
79
- setting :database, default: ENV["DATABASE"] || "mihari.db"
80
- setting :greynoise_api_key, default: ENV["GREYNOISE_API_KEY"]
81
- setting :ipinfo_api_key, default: ENV["IPINFO_API_KEY"]
82
- setting :misp_api_endpoint, default: ENV["MISP_API_ENDPOINT"]
83
- setting :misp_api_key, default: ENV["MISP_API_KEY"]
84
- setting :onyphe_api_key, default: ENV["ONYPHE_API_KEY"]
85
- setting :otx_api_key, default: ENV["OTX_API_KEY"]
86
- setting :passivetotal_api_key, default: ENV["PASSIVETOTAL_API_KEY"]
87
- setting :passivetotal_username, default: ENV["PASSIVETOTAL_USERNAME"]
88
- setting :pulsedive_api_key, default: ENV["PULSEDIVE_API_KEY"]
89
- setting :securitytrails_api_key, default: ENV["SECURITYTRAILS_API_KEY"]
90
- setting :shodan_api_key, default: ENV["SHODAN_API_KEY"]
91
- setting :slack_channel, default: ENV["SLACK_CHANNEL"]
92
- setting :slack_webhook_url, default: ENV["SLACK_WEBHOOK_URL"]
93
- setting :spyse_api_key, default: ENV["SPYSE_API_KEY"]
94
- setting :thehive_api_endpoint, default: ENV["THEHIVE_API_ENDPOINT"]
95
- setting :thehive_api_key, default: ENV["THEHIVE_API_KEY"]
96
- setting :urlscan_api_key, default: ENV["URLSCAN_API_KEY"]
97
- setting :virustotal_api_key, default: ENV["VIRUSTOTAL_API_KEY"]
98
- setting :webhook_url, default: ENV["WEBHOOK_URL"]
99
- setting :webhook_use_json_body, constructor: ->(value = ENV["WEBHOOK_USE_JSON_BODY"]) { truthy?(value) }
100
- setting :zoomeye_api_key, default: ENV["ZOOMEYE_API_KEY"]
101
- setting :sentry_dsn, default: ENV["SENTRY_DSN"]
74
+ setting :binaryedge_api_key, default: ENV.fetch("BINARYEDGE_API_KEY", nil)
75
+
76
+ setting :censys_id, default: ENV.fetch("CENSYS_ID", nil)
77
+ setting :censys_secret, default: ENV.fetch("CENSYS_SECRET", nil)
78
+
79
+ setting :circl_passive_password, default: ENV.fetch("CIRCL_PASSIVE_PASSWORD", nil)
80
+ setting :circl_passive_username, default: ENV.fetch("CIRCL_PASSIVE_USERNAME", nil)
81
+
82
+ setting :database, default: ENV.fetch("DATABASE", "mihari.db")
83
+
84
+ setting :greynoise_api_key, default: ENV.fetch("GREYNOISE_API_KEY", nil)
85
+
86
+ setting :ipinfo_api_key, default: ENV.fetch("IPINFO_API_KEY", nil)
87
+
88
+ setting :misp_api_endpoint, default: ENV.fetch("MISP_API_ENDPOINT", nil)
89
+ setting :misp_api_key, default: ENV.fetch("MISP_API_KEY", nil)
90
+
91
+ setting :onyphe_api_key, default: ENV.fetch("ONYPHE_API_KEY", nil)
92
+
93
+ setting :otx_api_key, default: ENV.fetch("OTX_API_KEY", nil)
94
+
95
+ setting :passivetotal_api_key, default: ENV.fetch("PASSIVETOTAL_API_KEY", nil)
96
+ setting :passivetotal_username, default: ENV.fetch("PASSIVETOTAL_USERNAME", nil)
97
+
98
+ setting :pulsedive_api_key, default: ENV.fetch("PULSEDIVE_API_KEY", nil)
99
+
100
+ setting :securitytrails_api_key, default: ENV.fetch("SECURITYTRAILS_API_KEY", nil)
101
+
102
+ setting :shodan_api_key, default: ENV.fetch("SHODAN_API_KEY", nil)
103
+
104
+ setting :slack_channel, default: ENV.fetch("SLACK_CHANNEL", nil)
105
+ setting :slack_webhook_url, default: ENV.fetch("SLACK_WEBHOOK_URL", nil)
106
+
107
+ setting :spyse_api_key, default: ENV.fetch("SPYSE_API_KEY", nil)
108
+
109
+ setting :thehive_api_endpoint, default: ENV.fetch("THEHIVE_API_ENDPOINT", nil)
110
+ setting :thehive_api_key, default: ENV.fetch("THEHIVE_API_KEY", nil)
111
+ setting :thehive_api_version, default: ENV.fetch("THEHIVE_API_VERSION", nil)
112
+
113
+ setting :urlscan_api_key, default: ENV.fetch("URLSCAN_API_KEY", nil)
114
+
115
+ setting :virustotal_api_key, default: ENV.fetch("VIRUSTOTAL_API_KEY", nil)
116
+
117
+ setting :webhook_url, default: ENV.fetch("WEBHOOK_URL", nil)
118
+ setting :webhook_use_json_body, constructor: ->(value = ENV.fetch("WEBHOOK_USE_JSON_BODY", nil)) { truthy?(value) }
119
+
120
+ setting :zoomeye_api_key, default: ENV.fetch("ZOOMEYE_API_KEY", nil)
121
+
122
+ setting :sentry_dsn, default: ENV.fetch("SENTRY_DSN", nil)
102
123
 
103
124
  class << self
104
125
  include Memist::Memoizable
data/mihari.gemspec CHANGED
@@ -39,13 +39,13 @@ Gem::Specification.new do |spec|
39
39
  spec.add_development_dependency "rerun", "~> 0.13"
40
40
  spec.add_development_dependency "rspec", "~> 3.11"
41
41
  spec.add_development_dependency "simplecov-lcov", "~> 0.8.0"
42
- spec.add_development_dependency "standard", "~> 1.11"
42
+ spec.add_development_dependency "standard", "~> 1.12"
43
43
  spec.add_development_dependency "steep", "~> 0.52"
44
44
  spec.add_development_dependency "timecop", "~> 0.9"
45
45
  spec.add_development_dependency "vcr", "~> 6.1"
46
46
  spec.add_development_dependency "webmock", "~> 3.14"
47
47
 
48
- spec.add_dependency "activerecord", "7.0.2.4"
48
+ spec.add_dependency "activerecord", "7.0.3"
49
49
  spec.add_dependency "addressable", "2.8.0"
50
50
  spec.add_dependency "awrence", "2.0.1"
51
51
  spec.add_dependency "binaryedge", "0.1.0"
@@ -61,13 +61,13 @@ Gem::Specification.new do |spec|
61
61
  spec.add_dependency "dry-schema", "1.9.1"
62
62
  spec.add_dependency "dry-struct", "1.4.0"
63
63
  spec.add_dependency "dry-validation", "1.8.0"
64
- spec.add_dependency "email_address", "0.2.2"
64
+ spec.add_dependency "email_address", "0.2.3"
65
65
  spec.add_dependency "grape", "1.6.2"
66
66
  spec.add_dependency "grape-entity", "0.10.1"
67
67
  spec.add_dependency "grape-swagger", "1.4.2"
68
68
  spec.add_dependency "grape-swagger-entity", "0.5.1"
69
69
  spec.add_dependency "greynoise", "0.1.1"
70
- spec.add_dependency "hachi", "1.0.0"
70
+ spec.add_dependency "hachi", "2.0.0"
71
71
  spec.add_dependency "insensitive_hash", "0.3.3"
72
72
  spec.add_dependency "jr-cli", "0.5.1"
73
73
  spec.add_dependency "launchy", "2.5.0"
@@ -88,8 +88,8 @@ Gem::Specification.new do |spec|
88
88
  spec.add_dependency "rack-contrib", "2.3.0"
89
89
  spec.add_dependency "rack-cors", "1.1.1"
90
90
  spec.add_dependency "securitytrails", "1.0.0"
91
- spec.add_dependency "semantic_logger", "4.10.0"
92
- spec.add_dependency "sentry-ruby", "5.3.0"
91
+ spec.add_dependency "semantic_logger", "4.11.0"
92
+ spec.add_dependency "sentry-ruby", "5.3.1"
93
93
  spec.add_dependency "shodanx", "0.2.1"
94
94
  spec.add_dependency "slack-notifier", "2.4.0"
95
95
  spec.add_dependency "spysex", "0.2.0"
@@ -5,11 +5,15 @@ module Mihari
5
5
 
6
6
  attr_reader api_key: String?
7
7
 
8
+ attr_reader api_version: String?
9
+
8
10
  # @return [true, false]
9
11
  def valid?: () -> bool
10
12
 
11
13
  def emit: (title: untyped title, description: untyped description, artifacts: untyped artifacts, ?tags: untyped tags, **untyped _options) -> (nil | untyped)
12
14
 
15
+ def normalized_api_version: () -> String?
16
+
13
17
  private
14
18
 
15
19
  def configuration_keys: () -> ::Array["thehive_api_endpoint" | "thehive_api_key"]
data/sig/lib/mihari.rbs CHANGED
@@ -19,6 +19,7 @@ class Configuration
19
19
  attr_accessor spyse_api_key (): String?
20
20
  attr_accessor thehive_api_endpoint (): String?
21
21
  attr_accessor thehive_api_key (): String?
22
+ attr_accessor thehive_api_version (): String?
22
23
  attr_accessor urlscan_api_key (): String?
23
24
  attr_accessor virustotal_api_key (): String?
24
25
  attr_accessor zoomeye_api_key (): String?
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mihari
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.5.2
4
+ version: 4.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Manabu Niseki
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-05-08 00:00:00.000000000 Z
11
+ date: 2022-05-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -184,14 +184,14 @@ dependencies:
184
184
  requirements:
185
185
  - - "~>"
186
186
  - !ruby/object:Gem::Version
187
- version: '1.11'
187
+ version: '1.12'
188
188
  type: :development
189
189
  prerelease: false
190
190
  version_requirements: !ruby/object:Gem::Requirement
191
191
  requirements:
192
192
  - - "~>"
193
193
  - !ruby/object:Gem::Version
194
- version: '1.11'
194
+ version: '1.12'
195
195
  - !ruby/object:Gem::Dependency
196
196
  name: steep
197
197
  requirement: !ruby/object:Gem::Requirement
@@ -254,14 +254,14 @@ dependencies:
254
254
  requirements:
255
255
  - - '='
256
256
  - !ruby/object:Gem::Version
257
- version: 7.0.2.4
257
+ version: 7.0.3
258
258
  type: :runtime
259
259
  prerelease: false
260
260
  version_requirements: !ruby/object:Gem::Requirement
261
261
  requirements:
262
262
  - - '='
263
263
  - !ruby/object:Gem::Version
264
- version: 7.0.2.4
264
+ version: 7.0.3
265
265
  - !ruby/object:Gem::Dependency
266
266
  name: addressable
267
267
  requirement: !ruby/object:Gem::Requirement
@@ -478,14 +478,14 @@ dependencies:
478
478
  requirements:
479
479
  - - '='
480
480
  - !ruby/object:Gem::Version
481
- version: 0.2.2
481
+ version: 0.2.3
482
482
  type: :runtime
483
483
  prerelease: false
484
484
  version_requirements: !ruby/object:Gem::Requirement
485
485
  requirements:
486
486
  - - '='
487
487
  - !ruby/object:Gem::Version
488
- version: 0.2.2
488
+ version: 0.2.3
489
489
  - !ruby/object:Gem::Dependency
490
490
  name: grape
491
491
  requirement: !ruby/object:Gem::Requirement
@@ -562,14 +562,14 @@ dependencies:
562
562
  requirements:
563
563
  - - '='
564
564
  - !ruby/object:Gem::Version
565
- version: 1.0.0
565
+ version: 2.0.0
566
566
  type: :runtime
567
567
  prerelease: false
568
568
  version_requirements: !ruby/object:Gem::Requirement
569
569
  requirements:
570
570
  - - '='
571
571
  - !ruby/object:Gem::Version
572
- version: 1.0.0
572
+ version: 2.0.0
573
573
  - !ruby/object:Gem::Dependency
574
574
  name: insensitive_hash
575
575
  requirement: !ruby/object:Gem::Requirement
@@ -856,28 +856,28 @@ dependencies:
856
856
  requirements:
857
857
  - - '='
858
858
  - !ruby/object:Gem::Version
859
- version: 4.10.0
859
+ version: 4.11.0
860
860
  type: :runtime
861
861
  prerelease: false
862
862
  version_requirements: !ruby/object:Gem::Requirement
863
863
  requirements:
864
864
  - - '='
865
865
  - !ruby/object:Gem::Version
866
- version: 4.10.0
866
+ version: 4.11.0
867
867
  - !ruby/object:Gem::Dependency
868
868
  name: sentry-ruby
869
869
  requirement: !ruby/object:Gem::Requirement
870
870
  requirements:
871
871
  - - '='
872
872
  - !ruby/object:Gem::Version
873
- version: 5.3.0
873
+ version: 5.3.1
874
874
  type: :runtime
875
875
  prerelease: false
876
876
  version_requirements: !ruby/object:Gem::Requirement
877
877
  requirements:
878
878
  - - '='
879
879
  - !ruby/object:Gem::Version
880
- version: 5.3.0
880
+ version: 5.3.1
881
881
  - !ruby/object:Gem::Dependency
882
882
  name: shodanx
883
883
  requirement: !ruby/object:Gem::Requirement