mihari 5.2.0 → 5.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -0
  3. data/lib/mihari/analyzers/base.rb +20 -115
  4. data/lib/mihari/analyzers/censys.rb +20 -2
  5. data/lib/mihari/analyzers/onyphe.rb +1 -1
  6. data/lib/mihari/analyzers/rule.rb +116 -60
  7. data/lib/mihari/analyzers/shodan.rb +1 -1
  8. data/lib/mihari/analyzers/urlscan.rb +6 -9
  9. data/lib/mihari/analyzers/virustotal_intelligence.rb +1 -5
  10. data/lib/mihari/cli/main.rb +2 -2
  11. data/lib/mihari/commands/search.rb +69 -0
  12. data/lib/mihari/commands/web.rb +9 -2
  13. data/lib/mihari/mixins/error_notification.rb +0 -2
  14. data/lib/mihari/models/artifact.rb +1 -1
  15. data/lib/mihari/schemas/rule.rb +2 -17
  16. data/lib/mihari/structs/censys.rb +167 -11
  17. data/lib/mihari/structs/config.rb +28 -0
  18. data/lib/mihari/structs/google_public_dns.rb +39 -1
  19. data/lib/mihari/structs/greynoise.rb +93 -6
  20. data/lib/mihari/structs/ipinfo.rb +40 -0
  21. data/lib/mihari/structs/onyphe.rb +88 -6
  22. data/lib/mihari/structs/rule.rb +4 -2
  23. data/lib/mihari/structs/shodan.rb +138 -4
  24. data/lib/mihari/structs/urlscan.rb +98 -1
  25. data/lib/mihari/structs/virustotal_intelligence.rb +96 -1
  26. data/lib/mihari/version.rb +1 -1
  27. data/lib/mihari/web/app.rb +2 -2
  28. data/lib/mihari/web/public/assets/index-cbe1734c.js +50 -0
  29. data/lib/mihari/web/public/assets/index-eed1bcd8.css +5 -0
  30. data/lib/mihari/web/public/index.html +2 -2
  31. data/lib/mihari.rb +1 -0
  32. data/mihari.gemspec +13 -12
  33. metadata +41 -42
  34. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -43
  35. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -15
  36. data/.github/workflows/test.yml +0 -90
  37. data/config/pre_commit.yml +0 -3
  38. data/docker/Dockerfile +0 -14
  39. data/examples/ipinfo_hosted_domains.rb +0 -45
  40. data/images/Tines-Full_Logo-Tines_Black.png +0 -0
  41. data/images/alert.png +0 -0
  42. data/images/logo.png +0 -0
  43. data/images/misp.png +0 -0
  44. data/images/overview.jpg +0 -0
  45. data/images/slack.png +0 -0
  46. data/images/tines.png +0 -0
  47. data/images/web_alerts.png +0 -0
  48. data/images/web_config.png +0 -0
  49. data/lib/mihari/commands/searcher.rb +0 -61
  50. data/lib/mihari/web/public/assets/index-9948ee35.js +0 -50
  51. data/lib/mihari/web/public/assets/index-d88cc3f1.css +0 -5
@@ -25,30 +25,15 @@ module Mihari
25
25
  AnalyzerWithoutAPIKey | AnalyzerWithAPIKey | Censys | CIRCL | PassiveTotal | ZoomEye | Urlscan | Crtsh | Feed
26
26
  end
27
27
 
28
- optional(:emitters).value(:array).each { Database | MISP | TheHive | Slack | Webhook }
28
+ optional(:emitters).value(:array).each { Database | MISP | TheHive | Slack | Webhook }.default(DEFAULT_EMITTERS)
29
29
 
30
- optional(:enrichers).value(:array).each(Enricher)
30
+ optional(:enrichers).value(:array).each(Enricher).default(DEFAULT_ENRICHERS)
31
31
 
32
32
  optional(:data_types).value(array[Types::DataTypes]).default(DEFAULT_DATA_TYPES)
33
33
  optional(:falsepositives).value(array[:string]).default([])
34
34
 
35
35
  optional(:artifact_lifetime).value(:integer)
36
36
  optional(:artifact_ttl).value(:integer)
37
-
38
- before(:key_coercer) do |result|
39
- # it looks like that dry-schema v1.9.1 has an issue with setting an array of schemas as a default value
40
- # e.g. optional(:emitters).value(:array).each { Emitter | HTTP }.default(DEFAULT_EMITTERS) does not work well
41
- # so let's do a dirty hack...
42
- h = result.to_h
43
-
44
- emitters = h[:emitters]
45
- h[:emitters] = emitters || DEFAULT_EMITTERS
46
-
47
- enrichers = h[:enrichers]
48
- h[:enrichers] = enrichers || DEFAULT_ENRICHERS
49
-
50
- h
51
- end
52
37
  end
