mihari 7.1.2 → 7.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Dockerfile +1 -1
- data/Rakefile +15 -7
- data/build_frontend.sh +1 -1
- data/lefthook.yml +4 -1
- data/lib/mihari/actor.rb +21 -4
- data/lib/mihari/analyzers/base.rb +7 -18
- data/lib/mihari/analyzers/binaryedge.rb +0 -6
- data/lib/mihari/analyzers/censys.rb +0 -9
- data/lib/mihari/analyzers/circl.rb +0 -6
- data/lib/mihari/analyzers/fofa.rb +0 -6
- data/lib/mihari/analyzers/greynoise.rb +0 -6
- data/lib/mihari/analyzers/hunterhow.rb +0 -6
- data/lib/mihari/analyzers/onyphe.rb +0 -6
- data/lib/mihari/analyzers/otx.rb +0 -6
- data/lib/mihari/analyzers/passivetotal.rb +0 -4
- data/lib/mihari/analyzers/pulsedive.rb +0 -6
- data/lib/mihari/analyzers/securitytrails.rb +0 -4
- data/lib/mihari/analyzers/shodan.rb +0 -6
- data/lib/mihari/analyzers/urlscan.rb +0 -6
- data/lib/mihari/analyzers/virustotal.rb +0 -4
- data/lib/mihari/analyzers/virustotal_intelligence.rb +7 -6
- data/lib/mihari/analyzers/zoomeye.rb +0 -6
- data/lib/mihari/commands/web.rb +4 -4
- data/lib/mihari/concerns/falsepositive_normalizable.rb +30 -0
- data/lib/mihari/concerns/falsepositive_validatable.rb +1 -17
- data/lib/mihari/config.rb +1 -1
- data/lib/mihari/database.rb +18 -1
- data/lib/mihari/emitters/database.rb +0 -6
- data/lib/mihari/emitters/misp.rb +0 -6
- data/lib/mihari/emitters/slack.rb +5 -21
- data/lib/mihari/emitters/the_hive.rb +0 -6
- data/lib/mihari/enrichers/whois.rb +5 -7
- data/lib/mihari/entities/artifact.rb +6 -2
- data/lib/mihari/entities/autonomous_system.rb +1 -1
- data/lib/mihari/entities/cpe.rb +1 -1
- data/lib/mihari/entities/port.rb +1 -1
- data/lib/mihari/entities/vulnerability.rb +10 -0
- data/lib/mihari/errors.rb +16 -1
- data/lib/mihari/models/artifact.rb +65 -30
- data/lib/mihari/models/vulnerability.rb +12 -0
- data/lib/mihari/rule.rb +18 -24
- data/lib/mihari/schemas/rule.rb +7 -0
- data/lib/mihari/services/builders.rb +22 -3
- data/lib/mihari/services/enrichers.rb +2 -0
- data/lib/mihari/services/feed.rb +2 -5
- data/lib/mihari/services/proxies.rb +3 -3
- data/lib/mihari/structs/censys.rb +2 -2
- data/lib/mihari/structs/config.rb +3 -20
- data/lib/mihari/structs/greynoise.rb +1 -1
- data/lib/mihari/structs/onyphe.rb +1 -1
- data/lib/mihari/structs/shodan.rb +59 -21
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/endpoints/artifacts.rb +4 -2
- data/lib/mihari/web/endpoints/rules.rb +1 -1
- data/lib/mihari/web/public/assets/{index-Guw2aMpk.js → index-GWurHG1o.js} +60 -40
- data/lib/mihari/web/public/assets/{index-dVaNxqTC.css → index-ReF8ffd-.css} +1 -1
- data/lib/mihari/web/public/index.html +2 -2
- data/lib/mihari/web/public/redoc-static.html +385 -385
- data/lib/mihari.rb +3 -0
- metadata +11 -51
- data/test.json.jbuilder +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6b91d99562526b653b793f71e8ef5575f113d26859ca86b91f1980d1c44e898c
|
4
|
+
data.tar.gz: 07c302ce446b0f0986bfc82dd575ebde203f4b25b73a1aee72a2ce37a08b9b87
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 67d607a09ab2992b6721358b9e96e0c049a355221b2e97358440d70f96ef4933ac86b337bcc12bdc4b2aafccf4e5d6cf8cd68f4b2bbe567523bfba22b7fbd88c
|
7
|
+
data.tar.gz: 6f73de19824d31e21bae4ee7ce456c1b15fa0a875d1f07025c33ac0356ef257ac2eb819ffd8b685bf8914a472da04f691e5ae02a361397da9a83253d5345b108
|
data/Dockerfile
CHANGED
@@ -2,7 +2,7 @@ FROM ruby:3.2.2-alpine3.19
|
|
2
2
|
|
3
3
|
ARG MIHARI_VERSION=0.0.0
|
4
4
|
|
5
|
-
RUN apk --no-cache add build-base ruby-dev libpq-dev && \
|
5
|
+
RUN apk --no-cache add build-base ruby-dev libpq-dev whois && \
|
6
6
|
echo 'gem: --no-document' >> /usr/local/etc/gemrc && \
|
7
7
|
gem install pg && \
|
8
8
|
gem install mihari -v ${MIHARI_VERSION} && \
|
data/Rakefile
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "time"
|
4
|
+
|
3
5
|
require "rspec/core/rake_task"
|
4
6
|
require "standard/rake"
|
5
7
|
|
@@ -46,18 +48,24 @@ namespace :build do
|
|
46
48
|
desc "Build Swagger doc"
|
47
49
|
task :swagger, [:path] do |_t, args|
|
48
50
|
args.with_defaults(path: "./frontend/swagger.yaml")
|
51
|
+
|
52
|
+
started_at = Time.now
|
49
53
|
build_swagger_doc args.path
|
54
|
+
elapsed = (Time.now - started_at).floor(2)
|
55
|
+
|
56
|
+
puts "Swagger doc is built in #{elapsed}s"
|
50
57
|
end
|
51
58
|
end
|
52
59
|
|
53
|
-
|
54
|
-
|
55
|
-
end
|
60
|
+
task :build do
|
61
|
+
Rake::Task["build:swagger"].invoke
|
56
62
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
63
|
+
# Build ReDocs docs & frontend assets
|
64
|
+
sh "cd frontend && npm install && npm run docs && npm run build-only"
|
65
|
+
# Copy built assets into ./lib/web/public/
|
66
|
+
sh "rm -rf ./lib/mihari/web/public/"
|
67
|
+
sh "mkdir -p ./lib/mihari/web/public/"
|
68
|
+
sh "cp -r frontend/dist/* ./lib/mihari/web/public"
|
61
69
|
end
|
62
70
|
|
63
71
|
# require it later enables doing pre-build step (= build the frontend app)
|
data/build_frontend.sh
CHANGED
data/lefthook.yml
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
pre-commit:
|
2
|
-
parallel: true
|
3
2
|
commands:
|
4
3
|
standard:
|
5
4
|
glob: "*.rb"
|
@@ -15,6 +14,10 @@ pre-commit:
|
|
15
14
|
glob: "*.{js,ts,vue}"
|
16
15
|
run: npx prettier --write {staged_files}
|
17
16
|
stage_fixed: true
|
17
|
+
type-check:
|
18
|
+
root: "frontend/"
|
19
|
+
glob: "*.{js,ts,vue}"
|
20
|
+
run: npm run type-check
|
18
21
|
actionlint:
|
19
22
|
glob: ".github/workflows/*.yaml"
|
20
23
|
run: actionlint
|
data/lib/mihari/actor.rb
CHANGED
@@ -53,10 +53,10 @@ module Mihari
|
|
53
53
|
def validate_configuration!
|
54
54
|
return if configured?
|
55
55
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
raise ConfigurationError,
|
56
|
+
message = "#{self.class.type.capitalize}:#{self.class.key} is not configured correctly"
|
57
|
+
detail = self.class.configuration_keys.map { |key| "#{key.upcase} is missing" }
|
58
|
+
|
59
|
+
raise ConfigurationError.new(message, detail)
|
60
60
|
end
|
61
61
|
|
62
62
|
def call(*args, **kwargs)
|
@@ -92,6 +92,23 @@ module Mihari
|
|
92
92
|
def keys
|
93
93
|
([key] + [key_aliases]).flatten.compact.map(&:downcase)
|
94
94
|
end
|
95
|
+
|
96
|
+
def configuration_keys
|
97
|
+
# Automatically generate configuration keys based on key
|
98
|
+
# For example,
|
99
|
+
# - Shodan analyzer's key is "shodan"
|
100
|
+
# - Mihari.config has "shodan_api_key"
|
101
|
+
# - Select "shodan_api_key" by using "#{key}_" prefix
|
102
|
+
Mihari.config.keys.select { |config_key| config_key.start_with?("#{key}_") }
|
103
|
+
end
|
104
|
+
|
105
|
+
def type
|
106
|
+
return "analyzer" if ancestors.include?(Mihari::Analyzers::Base)
|
107
|
+
return "emitter" if ancestors.include?(Mihari::Emitters::Base)
|
108
|
+
return "enricher" if ancestors.include?(Mihari::Enrichers::Base)
|
109
|
+
|
110
|
+
nil
|
111
|
+
end
|
95
112
|
end
|
96
113
|
end
|
97
114
|
end
|
@@ -63,12 +63,10 @@ module Mihari
|
|
63
63
|
artifacts.compact.sort.map do |artifact|
|
64
64
|
# No need to set data_type manually
|
65
65
|
# It is set automatically in #initialize
|
66
|
-
artifact
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
artifact
|
66
|
+
(artifact.is_a?(Models::Artifact) ? artifact : Models::Artifact.new(data: artifact)).tap do |normalized|
|
67
|
+
normalized.source = self.class.key
|
68
|
+
normalized.query = query
|
69
|
+
end
|
72
70
|
end.select(&:valid?).uniq(&:data)
|
73
71
|
end
|
74
72
|
|
@@ -118,18 +116,9 @@ module Mihari
|
|
118
116
|
#
|
119
117
|
# @return [Mihari::Analyzers::Base]
|
120
118
|
#
|
121
|
-
def
|
122
|
-
|
123
|
-
|
124
|
-
# convert params into arguments for initialization
|
125
|
-
query = copied[:query]
|
126
|
-
|
127
|
-
# delete analyzer and query
|
128
|
-
%i[analyzer query].each { |key| copied.delete key }
|
129
|
-
|
130
|
-
copied[:options] = copied[:options] || nil
|
131
|
-
|
132
|
-
new(query, **copied)
|
119
|
+
def from_params(params)
|
120
|
+
query = params.delete(:query)
|
121
|
+
new(query, **params)
|
133
122
|
end
|
134
123
|
|
135
124
|
def inherited(child)
|
data/lib/mihari/analyzers/otx.rb
CHANGED
@@ -24,12 +24,6 @@ module Mihari
|
|
24
24
|
client.intel_search_with_pagination(query, pagination_limit: pagination_limit).map(&:artifacts).flatten
|
25
25
|
end
|
26
26
|
|
27
|
-
class << self
|
28
|
-
def configuration_keys
|
29
|
-
%w[virustotal_api_key]
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
27
|
class << self
|
34
28
|
#
|
35
29
|
# @return [String]
|
@@ -44,6 +38,13 @@ module Mihari
|
|
44
38
|
def key_aliases
|
45
39
|
["vt_intel"]
|
46
40
|
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# @return [Array<String>]
|
44
|
+
#
|
45
|
+
def configuration_keys
|
46
|
+
%w[virustotal_api_key]
|
47
|
+
end
|
47
48
|
end
|
48
49
|
|
49
50
|
private
|
data/lib/mihari/commands/web.rb
CHANGED
@@ -10,10 +10,10 @@ module Mihari
|
|
10
10
|
def included(thor)
|
11
11
|
thor.class_eval do
|
12
12
|
desc "web", "Start the web app"
|
13
|
-
method_option :port, type: :numeric, default: 9292, desc: "
|
14
|
-
method_option :host, type: :string, default: "localhost", desc: "
|
15
|
-
method_option :threads, type: :string, default: "0:
|
16
|
-
method_option :verbose, type: :boolean, default:
|
13
|
+
method_option :port, type: :numeric, default: 9292, desc: "Port to listen on"
|
14
|
+
method_option :host, type: :string, default: "localhost", desc: "Hostname to listen on"
|
15
|
+
method_option :threads, type: :string, default: "0:3", desc: "min:max threads to use"
|
16
|
+
method_option :verbose, type: :boolean, default: false, desc: "Don't report each request"
|
17
17
|
method_option :worker_timeout, type: :numeric, default: 60, desc: "Worker timeout value (in seconds)"
|
18
18
|
method_option :open, type: :boolean, default: true, desc: "Whether to open the app in browser or not"
|
19
19
|
method_option :env, type: :string, default: "production", desc: "Environment"
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mihari
|
4
|
+
module Concerns
|
5
|
+
#
|
6
|
+
# False positive normalizable concern
|
7
|
+
#
|
8
|
+
module FalsePositiveNormalizable
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
prepend MemoWise
|
12
|
+
|
13
|
+
#
|
14
|
+
# Normalize a falsepositive value
|
15
|
+
#
|
16
|
+
# @param [String] value
|
17
|
+
#
|
18
|
+
# @return [String, Regexp]
|
19
|
+
#
|
20
|
+
def normalize_falsepositive(value)
|
21
|
+
return value if !value.start_with?("/") || !value.end_with?("/")
|
22
|
+
|
23
|
+
# if a value is surrounded by slashes, take it as a regexp
|
24
|
+
value_without_slashes = value[1..-2]
|
25
|
+
Regexp.compile value_without_slashes.to_s
|
26
|
+
end
|
27
|
+
memo_wise :normalize_falsepositive
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -8,23 +8,7 @@ module Mihari
|
|
8
8
|
module FalsePositiveValidatable
|
9
9
|
extend ActiveSupport::Concern
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
#
|
14
|
-
# Normalize a falsepositive value
|
15
|
-
#
|
16
|
-
# @param [String] value
|
17
|
-
#
|
18
|
-
# @return [String, Regexp]
|
19
|
-
#
|
20
|
-
def normalize_falsepositive(value)
|
21
|
-
return value if !value.start_with?("/") || !value.end_with?("/")
|
22
|
-
|
23
|
-
# if a value is surrounded by slashes, take it as a regexp
|
24
|
-
value_without_slashes = value[1..-2]
|
25
|
-
Regexp.compile value_without_slashes.to_s
|
26
|
-
end
|
27
|
-
memo_wise :normalize_falsepositive
|
11
|
+
include FalsePositiveNormalizable
|
28
12
|
|
29
13
|
#
|
30
14
|
# Check whether a value is valid format as a disallowed data value
|
data/lib/mihari/config.rb
CHANGED
data/lib/mihari/database.rb
CHANGED
@@ -104,11 +104,28 @@ class V7Schema < ActiveRecord::Migration[7.1]
|
|
104
104
|
end
|
105
105
|
end
|
106
106
|
|
107
|
+
class V72Schema < ActiveRecord::Migration[7.1]
|
108
|
+
def change
|
109
|
+
create_table :vulnerabilities, if_not_exists: true do |t|
|
110
|
+
t.string :name, null: false
|
111
|
+
t.datetime :created_at
|
112
|
+
|
113
|
+
t.belongs_to :artifact, foreign_key: true, null: false
|
114
|
+
end
|
115
|
+
|
116
|
+
rename_column :cpes, :cpe, :name if ActiveRecord::Base.connection.column_exists?(:cpes, :cpe)
|
117
|
+
rename_column :autonomous_systems, :asn, :number if ActiveRecord::Base.connection.column_exists?(
|
118
|
+
:autonomous_systems, :asn
|
119
|
+
)
|
120
|
+
rename_column :ports, :port, :number if ActiveRecord::Base.connection.column_exists?(:ports, :port)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
107
124
|
#
|
108
125
|
# @return [Array<ActiveRecord::Migration>] schemas
|
109
126
|
#
|
110
127
|
def schemas
|
111
|
-
[V7Schema]
|
128
|
+
[V7Schema, V72Schema]
|
112
129
|
end
|
113
130
|
|
114
131
|
module Mihari
|
data/lib/mihari/emitters/misp.rb
CHANGED
@@ -176,21 +176,11 @@ module Mihari
|
|
176
176
|
# @return [::Slack::Notifier]
|
177
177
|
#
|
178
178
|
def notifier
|
179
|
-
@notifier ||=
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
)
|
185
|
-
else
|
186
|
-
::Slack::Notifier.new(
|
187
|
-
webhook_url,
|
188
|
-
channel: channel,
|
189
|
-
username: username,
|
190
|
-
http_options: { timeout: timeout }
|
191
|
-
)
|
192
|
-
end
|
193
|
-
end.first
|
179
|
+
@notifier ||= lambda do
|
180
|
+
return ::Slack::Notifier.new(webhook_url, channel: channel, username: username) if timeout.nil?
|
181
|
+
|
182
|
+
::Slack::Notifier.new(webhook_url, channel: channel, username: username, http_options: { timeout: timeout })
|
183
|
+
end.call
|
194
184
|
end
|
195
185
|
|
196
186
|
#
|
@@ -227,12 +217,6 @@ module Mihari
|
|
227
217
|
|
228
218
|
notifier.post(text: text, attachments: attachments, mrkdwn: true)
|
229
219
|
end
|
230
|
-
|
231
|
-
class << self
|
232
|
-
def configuration_keys
|
233
|
-
%w[slack_webhook_url slack_channel]
|
234
|
-
end
|
235
|
-
end
|
236
220
|
end
|
237
221
|
end
|
238
222
|
end
|
@@ -53,13 +53,11 @@ module Mihari
|
|
53
53
|
# @return [::Whois::Client]
|
54
54
|
#
|
55
55
|
def whois
|
56
|
-
@whois ||=
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
end
|
62
|
-
end.last
|
56
|
+
@whois ||= lambda do
|
57
|
+
return ::Whois::Client.new if timeout.nil?
|
58
|
+
|
59
|
+
::Whois::Client.new(timeout: timeout)
|
60
|
+
end.call
|
63
61
|
end
|
64
62
|
|
65
63
|
#
|
@@ -8,12 +8,12 @@ module Mihari
|
|
8
8
|
expose :data_type, documentation: { type: String, required: true }, as: :dataType
|
9
9
|
expose :source, documentation: { type: String, required: true }
|
10
10
|
expose :query, documentation: { type: String, required: false }
|
11
|
-
expose :metadata, documentation: { type: Hash }
|
12
11
|
expose :created_at, documentation: { type: DateTime, required: true }, as: :createdAt
|
12
|
+
expose :tags, using: Entities::Tag, documentation: { type: Entities::Tag, is_array: true, required: true }
|
13
13
|
end
|
14
14
|
|
15
15
|
class Artifact < BaseArtifact
|
16
|
-
expose :
|
16
|
+
expose :metadata, documentation: { type: Hash }
|
17
17
|
expose :autonomous_system, using: Entities::AutonomousSystem,
|
18
18
|
documentation: { type: Entities::AutonomousSystem, required: false }, as: :autonomousSystem
|
19
19
|
expose :geolocation, using: Entities::Geolocation, documentation: { type: Entities::Geolocation, required: false }
|
@@ -36,6 +36,10 @@ module Mihari
|
|
36
36
|
as: :ports do |status, _options|
|
37
37
|
status.ports.empty? ? nil : status.ports
|
38
38
|
end
|
39
|
+
expose :vulnerabilities, using: Vulnerability, documentation: { type: Vulnerability, is_array: true, required: false },
|
40
|
+
as: :vulnerabilities do |status, _options|
|
41
|
+
status.vulnerabilities.empty? ? nil : status.vulnerabilities
|
42
|
+
end
|
39
43
|
end
|
40
44
|
|
41
45
|
class ArtifactsWithPagination < Pagination
|
data/lib/mihari/entities/cpe.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
module Mihari
|
4
4
|
module Entities
|
5
5
|
class CPE < Grape::Entity
|
6
|
-
expose :
|
6
|
+
expose :name, documentation: { type: String, required: true }
|
7
7
|
expose :created_at, documentation: { type: DateTime, required: true }, as: :createdAt
|
8
8
|
end
|
9
9
|
end
|