prenus 0.0.3 → 0.0.4

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.
@@ -0,0 +1,394 @@
1
+ require 'lib/gemcache/ruby-nessus/ruby-nessus/Version1/host'
2
+ require 'lib/gemcache/ruby-nessus/ruby-nessus/Version1/event'
3
+
4
+ module Nessus
5
+
6
+ # .Nessus Version 2 Schema
7
+ module Version1
8
+
9
+ # File to parse
10
+ attr_reader :file
11
+
12
+ class XML
13
+
14
+ include Enumerable
15
+
16
+ #
17
+ # Creates a new .Nessus (XML) object to be parser
18
+ #
19
+ # @param [String] file The Nessus xml results file to parse.
20
+ #
21
+ # @yield [prog] If a block is given, it will be passed the newly
22
+ # created XML object.
23
+ #
24
+ # @yieldparam [XML] prog The newly created XML object.
25
+ #
26
+ # @example
27
+ # Nessus::XML.new(nessus_scan_file) do |scan|
28
+ # scan.report_name
29
+ # end
30
+ #
31
+ def initialize(xml)
32
+ @xml = xml
33
+ raise "Error: Not A Version 1.0 .Nessus file." unless @xml.at('NessusClientData')
34
+ end
35
+
36
+ def version
37
+ 1
38
+ end
39
+
40
+ #
41
+ # Return the nessus report title.
42
+ #
43
+ # @return [String]
44
+ # The Nessus Report Title
45
+ #
46
+ # @example
47
+ # scan.report_name #=> "My Super Cool Nessus Report"
48
+ #
49
+ def title
50
+ @report_name ||= @xml.xpath("//NessusClientData//Report//ReportName").inner_text.split(' - ').last
51
+ end
52
+
53
+ #
54
+ # Return the nessus report time.
55
+ #
56
+ # @return [String]
57
+ # The Nessus Report Time
58
+ #
59
+ # @example
60
+ # scan.report_time #=> "09/11/08 02:21:22 AM"
61
+ #
62
+ def time
63
+ datetime = @xml.xpath("//NessusClientData//Report//ReportName").inner_text.split(' - ').first
64
+ @report_time ||= DateTime.strptime(datetime, fmt='%y/%m/%d %I:%M:%S %p')
65
+ end
66
+
67
+ #
68
+ # Return the scan start time.
69
+ #
70
+ # @return [DateTime]
71
+ # The Nessus Scan Start Time
72
+ #
73
+ # @example
74
+ # scan.start_time #=> 'Fri Nov 11 23:36:54 1985'
75
+ #
76
+ def start_time
77
+ @start_time = DateTime.strptime(@xml.xpath("//NessusClientData//Report//StartTime").inner_text, fmt='%a %b %d %H:%M:%S %Y')
78
+ end
79
+
80
+ #
81
+ # Return the scan stop time.
82
+ #
83
+ # @return [DateTime]
84
+ # The Nessus Scan Stop Time
85
+ #
86
+ # @example
87
+ # scan.stop_time #=> 'Mon Nov 11 23:36:54 1985'
88
+ #
89
+ def stop_time
90
+ @stop_time = DateTime.strptime(@xml.xpath("//NessusClientData//Report//StopTime").inner_text, fmt='%a %b %d %H:%M:%S %Y')
91
+ end
92
+
93
+ #
94
+ # Return the scan run time.
95
+ #
96
+ # @return [String]
97
+ # The Nessus Scan Run Time
98
+ #
99
+ # @example
100
+ # scan.runtime #=> '2 hours 5 minutes and 16 seconds'
101
+ #
102
+ def runtime
103
+ h = ("#{Time.parse(stop_time.to_s).strftime('%H').to_i - Time.parse(start_time.to_s).strftime('%H').to_i}").gsub('-', '')
104
+ m = ("#{Time.parse(stop_time.to_s).strftime('%M').to_i - Time.parse(start_time.to_s).strftime('%M').to_i}").gsub('-', '')
105
+ s = ("#{Time.parse(stop_time.to_s).strftime('%S').to_i - Time.parse(start_time.to_s).strftime('%S').to_i}").gsub('-', '')
106
+ return "#{h} hours #{m} minutes and #{s} seconds"
107
+ end
108
+
109
+ #
110
+ # Return the nessus scan policy name. When creating a nessus policy this is usually the title field.
111
+ #
112
+ # @return [String]
113
+ # The Nessus Scan Policy Name
114
+ #
115
+ def policy_title
116
+ @policy_name ||= @xml.xpath("//NessusClientData//Report//policyName").inner_text
117
+ end
118
+
119
+ #
120
+ # Return the nessus scan policy comments. This is the description field when creating a new policy with the Nessus GUI client.
121
+ #
122
+ # @return [String]
123
+ # The Nessus Scan Policy Comments
124
+ #
125
+ def policy_notes
126
+ @policy_comments ||= @xml.xpath("//NessusClientData//Report//policyComments").inner_text
127
+ end
128
+
129
+ #
130
+ # Returns and array of the plugin ids userd for the passed .nessus scan.
131
+ #
132
+ # @return [Array]
133
+ # The Nessus Scan Plugin Ids
134
+ #
135
+ # @example
136
+ # scan.plugin_ids #=> [1234,2343,9742,5452,5343,2423,1233]
137
+ #
138
+ def plugin_ids
139
+ unless @plugin_ids
140
+ @plugin_ids = []
141
+
142
+ @xml.xpath("//PluginSelection").last.text.split(';').each do |id|
143
+ @plugin_ids << id
144
+ end
145
+ end
146
+
147
+ @plugin_ids
148
+ end
149
+
150
+ #
151
+ # Returns and array of the plugin names userd for the passed .nessus scan.
152
+ #
153
+ # @return [Array]
154
+ # The Nessus Scan Plugin Names
155
+ #
156
+ # @example
157
+ # scan.plugins #=> ["PHP < 5.2.1 Multiple Vulnerabilities", "PHP < 4.4.1 / 5.0.6 Multiple Vulnerabilities"]
158
+ #
159
+ def plugins
160
+ unless @plugins
161
+ # get elements with attribute:
162
+ @plugins = []
163
+
164
+ @xml.xpath("//pluginName").each do |x|
165
+ @plugins << x.inner_text unless x.inner_text.empty?
166
+ end
167
+
168
+ @plugins.uniq!
169
+ @plugins.sort!
170
+ end
171
+
172
+ return @plugins
173
+ end
174
+
175
+ #
176
+ # Creates a new Host object to be parser
177
+ #
178
+ # @yield [prog] If a block is given, it will be passed the newly
179
+ # created Host object.
180
+ #
181
+ # @yieldparam [XML] prog The newly created Host object.
182
+ #
183
+ # @example
184
+ # scan.hosts do |host|
185
+ # puts host.hostname
186
+ # end
187
+ #
188
+ def each_host(&block)
189
+ hosts = []
190
+ @xml.xpath("//ReportHost").each do |host|
191
+ hosts << host.at('HostName').inner_text if host.at('HostName').inner_text
192
+ block.call(Host.new(host)) if block
193
+ end
194
+ hosts
195
+ end
196
+
197
+ #
198
+ # Parses the hosts of the scan.
199
+ #
200
+ # @return [Array<String>]
201
+ # The Hosts of the scan.
202
+ #
203
+ def hosts
204
+ Enumerator.new(self,:each_host).to_a
205
+ end
206
+
207
+ #
208
+ # Return the nessus scan host count.
209
+ #
210
+ # @return [Integer]
211
+ # The Nessus Scan Host Count
212
+ #
213
+ # @example
214
+ # scan.host_count #=> 23
215
+ #
216
+ def host_count
217
+ hosts.size
218
+ end
219
+
220
+ #
221
+ # Retunrs an array of all unique ports.
222
+ #
223
+ # @return [Array]
224
+ #
225
+ # @example
226
+ # scan.unique_ports #=> 234
227
+ #
228
+ def unique_ports
229
+ unless @unique_ports
230
+ @unique_ports = []
231
+ @xml.xpath("//ReportItem//port").each do |port|
232
+ @unique_ports << port.inner_text
233
+ end
234
+ @unique_ports.uniq!
235
+ @unique_ports.sort!
236
+ end
237
+ end
238
+
239
+ #
240
+ # Return the informational severity count.
241
+ #
242
+ # @return [Integer]
243
+ # The Informational Severity Count
244
+ #
245
+ # @example
246
+ # scan.informational_severity_count #=> 1203
247
+ #
248
+ def open_ports_count
249
+ count_severity[:open_ports].to_i
250
+ end
251
+
252
+ #
253
+ # Return the High severity count.
254
+ #
255
+ # @return [Integer]
256
+ # The High Severity Count
257
+ #
258
+ # @example
259
+ # scan.high_severity_count #=> 10
260
+ #
261
+ def high_severity_count
262
+ count_severity[:high].to_i
263
+ end
264
+
265
+ #
266
+ # Return the Medium severity count.
267
+ #
268
+ # @return [Integer]
269
+ # The Medium Severity Count
270
+ #
271
+ # @example
272
+ # scan.medium_severity_count #=> 234
273
+ #
274
+ def medium_severity_count
275
+ count_severity[:medium].to_i
276
+ end
277
+
278
+ #
279
+ # Return the Low severity count.
280
+ #
281
+ # @return [Integer]
282
+ # The Low Severity Count
283
+ #
284
+ # @example
285
+ # scan.low_severity_count #=> 114
286
+ #
287
+ def low_severity_count
288
+ count_severity[:low].to_i
289
+ end
290
+
291
+ #
292
+ # Return the Total severity count. [high, medium, low, informational]
293
+ #
294
+ # @return [Integer]
295
+ # The Total Severity Count
296
+ #
297
+ # @example
298
+ # scan.total_event_count #=> 1561
299
+ #
300
+ def total_event_count
301
+ count_severity[:all].to_i
302
+ end
303
+
304
+ #
305
+ # Return the Total severity count.
306
+ #
307
+ # @param [String] severity the severity in which to calculate percentage for.
308
+ #
309
+ # @param [Boolean] round round the result to the nearest whole number.
310
+ #
311
+ # @raise [ExceptionClass] One of the following severity options must be passed. [high, medium, low, informational, all]
312
+ #
313
+ # @return [Integer]
314
+ # The Percentage Of Events For A Passed Severity
315
+ #
316
+ # @example
317
+ # scan.event_percentage_for("low", true) #=> 11%
318
+ #
319
+ def event_percentage_for(type, round_percentage=false)
320
+ @sc ||= count_severity
321
+ if %W(high medium low all).include?(type)
322
+ calc = ((@sc[:"#{type}"].to_f / @sc[:all].to_f) * 100)
323
+ if round_percentage
324
+ return "#{calc.round}"
325
+ else
326
+ return "#{calc}"
327
+ end
328
+ else
329
+ raise "Error: #{type} is not an acceptable severity. Possible options include: all, high, medium, low and informational."
330
+ end
331
+ end
332
+
333
+ #
334
+ # Creates a new Host object to be parser from a passed search param.
335
+ #
336
+ # @param [String] hostname the hostname to build a Host object for.
337
+ #
338
+ # @yield [prog] If a block is given, it will be passed the newly
339
+ # created Host object.
340
+ #
341
+ # @yieldparam [XML] prog The newly created Host object.
342
+ #
343
+ # @example
344
+ # scan.find_by_hostname('127.0.0.1') do |host|
345
+ # puts host.hostname
346
+ # end
347
+ #
348
+ def find_by_hostname(hostname, &block)
349
+ raise "Error: hostname can't be blank." if hostname.blank?
350
+ @xml.xpath('//ReportHost[HostName]').each do |host|
351
+ next unless host.inner_text.match(hostname)
352
+ block.call(Host.new(host)) if block
353
+ end
354
+ end
355
+
356
+ private
357
+
358
+ #
359
+ # Calculates an event hash of totals for severity counts.
360
+ #
361
+ # @return [hash]
362
+ # The Event Totals For Severity
363
+ #
364
+ def count_severity
365
+ unless @count
366
+ @count = {}
367
+ @open_ports = 0
368
+ @low = 0
369
+ @medium = 0
370
+ @high = 0
371
+
372
+ @xml.xpath("//ReportHost").each do |s|
373
+ @open_ports += s.at('num_ports').inner_text.to_i
374
+ @low += s.at('num_lo').inner_text.to_i
375
+ @medium += s.at('num_med').inner_text.to_i
376
+ @high += s.at('num_hi').inner_text.to_i
377
+ end
378
+
379
+ @count = { :open_ports => @open_ports,
380
+ :low => @low,
381
+ :medium => @medium,
382
+ :high => @high,
383
+ :all => (@low + @medium + @high) }
384
+ end
385
+
386
+ return @count
387
+ end
388
+
389
+ end
390
+
391
+
392
+ end
393
+
394
+ end
@@ -0,0 +1,341 @@
1
+ require 'lib/gemcache/ruby-nessus/ruby-nessus/Version2/port'
2
+
3
+ module Nessus
4
+ module Version2
5
+
6
+ class Event
7
+
8
+ def initialize(event)
9
+ @event = event
10
+ end
11
+
12
+ #
13
+ # Return the event port.
14
+ #
15
+ # @return [Object]
16
+ # Return the event port object or port string.
17
+ #
18
+ # @example
19
+ # event.port #=> "https (443/tcp)"
20
+ # event.port.number #=> 443
21
+ # event.port.service #=> "https"
22
+ # event.port.protocol #=> "tcp"
23
+ #
24
+ def port
25
+ @port ||= Port.new(@event.at('@port'), @event.at('@svc_name'), @event.at('@protocol'))
26
+ end
27
+
28
+ #
29
+ # Return the event severity.
30
+ #
31
+ # @return [String]
32
+ # Return the event severity.
33
+ #
34
+ # @example
35
+ # event.severity #=> 3
36
+ # event.severity.in_words #=> "High Severity"
37
+ #
38
+ # @see String#in_words
39
+ #
40
+ def severity
41
+ @severity ||= @event.at('@severity').inner_text.to_i
42
+ end
43
+
44
+ #
45
+ # Return true if event is of informational severity.
46
+ #
47
+ # @return [Boolean]
48
+ # Return true if the event is informational.
49
+ #
50
+ def informational?
51
+ severity == 0
52
+ end
53
+
54
+ #
55
+ # Return ture if the event is of low severity.
56
+ #
57
+ # @return [Boolean]
58
+ # Return true if the event is low severity.
59
+ #
60
+ def low?
61
+ severity == 1
62
+ end
63
+
64
+ #
65
+ # Return ture if the event is of medium severity.
66
+ #
67
+ # @return [Boolean]
68
+ # Return true if the event is medium severity.
69
+ #
70
+ def medium?
71
+ severity == 2
72
+ end
73
+
74
+ #
75
+ # Return ture if the event is of high severity.
76
+ #
77
+ # @return [Boolean]
78
+ # Return true if the event is high severity.
79
+ #
80
+ def high?
81
+ severity == 3
82
+ end
83
+
84
+ #
85
+ # Return true if the event is of critical severity.
86
+ #
87
+ # @return [Boolean]
88
+ # Return true if the event is high severity.
89
+ #
90
+ def critical?
91
+ severity == 4
92
+ end
93
+
94
+ #
95
+ # Return the event object nessus plugin id
96
+ #
97
+ # @return [String]
98
+ # Return the event object nessus plugin id
99
+ #
100
+ # @example
101
+ # event.plugin_id #=> 3245
102
+ #
103
+ def id
104
+ @plugin_id ||= @event.at('@pluginID').inner_text.to_i
105
+ end
106
+ alias plugin_id id
107
+
108
+ #
109
+ # Return the event object plugin family name.
110
+ #
111
+ # @return [String]
112
+ # Return the event object plugin family name.
113
+ #
114
+ # @example
115
+ # event.family #=> "Service detection"
116
+ #
117
+ def family
118
+ @plugin_family ||= @event.at('@pluginFamily').inner_text
119
+ end
120
+ alias plugin_family family
121
+
122
+ #
123
+ # Return the event name (plugin_name)
124
+ #
125
+ # @return [String, false]
126
+ # Return the event name (plugin_name)
127
+ #
128
+ # @example
129
+ # event.plugin_name #=> "PHP < 5.2.4 Multiple Vulnerabilities"
130
+ # event.name #=> "PHP < 5.2.4 Multiple Vulnerabilities"
131
+ #
132
+ def plugin_name
133
+ s = @event.at('@pluginName').inner_text
134
+
135
+ @plugin_name ||= if s.empty?
136
+ false
137
+ else
138
+ @event.at('@pluginName').inner_text
139
+ end
140
+
141
+ return @plugin_name
142
+ end
143
+ alias name plugin_name
144
+
145
+ #
146
+ # Return the event synopsis.
147
+ #
148
+ # @return [String, false]
149
+ # Return the event synopsis.
150
+ #
151
+ def synopsis
152
+ @synopsis ||= if @event.at('synopsis')
153
+ @event.at('synopsis').inner_text
154
+ else
155
+ false
156
+ end
157
+ end
158
+
159
+ #
160
+ # Return the event description.
161
+ #
162
+ # @return [String, false]
163
+ # Return the event description.
164
+ #
165
+ def description
166
+ @description ||= if @event.at('description')
167
+ @event.at('description').inner_text
168
+ else
169
+ false
170
+ end
171
+ end
172
+
173
+ #
174
+ # Return the event solution.
175
+ #
176
+ # @return [String, false]
177
+ # Return the event solution.
178
+ #
179
+ def solution
180
+ @solution ||= if @event.at('solution')
181
+ @event.at('solution').inner_text
182
+ else
183
+ false
184
+ end
185
+ end
186
+
187
+ #
188
+ # Return the event risk.
189
+ #
190
+ # @return [String, false]
191
+ # Return the event risk.
192
+ #
193
+ def risk
194
+ @risk_factor ||= if @event.at('risk_factor')
195
+ @event.at('risk_factor').inner_text
196
+ else
197
+ false
198
+ end
199
+ end
200
+
201
+ #
202
+ # Return the event plugin output.
203
+ #
204
+ # @return [String, false]
205
+ # Return the event plugin output.
206
+ #
207
+ def output
208
+ @plugin_output ||= if @event.at('plugin_output')
209
+ @event.at('plugin_output').inner_text
210
+ else
211
+ false
212
+ end
213
+ end
214
+ alias data output
215
+ alias plugin_output output
216
+
217
+ #
218
+ # Return the event plugin version.
219
+ #
220
+ # @return [String, false]
221
+ # Return the event plugin version.
222
+ #
223
+ def version
224
+ @plugin_version ||= if @event.at('plugin_version')
225
+ @event.at('plugin_version').inner_text
226
+ else
227
+ false
228
+ end
229
+ end
230
+ alias plugin_version version
231
+
232
+ #
233
+ # Return the event reference links.
234
+ #
235
+ # @return [String, false]
236
+ # Return the event reference links.
237
+ #
238
+ def see_also
239
+ unless @see_also
240
+ @see_also = []
241
+ @event.xpath("see_also").each do |see_also|
242
+ @see_also << see_also.inner_text
243
+ end
244
+ end
245
+ @see_also
246
+ end
247
+ alias links see_also
248
+ alias more see_also
249
+ alias references see_also
250
+
251
+ #
252
+ # Return the event patch publication date.
253
+ #
254
+ # @return [String, false]
255
+ # Return the event patch publication date.
256
+ #
257
+ def patch_publication_date
258
+ @patch_publication_date ||= if @event.at('patch_publication_date')
259
+ DateTime.strptime(@event.at('patch_publication_date').inner_text, fmt='%Y/%m/%d')
260
+ else
261
+ false
262
+ end
263
+ end
264
+
265
+ #
266
+ # Return the event cvss base score.
267
+ #
268
+ # @return [String, false]
269
+ # Return the event cvss base score.
270
+ #
271
+ def cvss_base_score
272
+ @cvss_base_score ||= if @event.at('cvss_base_score')
273
+ @event.at('cvss_base_score').inner_text.to_f
274
+ else
275
+ false
276
+ end
277
+ end
278
+
279
+ #
280
+ # Return the event cve.
281
+ #
282
+ # @return [String, false]
283
+ # Return the event cvss base score.
284
+ #
285
+ def cve
286
+ @cve ||= if @event.at('cve')
287
+ @event.at('cve').inner_text
288
+ else
289
+ false
290
+ end
291
+ end
292
+
293
+ #
294
+ # Return the event bid.
295
+ #
296
+ # @return [String, false]
297
+ # Return the event bid.
298
+ #
299
+ def bid
300
+ @bid ||= if @event.at('bid')
301
+ @event.at('bid').inner_text.to_i
302
+ else
303
+ false
304
+ end
305
+ end
306
+
307
+ #
308
+ # Return other event related references.
309
+ #
310
+ # @return [String, false]
311
+ # Return the event related references.
312
+ #
313
+ def xref
314
+ unless @xref
315
+ @xref = []
316
+ @event.xpath("xref").each do |xref|
317
+ @xref << xref.inner_text
318
+ end
319
+ end
320
+ @xref
321
+ end
322
+
323
+ #
324
+ # Return other event cvss vector.
325
+ #
326
+ # @return [String, false]
327
+ # Return the event cvss vector.
328
+ #
329
+ def cvss_vector
330
+ @cvss_vector ||= if @event.at('cvss_vector')
331
+ @event.at('cvss_vector').inner_text
332
+ else
333
+ false
334
+ end
335
+ end
336
+
337
+ end
338
+
339
+ end
340
+
341
+ end