53
38
 
54
39
  class RuleContract < Dry::Validation::Contract
@@ -8,6 +8,13 @@ module Mihari
8
8
 
9
9
  attribute :asn, Types::Int
10
10
 
11
+ #
12
+ # @return [Integer]
13
+ #
14
+ def asn
15
+ attributes[:asn]
16
+ end
17
+
11
18
  #
12
19
  # @return [Mihari::AutonomousSystem]
13
20
  #
@@ -15,6 +22,11 @@ module Mihari
15
22
  Mihari::AutonomousSystem.new(asn: normalize_asn(asn))
16
23
  end
17
24
 
25
+ #
26
+ # @param [Hash] d
27
+ #
28
+ # @return [AutonomousSystem]
29
+ #
18
30
  def self.from_dynamic!(d)
19
31
  d = Types::Hash[d]
20
32
  new(
@@ -27,6 +39,20 @@ module Mihari
27
39
  attribute :country, Types::String.optional
28
40
  attribute :country_code, Types::String.optional
29
41
 
42
+ #
43
+ # @return [String, nil]
44
+ #
45
+ def country
46
+ attributes[:country]
47
+ end
48
+
49
+ #
50
+ # @return [String, nil]
51
+ #
52
+ def country_code
53
+ attributes[:country_code]
54
+ end
55
+
30
56
  #
31
57
  # @return [Mihari::Geolocation] <description>
32
58
  #
@@ -41,6 +67,11 @@ module Mihari
41
67
  )
42
68
  end
43
69
 
70
+ #
71
+ # @param [Hash] d
72
+ #
73
+ # @return [Location]
74
+ #
44
75
  def self.from_dynamic!(d)
45
76
  d = Types::Hash[d]
