mihari 5.2.0 → 5.2.2
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/.rubocop.yml +2 -0
- data/lib/mihari/analyzers/base.rb +20 -115
- data/lib/mihari/analyzers/censys.rb +20 -2
- data/lib/mihari/analyzers/onyphe.rb +1 -1
- data/lib/mihari/analyzers/rule.rb +116 -60
- data/lib/mihari/analyzers/shodan.rb +1 -1
- data/lib/mihari/analyzers/urlscan.rb +6 -9
- data/lib/mihari/analyzers/virustotal_intelligence.rb +1 -5
- data/lib/mihari/cli/main.rb +2 -2
- data/lib/mihari/commands/search.rb +69 -0
- data/lib/mihari/commands/web.rb +9 -2
- data/lib/mihari/mixins/error_notification.rb +0 -2
- data/lib/mihari/models/artifact.rb +1 -1
- data/lib/mihari/schemas/rule.rb +2 -17
- data/lib/mihari/structs/censys.rb +167 -11
- data/lib/mihari/structs/config.rb +28 -0
- data/lib/mihari/structs/google_public_dns.rb +39 -1
- data/lib/mihari/structs/greynoise.rb +93 -6
- data/lib/mihari/structs/ipinfo.rb +40 -0
- data/lib/mihari/structs/onyphe.rb +88 -6
- data/lib/mihari/structs/rule.rb +4 -2
- data/lib/mihari/structs/shodan.rb +138 -4
- data/lib/mihari/structs/urlscan.rb +98 -1
- data/lib/mihari/structs/virustotal_intelligence.rb +96 -1
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/app.rb +2 -2
- data/lib/mihari/web/public/assets/index-cbe1734c.js +50 -0
- data/lib/mihari/web/public/assets/index-eed1bcd8.css +5 -0
- data/lib/mihari/web/public/index.html +2 -2
- data/lib/mihari.rb +1 -0
- data/mihari.gemspec +13 -12
- metadata +41 -42
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -43
- data/.github/ISSUE_TEMPLATE/feature_request.md +0 -15
- data/.github/workflows/test.yml +0 -90
- data/config/pre_commit.yml +0 -3
- data/docker/Dockerfile +0 -14
- data/examples/ipinfo_hosted_domains.rb +0 -45
- data/images/Tines-Full_Logo-Tines_Black.png +0 -0
- data/images/alert.png +0 -0
- data/images/logo.png +0 -0
- data/images/misp.png +0 -0
- data/images/overview.jpg +0 -0
- data/images/slack.png +0 -0
- data/images/tines.png +0 -0
- data/images/web_alerts.png +0 -0
- data/images/web_config.png +0 -0
- data/lib/mihari/commands/searcher.rb +0 -61
- data/lib/mihari/web/public/assets/index-9948ee35.js +0 -50
- data/lib/mihari/web/public/assets/index-d88cc3f1.css +0 -5
data/lib/mihari/structs/rule.rb
CHANGED
@@ -10,6 +10,8 @@ require "yaml"
|
|
10
10
|
module Mihari
|
11
11
|
module Structs
|
12
12
|
class Rule
|
13
|
+
include Mixins::FalsePositive
|
14
|
+
|
13
15
|
# @return [Hash]
|
14
16
|
attr_reader :data
|
15
17
|
|
@@ -109,10 +111,10 @@ module Mihari
|
|
109
111
|
end
|
110
112
|
|
111
113
|
#
|
112
|
-
# @return [Array<String>]
|
114
|
+
# @return [Array<String, RegExp>]
|
113
115
|
#
|
114
116
|
def falsepositives
|
115
|
-
@falsepositives ||= data[:falsepositives]
|
117
|
+
@falsepositives ||= data[:falsepositives].map { |fp| normalize_falsepositive fp }
|
116
118
|
end
|
117
119
|
|
118
120
|
#
|
@@ -7,6 +7,20 @@ module Mihari
|
|
7
7
|
attribute :country_code, Types::String.optional
|
8
8
|
attribute :country_name, Types::String.optional
|
9
9
|
|
10
|
+
#
|
11
|
+
# @return [String, nil]
|
12
|
+
#
|
13
|
+
def country_code
|
14
|
+
attributes[:country_code]
|
15
|
+
end
|
16
|
+
|
17
|
+
#
|
18
|
+
# @return [String, nil]
|
19
|
+
#
|
20
|
+
def country_name
|
21
|
+
attributes[:country_name]
|
22
|
+
end
|
23
|
+
|
10
24
|
#
|
11
25
|
# @return [Mihari::Geolocation, nil]
|
12
26
|
#
|
@@ -19,6 +33,11 @@ module Mihari
|
|
19
33
|
)
|
20
34
|
end
|
21
35
|
|
36
|
+
#
|
37
|
+
# @param [Hash] d
|
38
|
+
#
|
39
|
+
# @return [Location]
|
40
|
+
#
|
22
41
|
def self.from_dynamic!(d)
|
23
42
|
d = Types::Hash[d]
|
24
43
|
new(
|
@@ -39,6 +58,48 @@ module Mihari
|
|
39
58
|
attribute :port, Types::Integer
|
40
59
|
attribute :metadata, Types::Hash
|
41
60
|
|
61
|
+
#
|
62
|
+
# @return [String, nil]
|
63
|
+
#
|
64
|
+
def asn
|
65
|
+
attributes[:asn]
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# @return [Array<String>]
|
70
|
+
#
|
71
|
+
def hostnames
|
72
|
+
attributes[:hostnames]
|
73
|
+
end
|
74
|
+
|
75
|
+
#
|
76
|
+
# @return [Location]
|
77
|
+
#
|
78
|
+
def location
|
79
|
+
attributes[:location]
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
# @return [String]
|
84
|
+
#
|
85
|
+
def ip_str
|
86
|
+
attributes[:ip_str]
|
87
|
+
end
|
88
|
+
|
89
|
+
#
|
90
|
+
# @return [Integer]
|
91
|
+
#
|
92
|
+
def port
|
93
|
+
attributes[:port]
|
94
|
+
end
|
95
|
+
|
96
|
+
#
|
97
|
+
# @return [Hash]
|
98
|
+
#
|
99
|
+
def metadata
|
100
|
+
attributes[:metadata]
|
101
|
+
end
|
102
|
+
|
42
103
|
#
|
43
104
|
# @return [Mihari::AutonomousSystem, nil]
|
44
105
|
#
|
@@ -48,6 +109,11 @@ module Mihari
|
|
48
109
|
Mihari::AutonomousSystem.new(asn: normalize_asn(asn))
|
49
110
|
end
|
50
111
|
|
112
|
+
#
|
113
|
+
# @param [Hash] d
|
114
|
+
#
|
115
|
+
# @return [Match]
|
116
|
+
#
|
51
117
|
def self.from_dynamic!(d)
|
52
118
|
d = Types::Hash[d]
|
53
119
|
|
@@ -74,6 +140,20 @@ module Mihari
|
|
74
140
|
attribute :matches, Types.Array(Match)
|
75
141
|
attribute :total, Types::Int
|
76
142
|
|
143
|
+
#
|
144
|
+
# @return [Array<Match>]
|
145
|
+
#
|
146
|
+
def matches
|
147
|
+
attributes[:matches]
|
148
|
+
end
|
149
|
+
|
150
|
+
#
|
151
|
+
# @return [Integer]
|
152
|
+
#
|
153
|
+
def total
|
154
|
+
attributes[:total]
|
155
|
+
end
|
156
|
+
|
77
157
|
#
|
78
158
|
# Collect metadata from matches
|
79
159
|
#
|
@@ -107,12 +187,10 @@ module Mihari
|
|
107
187
|
matches.select { |match| match.ip_str == ip }.map(&:hostnames).flatten.uniq
|
108
188
|
end
|
109
189
|
|
110
|
-
#
|
111
|
-
# @param [Source] source
|
112
190
|
#
|
113
191
|
# @return [Array<Mihari::Artifact>]
|
114
192
|
#
|
115
|
-
def to_artifacts
|
193
|
+
def to_artifacts
|
116
194
|
matches.map do |match|
|
117
195
|
metadata = collect_metadata_by_ip(match.ip_str)
|
118
196
|
ports = collect_ports_by_ip(match.ip_str).map do |port|
|
@@ -124,7 +202,6 @@ module Mihari
|
|
124
202
|
|
125
203
|
Mihari::Artifact.new(
|
126
204
|
data: match.ip_str,
|
127
|
-
source: source,
|
128
205
|
metadata: metadata,
|
129
206
|
autonomous_system: match.to_asn,
|
130
207
|
geolocation: match.location.to_geolocation,
|
@@ -134,6 +211,11 @@ module Mihari
|
|
134
211
|
end
|
135
212
|
end
|
136
213
|
|
214
|
+
#
|
215
|
+
# @param [Hash] d
|
216
|
+
#
|
217
|
+
# @return [Result]
|
218
|
+
#
|
137
219
|
def self.from_dynamic!(d)
|
138
220
|
d = Types::Hash[d]
|
139
221
|
new(
|
@@ -151,6 +233,53 @@ module Mihari
|
|
151
233
|
attribute :tags, Types.Array(Types::String)
|
152
234
|
attribute :vulns, Types.Array(Types::String)
|
153
235
|
|
236
|
+
#
|
237
|
+
# @return [String]
|
238
|
+
#
|
239
|
+
def ip
|
240
|
+
attributes[:ip]
|
241
|
+
end
|
242
|
+
|
243
|
+
#
|
244
|
+
# @return [Array<Integer>]
|
245
|
+
#
|
246
|
+
def ports
|
247
|
+
attributes[:ports]
|
248
|
+
end
|
249
|
+
|
250
|
+
#
|
251
|
+
# @return [Array<String>]
|
252
|
+
#
|
253
|
+
def cpes
|
254
|
+
attributes[:cpes]
|
255
|
+
end
|
256
|
+
|
257
|
+
#
|
258
|
+
# @return [Array<String>]
|
259
|
+
#
|
260
|
+
def hostnames
|
261
|
+
attributes[:hostnames]
|
262
|
+
end
|
263
|
+
|
264
|
+
#
|
265
|
+
# @return [Array<String>]
|
266
|
+
#
|
267
|
+
def tags
|
268
|
+
attributes[:tags]
|
269
|
+
end
|
270
|
+
|
271
|
+
#
|
272
|
+
# @return [Array<String>]
|
273
|
+
#
|
274
|
+
def vulns
|
275
|
+
attributes[:vulns]
|
276
|
+
end
|
277
|
+
|
278
|
+
#
|
279
|
+
# @param [Hash] d
|
280
|
+
#
|
281
|
+
# @return [InternetDBResponse]
|
282
|
+
#
|
154
283
|
def self.from_dynamic!(d)
|
155
284
|
d = Types::Hash[d]
|
156
285
|
new(
|
@@ -163,6 +292,11 @@ module Mihari
|
|
163
292
|
)
|
164
293
|
end
|
165
294
|
|
295
|
+
#
|
296
|
+
# @param [String] json
|
297
|
+
#
|
298
|
+
# @return [InternetDBResponse]
|
299
|
+
#
|
166
300
|
def self.from_json!(json)
|
167
301
|
from_dynamic!(JSON.parse(json))
|
168
302
|
end
|
@@ -8,6 +8,32 @@ module Mihari
|
|
8
8
|
attribute :ip, Types::String.optional
|
9
9
|
attribute :url, Types::String
|
10
10
|
|
11
|
+
#
|
12
|
+
# @return [String, nil]
|
13
|
+
#
|
14
|
+
def domain
|
15
|
+
attributes[:domain]
|
16
|
+
end
|
17
|
+
|
18
|
+
#
|
19
|
+
# @return [String, nil]
|
20
|
+
#
|
21
|
+
def ip
|
22
|
+
attributes[:ip]
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# @return [String]
|
27
|
+
#
|
28
|
+
def url
|
29
|
+
attributes[:url]
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# @param [Hash] d
|
34
|
+
#
|
35
|
+
# @return [Page]
|
36
|
+
#
|
11
37
|
def self.from_dynamic!(d)
|
12
38
|
d = Types::Hash[d]
|
13
39
|
new(
|
@@ -22,13 +48,58 @@ module Mihari
|
|
22
48
|
attribute :page, Page
|
23
49
|
attribute :id, Types::String
|
24
50
|
attribute :sort, Types.Array(Types::String | Types::Integer)
|
51
|
+
attribute :metadata, Types::Hash
|
52
|
+
|
53
|
+
#
|
54
|
+
# @return [Page]
|
55
|
+
#
|
56
|
+
def page
|
57
|
+
attributes[:page]
|
58
|
+
end
|
59
|
+
|
60
|
+
#
|
61
|
+
# @return [String]
|
62
|
+
#
|
63
|
+
def id
|
64
|
+
attributes[:id]
|
65
|
+
end
|
66
|
+
|
67
|
+
#
|
68
|
+
# @return [Array<String, Integer>]
|
69
|
+
#
|
70
|
+
def sort
|
71
|
+
attributes[:sort]
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# @return [Array<String, Integer>]
|
76
|
+
#
|
77
|
+
def metadata
|
78
|
+
attributes[:metadata]
|
79
|
+
end
|
25
80
|
|
81
|
+
#
|
82
|
+
# @return [Array<Mihari::Artifact>]
|
83
|
+
#
|
84
|
+
def to_artifacts
|
85
|
+
values = [page.url, page.domain, page.ip].compact
|
86
|
+
values.map do |value|
|
87
|
+
Mihari::Artifact.new(data: value, metadata: metadata)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
#
|
92
|
+
# @param [Hash] d
|
93
|
+
#
|
94
|
+
# @return [Result]
|
95
|
+
#
|
26
96
|
def self.from_dynamic!(d)
|
27
97
|
d = Types::Hash[d]
|
28
98
|
new(
|
29
99
|
page: Page.from_dynamic!(d.fetch("page")),
|
30
100
|
id: d.fetch("_id"),
|
31
|
-
sort: d.fetch("sort")
|
101
|
+
sort: d.fetch("sort"),
|
102
|
+
metadata: d
|
32
103
|
)
|
33
104
|
end
|
34
105
|
end
|
@@ -37,6 +108,32 @@ module Mihari
|
|
37
108
|
attribute :results, Types.Array(Result)
|
38
109
|
attribute :has_more, Types::Bool
|
39
110
|
|
111
|
+
#
|
112
|
+
# @return [Array<Result>]
|
113
|
+
#
|
114
|
+
def results
|
115
|
+
attributes[:results]
|
116
|
+
end
|
117
|
+
|
118
|
+
#
|
119
|
+
# @return [Boolean]
|
120
|
+
#
|
121
|
+
def has_more
|
122
|
+
attributes[:has_more]
|
123
|
+
end
|
124
|
+
|
125
|
+
#
|
126
|
+
# @return [Array<Mihari::Artifact>]
|
127
|
+
#
|
128
|
+
def to_artifacts
|
129
|
+
results.map(&:to_artifacts).flatten
|
130
|
+
end
|
131
|
+
|
132
|
+
#
|
133
|
+
# @param [Hash] d
|
134
|
+
#
|
135
|
+
# @return [Response]
|
136
|
+
#
|
40
137
|
def self.from_dynamic!(d)
|
41
138
|
d = Types::Hash[d]
|
42
139
|
new(
|
@@ -6,6 +6,18 @@ module Mihari
|
|
6
6
|
class ContextAttributes < Dry::Struct
|
7
7
|
attribute :url, Types::String.optional
|
8
8
|
|
9
|
+
#
|
10
|
+
# @return [String, nil]
|
11
|
+
#
|
12
|
+
def url
|
13
|
+
attributes[:url]
|
14
|
+
end
|
15
|
+
|
16
|
+
#
|
17
|
+
# @param [Hash] d
|
18
|
+
#
|
19
|
+
# @return [ContextAttributes]
|
20
|
+
#
|
9
21
|
def self.from_dynamic!(d)
|
10
22
|
d = Types::Hash[d]
|
11
23
|
new(
|
@@ -20,6 +32,37 @@ module Mihari
|
|
20
32
|
attribute :context_attributes, ContextAttributes.optional
|
21
33
|
attribute :metadata, Types::Hash
|
22
34
|
|
35
|
+
#
|
36
|
+
# @return [String]
|
37
|
+
#
|
38
|
+
def type
|
39
|
+
attributes[:type]
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# @return [String]
|
44
|
+
#
|
45
|
+
def id
|
46
|
+
attributes[:id]
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# @return [ContextAttributes, nil]
|
51
|
+
#
|
52
|
+
def context_attributes
|
53
|
+
attributes[:context_attributes]
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# @return [Hash, nil]
|
58
|
+
#
|
59
|
+
def metadata
|
60
|
+
attributes[:metadata]
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
# @return [String, nil]
|
65
|
+
#
|
23
66
|
def value
|
24
67
|
case type
|
25
68
|
when "file"
|
@@ -33,11 +76,25 @@ module Mihari
|
|
33
76
|
end
|
34
77
|
end
|
35
78
|
|
79
|
+
#
|
80
|
+
# @return [Mihari::Artifact]
|
81
|
+
#
|
82
|
+
def to_artifact
|
83
|
+
Artifact.new(data: value, metadata: metadata)
|
84
|
+
end
|
85
|
+
|
86
|
+
#
|
87
|
+
# @param [Hash] d
|
88
|
+
#
|
89
|
+
# @return [Datum]
|
90
|
+
#
|
36
91
|
def self.from_dynamic!(d)
|
37
92
|
d = Types::Hash[d]
|
38
93
|
|
39
94
|
context_attributes = nil
|
40
|
-
|
95
|
+
if d.key?("context_attributes")
|
96
|
+
context_attributes = ContextAttributes.from_dynamic!(d.fetch("context_attributes"))
|
97
|
+
end
|
41
98
|
|
42
99
|
new(
|
43
100
|
type: d.fetch("type"),
|
@@ -51,6 +108,18 @@ module Mihari
|
|
51
108
|
class Meta < Dry::Struct
|
52
109
|
attribute :cursor, Types::String.optional
|
53
110
|
|
111
|
+
#
|
112
|
+
# @return [String, nil]
|
113
|
+
#
|
114
|
+
def cursor
|
115
|
+
attributes[:cursor]
|
116
|
+
end
|
117
|
+
|
118
|
+
#
|
119
|
+
# @param [Hash] d
|
120
|
+
#
|
121
|
+
# @return [Meta]
|
122
|
+
#
|
54
123
|
def self.from_dynamic!(d)
|
55
124
|
d = Types::Hash[d]
|
56
125
|
new(
|
@@ -63,6 +132,32 @@ module Mihari
|
|
63
132
|
attribute :meta, Meta
|
64
133
|
attribute :data, Types.Array(Datum)
|
65
134
|
|
135
|
+
#
|
136
|
+
# @return [Meta]
|
137
|
+
#
|
138
|
+
def meta
|
139
|
+
attributes[:meta]
|
140
|
+
end
|
141
|
+
|
142
|
+
#
|
143
|
+
# @return [Array<Datum>]
|
144
|
+
#
|
145
|
+
def data
|
146
|
+
attributes[:data]
|
147
|
+
end
|
148
|
+
|
149
|
+
#
|
150
|
+
# @return [Array<Mihari::Artifact>]
|
151
|
+
#
|
152
|
+
def to_artifacts
|
153
|
+
data.map(&:to_artifact)
|
154
|
+
end
|
155
|
+
|
156
|
+
#
|
157
|
+
# @param [Hash] d
|
158
|
+
#
|
159
|
+
# @return [Response]
|
160
|
+
#
|
66
161
|
def self.from_dynamic!(d)
|
67
162
|
d = Types::Hash[d]
|
68
163
|
new(
|
data/lib/mihari/version.rb
CHANGED
data/lib/mihari/web/app.rb
CHANGED
@@ -44,7 +44,7 @@ module Mihari
|
|
44
44
|
end.to_app
|
45
45
|
end
|
46
46
|
|
47
|
-
def run!(port: 9292, host: "localhost", threads: "0:5", verbose: false, worker_timeout: 60)
|
47
|
+
def run!(port: 9292, host: "localhost", threads: "0:5", verbose: false, worker_timeout: 60, open: true)
|
48
48
|
url = "http://#{host}:#{port}"
|
49
49
|
|
50
50
|
# set maximum number of threads to use as PARALLEL_PROCESSOR_COUNT (if it is not set)
|
@@ -60,7 +60,7 @@ module Mihari
|
|
60
60
|
Verbose: verbose,
|
61
61
|
worker_timeout: worker_timeout
|
62
62
|
) do |_|
|
63
|
-
Launchy.open(url) if ENV["RACK_ENV"] != "development"
|
63
|
+
Launchy.open(url) if ENV["RACK_ENV"] != "development" && open
|
64
64
|
rescue Launchy::CommandNotFoundError
|
65
65
|
# ref. https://github.com/ninoseki/mihari/issues/477
|
66
66
|
# do nothing
|