mihari 5.2.1 → 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.
Files changed (44) 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/mixins/error_notification.rb +0 -2
  13. data/lib/mihari/models/artifact.rb +1 -1
  14. data/lib/mihari/schemas/rule.rb +2 -17
  15. data/lib/mihari/structs/censys.rb +167 -11
  16. data/lib/mihari/structs/config.rb +28 -0
  17. data/lib/mihari/structs/google_public_dns.rb +39 -1
  18. data/lib/mihari/structs/greynoise.rb +93 -6
  19. data/lib/mihari/structs/ipinfo.rb +40 -0
  20. data/lib/mihari/structs/onyphe.rb +88 -6
  21. data/lib/mihari/structs/rule.rb +4 -2
  22. data/lib/mihari/structs/shodan.rb +138 -4
  23. data/lib/mihari/structs/urlscan.rb +98 -1
  24. data/lib/mihari/structs/virustotal_intelligence.rb +96 -1
  25. data/lib/mihari/version.rb +1 -1
  26. data/lib/mihari.rb +1 -0
  27. data/mihari.gemspec +8 -7
  28. metadata +29 -30
  29. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -43
  30. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -15
  31. data/.github/workflows/test.yml +0 -90
  32. data/config/pre_commit.yml +0 -3
  33. data/docker/Dockerfile +0 -14
  34. data/examples/ipinfo_hosted_domains.rb +0 -45
  35. data/images/Tines-Full_Logo-Tines_Black.png +0 -0
  36. data/images/alert.png +0 -0
  37. data/images/logo.png +0 -0
  38. data/images/misp.png +0 -0
  39. data/images/overview.jpg +0 -0
  40. data/images/slack.png +0 -0
  41. data/images/tines.png +0 -0
  42. data/images/web_alerts.png +0 -0
  43. data/images/web_config.png +0 -0
  44. data/lib/mihari/commands/searcher.rb +0 -61
@@ -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(
@@ -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
  #