46
77
  new(
@@ -53,6 +84,13 @@ module Mihari
53
84
  class Service < Dry::Struct
54
85
  attribute :port, Types::Integer
55
86
 
87
+ #
88
+ # @return [Integer]
89
+ #
90
+ def port
91
+ attributes[:port]
92
+ end
93
+
56
94
  #
57
95
  # @return [Mihari::Port]
58
96
  #
@@ -60,6 +98,11 @@ module Mihari
60
98
  Port.new(port: port)
61
99
  end
62
100
 
101
+ #
102
+ # @param [Hash] d
103
+ #
104
+ # @return [Service]
105
+ #
63
106
  def self.from_dynamic!(d)
64
107
  d = Types::Hash[d]
65
108
  new(
@@ -75,6 +118,41 @@ module Mihari
75
118
  attribute :metadata, Types::Hash
76
119
  attribute :services, Types.Array(Service)
77
120
 
121
+ #
122
+ # @return [String]
123
+ #
124
+ def ip
125
+ attributes[:ip]
126
+ end
127
+
128
+ #
129
+ # @return [Location]
130
+ #
131
+ def location
132
+ attributes[:location]
133
+ end
134
+
135
+ #
136
+ # @return [AutonomousSystem]
137
+ #
138
+ def autonomous_system
139
+ attributes[:autonomous_system]
140
+ end
141
+
142
+ #
143
+ # @return [Hash]
144
+ #
145
+ def metadata
146
+ attributes[:metadata]
147
+ end
148
+
149
+ #
150
+ # @return [Array<Service>]
151
+ #
152
+ def services
153
+ attributes[:services]
154
+ end
155
+
78
156
  #
79
157
  # @return [Array<Mihari::Port>]
80
158
  #
@@ -82,15 +160,12 @@ module Mihari
82
160
  services.map(&:to_port)
83
161
  end
84
162
 
85
- #
86
- # @param [String] source
87
163
  #
88
164
  # @return [Mihari::Artifact]
89
165
  #
90
- def to_artifact(source = "Censys")
166
+ def to_artifact
91
167
  Artifact.new(
92
168
  data: ip,
93
- source: source,
94
169
  metadata: metadata,
95
170
  autonomous_system: autonomous_system.to_as,
96
171
  geolocation: location.to_geolocation,
@@ -98,6 +173,11 @@ module Mihari
98
173
  )
99
174
  end
100
175
 
176
+ #
177
+ # @param [Hash] d
178
+ #
179
+ # @return [Hit]
180
+ #
101
181
  def self.from_dynamic!(d)
102
182
  d = Types::Hash[d]
103
183
  new(
@@ -111,14 +191,33 @@ module Mihari
111
191
  end
112
192
 
113
193
  class Links < Dry::Struct
114
- attribute :next, Types::String
115
- attribute :prev, Types::String
194
+ attribute :next, Types::String.optional
195
+ attribute :prev, Types::String.optional
196
+
197
+ #
198
+ # @return [String, nil]
199
+ #
200
+ def next
201
+ attributes[:next]
202
+ end
116
203
 
204
+ #
205
+ # @return [String, nil]
206
+ #
207
+ def prev
208
+ attributes[:prev]
209
+ end
210
+
211
+ #
212
+ # @param [Hash] d
213
+ #
214
+ # @return [Links]
215
+ #
117
216
  def self.from_dynamic!(d)
118
217
  d = Types::Hash[d]
119
218
  new(
120
- next: d.fetch("next"),
121
- prev: d.fetch("prev")
219
+ next: d["next"],
220
+ prev: d["prev"]
122
221
  )
123
222
  end
124
223
  end
@@ -130,14 +229,45 @@ module Mihari
130
229
  attribute :links, Links
131
230
 
132
231
  #
133
- # @param [String] source
232
+ # @return [String]
233
+ #
234
+ def query
235
+ attributes[:query]
236
+ end
237
+
238
+ #
239
+ # @return [Integer]
240
+ #
241
+ def total
242
+ attributes[:total]
243
+ end
244
+
245
+ #
246
+ # @return [Array<Hit>]
247
+ #
248
+ def hits
249
+ attributes[:hits]
250
+ end
251
+
252
+ #
253
+ # @return [Links]
254
+ #
255
+ def links
256
+ attributes[:links]
257
+ end
258
+
134
259
  #
135
260
  # @return [Array<Mihari::Artifact>]
136
261
  #
137
- def to_artifacts(source = "Censys")
138
- hits.map { |hit| hit.to_artifact(source) }
262
+ def to_artifacts
263
+ hits.map { |hit| hit.to_artifact }
139
264
  end
140
265
 
266
+ #
267
+ # @param [Hash] d
268
+ #
269
+ # @return [Result]
270
+ #
141
271
  def self.from_dynamic!(d)
142
272
  d = Types::Hash[d]
143
273
  new(
@@ -154,6 +284,32 @@ module Mihari
154
284
  attribute :status, Types::String
155
285
  attribute :result, Result
156
286
 
287
+ #
288
+ # @return [Integer]
289
+ #
290
+ def code
291
+ attributes[:code]
292
+ end
293
+
294
+ #
295
+ # @return [String]
296
+ #
297
+ def status
298
+ attributes[:status]
299
+ end
300
+
301
+ #
302
+ # @return [Result]
303
+ #
304
+ def result
305
+ attributes[:result]
306
+ end
307
+
308
+ #
309
+ # @param [Hash] d
310
+ #
311
+ # @return [Response]
312
+ #
157
313
  def self.from_dynamic!(d)
158
314
  d = Types::Hash[d]
159
315
  new(
@@ -8,6 +8,34 @@ module Mihari
8
8
  attribute :is_configured, Types::Bool
9
9
  attribute :values, Types.Array(Types::Hash).optional
10
10
 
11
+ #
12
+ # @return [String]
13
+ #
14
+ def name
15
+ attributes[:name]
16
+ end
17
+
18
+ #
19
+ # @return [String]
20
+ #
21
+ def type
22
+ attributes[:type]
23
+ end
24
+
25
+ #
26
+ # @return [Boolean]
27
+ #
28
+ def is_configured
29
+ attributes[:is_configured]
30
+ end
31
+
32
+ #
33
+ # @return [Array<Hash>]
34
+ #
35
+ def values
36
+ attributes[:values]
37
+ end
38
+
11
39
  #
12
40
  # @param [Class<Mihari::Analyzers::Base>, Class<Mihari::Emitters::Base>] klass
13
41
  #
@@ -9,13 +9,39 @@ module Mihari
9
9
  5 => "CNAME",
10
10
  16 => "TXT",
11
11
  28 => "AAAA"
12
- }
12
+ }.freeze
13
13
 
14
14
  class Answer < Dry::Struct
15
15
  attribute :name, Types::String
16
16
  attribute :data, Types::String
17
17
  attribute :resource_type, Types::String
18
18
 
19
+ #
20
+ # @return [String]
21
+ #
22
+ def name
23
+ attributes[:name]
24
+ end
25
+
26
+ #
27
+ # @return [String]
28
+ #
29
+ def data
30
+ attributes[:data]
31
+ end
32
+
33
+ #
34
+ # @return [String]
35
+ #
36
+ def resource_type
37
+ attributes[:resource_type]
38
+ end
39
+
40
+ #
41
+ # @param [Hash] d
42
+ #
43
+ # @return [Answer]
44
+ #
19
45
  def self.from_dynamic!(d)
20
46
  d = Types::Hash[d]
21
47
  resource_type = INT_TYPE_TO_TYPE[d.fetch("type")]
@@ -30,6 +56,18 @@ module Mihari
30
56
  class Response < Dry::Struct
31
57
  attribute :answers, Types.Array(Answer)
32
58
 
59
+ #
60
+ # @return [Array<Answer>]
61
+ #
62
+ def answers
63
+ attributes[:answers]
64
+ end
65
+
66
+ #
67
+ # @param [Hash] d
68
+ #
69
+ # @return [Response]
70
+ #
33
71
  def self.from_dynamic!(d)
34
72
  d = Types::Hash[d]
35
73
  new(
@@ -10,6 +10,27 @@ module Mihari
10
10
  attribute :country_code, Types::String
11
11
  attribute :asn, Types::String
12
12
 
13
+ #
14
+ # @return [String]
15
+ #
16
+ def country
17
+ attributes[:country]
18
+ end
19
+
20
+ #
21
+ # @return [String]
22
+ #
23
+ def country_code
24
+ attributes[:country_code]
25
+ end
26
+
27
+ #
28
+ # @return [String]
29
+ #
30
+ def asn
31
+ attributes[:asn]
32
+ end
33
+
13
34
  #
14
35
  # @return [Mihari::AutonomousSystem]
15
36
  #
@@ -27,6 +48,11 @@ module Mihari
27
48
  )
28
49
  end
29
50
 
51
+ #
52
+ # @param [Hash] d
53
+ #
54
+ # @return [Metadata]
55
+ #
30
56
  def self.from_dynamic!(d)
31
57
  d = Types::Hash[d]
32
58
  new(
@@ -43,20 +69,43 @@ module Mihari
43
69
  attribute :metadata_, Types::Hash
44
70
 
45
71
  #
46
- # @param [String] source
72
+ # @return [String]
73
+ #
74
+ def ip
75
+ attributes[:ip]
76
+ end
77
+
78
+ #
79
+ # @return [Metadata]
80
+ #
81
+ def metadata
82
+ attributes[:metadata]
83
+ end
84
+
85
+ #
86
+ # @return [Hash]
87
+ #
88
+ def metadata_
89
+ attributes[:metadata_]
90
+ end
91
+
47
92
  #
48
93
  # @return [Mihari::Artifact]
49
94
  #
50
- def to_artifact(source = "GreyNoise")
95
+ def to_artifact
51
96
  Mihari::Artifact.new(
52
97
  data: ip,
53
- source: source,
54
98
  metadata: metadata_,
55
99
  autonomous_system: metadata.to_as,
56
100
  geolocation: metadata.to_geolocation
57
101
  )
58
102
  end
59
103
 
104
+ #
105
+ # @param [Hash] d
106
+ #
107
+ # @return [Datum]
108
+ #
60
109
  def self.from_dynamic!(d)
61
110
  d = Types::Hash[d]
62
111
  new(
@@ -75,14 +124,52 @@ module Mihari
75
124
  attribute :query, Types::String
76
125
 
77
126
  #
78
- # @param [String] source
127
+ # @return [Boolean]
128
+ #
129
+ def complete
130
+ attributes[:complete]
131
+ end
132
+
133
+ #
134
+ # @return [Integer]
135
+ #
136
+ def count
137
+ attributes[:count]
138
+ end
139
+
140
+ #
141
+ # @return [Array<Datum>]
142
+ #
143
+ def data
144
+ attributes[:data]
145
+ end
146
+
147
+ #
148
+ # @return [String]
149
+ #
150
+ def message
151
+ attributes[:message]
152
+ end
153
+
154
+ #
155
+ # @return [String]
156
+ #
157
+ def query
158
+ attributes[:query]
159
+ end
160
+
79
161
  #
80
162
  # @return [Array<Mihari::Artifact>]
81
163
  #
82
- def to_artifacts(source = "GreyNoise")
83
- data.map { |datum| datum.to_artifact(source) }
164
+ def to_artifacts
165
+ data.map { |datum| datum.to_artifact }
84
166
  end
85
167
 
168
+ #
169
+ # @param [Hash] d
170
+ #
171
+ # @return [Response]
172
+ #
86
173
  def self.from_dynamic!(d)
87
174
  d = Types::Hash[d]
88
175
  new(
@@ -10,9 +10,49 @@ module Mihari
10
10
  attribute :country_code, Types::String.optional
11
11
  attribute :asn, Types::Integer.optional
12
12
 
13
+ #
14
+ # @return [String]
15
+ #
16
+ def ip
17
+ attributes[:ip]
18
+ end
19
+
20
+ #
21
+ # @return [String, nil]
22
+ #
23
+ def hostname
24
+ attributes[:hostname]
25
+ end
26
+
27
+ #
28
+ # @return [String, nil]
29
+ #
30
+ def loc
31
+ attributes[:loc]
32
+ end
33
+
34
+ #
35
+ # @return [String, nil]
36
+ #
37
+ def country_code
38
+ attributes[:country_code]
39
+ end
40
+
41
+ #
42
+ # @return [Integer, nil]
43
+ #
44
+ def asn
45
+ attributes[:asn]
46
+ end
47
+
13
48
  class << self
14
49
  include Mixins::AutonomousSystem
15
50
 
51
+ #
52
+ # @param [Hash] d
53
+ #
54
+ # @return [Response]
55
+ #
16
56
  def from_dynamic!(d)
17
57
  d = d.deep_stringify_keys
18
58
  d = Types::Hash[d]
@@ -12,14 +12,39 @@ module Mihari
12
12
  attribute :metadata, Types::Hash
13
13
 
14
14
  #
15
- # @param [String] source
15
+ # @return [String]
16
+ #
17
+ def asn
18
+ attributes[:asn]
19
+ end
20
+
21
+ #
22
+ # @return [String, nil]
23
+ #
24
+ def country_code
25
+ attributes[:country_code]
26
+ end
27
+
28
+ #
29
+ # @return [String]
30
+ #
31
+ def ip
32
+ attributes[:ip]
33
+ end
34
+
35
+ #
36
+ # @return [Hash]
37
+ #
38
+ def metadata
39
+ attributes[:metadata]
40
+ end
41
+
16
42
  #
17
43
  # @return [Mihari::Artifact]
18
44
  #
19
- def to_artifact(source = "Onyphe")
45
+ def to_artifact
20
46
  Mihari::Artifact.new(
21
47
  data: ip,
22
- source: source,
23
48
  metadata: metadata,
24
49
  autonomous_system: to_as,
25
50
  geolocation: to_geolocation
@@ -45,6 +70,11 @@ module Mihari
45
70
  Mihari::AutonomousSystem.new(asn: normalize_asn(asn))
46
71
  end
47
72
 
73
+ #
74
+ # @param [Hash] d
75
+ #
76
+ # @return [Result]
77
+ #
48
78
  def self.from_dynamic!(d)
49
79
  d = Types::Hash[d]
50
80
  new(
@@ -67,14 +97,66 @@ module Mihari
67
97
  attribute :total, Types::Int
68
98
 
69
99
  #
70
- # @param [String] source
100
+ # @return [Integer]
101
+ #
102
+ def count
103
+ attributes[:count]
104
+ end
105
+
106
+ #
107
+ # @return [Integer]
108
+ #
109
+ def error
110
+ attributes[:error]
111
+ end
112
+
113
+ #
114
+ # @return [Integer]
115
+ #
116
+ def max_page
117
+ attributes[:max_page]
118
+ end
119
+
120
+ #
121
+ # @return [Integer]
122
+ #
123
+ def page
124
+ attributes[:page]
125
+ end
126
+
127
+ #
128
+ # @return [Array<Result>]
129
+ #
130
+ def results
131
+ attributes[:results]
132
+ end
133
+
134
+ #
135
+ # @return [String]
136
+ #
137
+ def status
138
+ attributes[:status]
139
+ end
140
+
141
+ #
142
+ # @return [Integer]
143
+ #
144
+ def total
145
+ attributes[:total]
146
+ end
147
+
71
148
  #
72
149
  # @return [Array<Mihari::Artifact>]
73
150
  #
74
- def to_artifacts(source = "Onyphe")
75
- results.map { |result| result.to_artifact(source) }
151
+ def to_artifacts
152
+ results.map(&:to_artifact)
76
153
  end
77
154
 
155
+ #
156
+ # @param [Hash] d
157
+ #
158
+ # @return [Response]
159
+ #
78
160
  def self.from_dynamic!(d)
79
161
  d = Types::Hash[d]
80
162
  new(