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
@@ -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(source = "Shodan")
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
- context_attributes = ContextAttributes.from_dynamic!(d.fetch("context_attributes")) if d.key?("context_attributes")
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(
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- VERSION = "5.2.0"
4
+ VERSION = "5.2.2"
5
5
  end
@@ -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