ruby-nessus 0.1.4 → 1.0.0

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,333 @@
1
+ require 'ruby-nessus/Version2/port'
2
+
3
+ module Nessus
4
+ module Version2
5
+
6
+ class Event
7
+ # Event
8
+ attr_reader :event
9
+
10
+ def initialize(event)
11
+ @event = event
12
+ end
13
+
14
+ #
15
+ # Return the event port.
16
+ #
17
+ # @return [Object]
18
+ # Return the event port object or port string.
19
+ #
20
+ # @example
21
+ # event.port #=> "https (443/tcp)"
22
+ # event.port.number #=> 443
23
+ # event.port.service #=> "https"
24
+ # event.port.protocol #=> "tcp"
25
+ #
26
+ def port
27
+ @port ||= Port.new(@event.at('@port'), @event.at('@svc_name'), @event.at('@protocol'))
28
+ end
29
+
30
+ #
31
+ # Return the event severity.
32
+ #
33
+ # @return [String]
34
+ # Return the event severity.
35
+ #
36
+ # @example
37
+ # event.severity #=> 3
38
+ # event.severity.in_words #=> "High Severity"
39
+ #
40
+ # @see String#in_words
41
+ #
42
+ def severity
43
+ @severity ||= @event.at('@severity').inner_text.to_i
44
+ end
45
+
46
+ #
47
+ # Return true if event is of informational severity.
48
+ #
49
+ # @return [Boolean]
50
+ # Return true if the event is informational.
51
+ #
52
+ def informational?
53
+ severity == 0
54
+ end
55
+
56
+ #
57
+ # Return ture if the event is of low severity.
58
+ #
59
+ # @return [Boolean]
60
+ # Return true if the event is low severity.
61
+ #
62
+ def low?
63
+ severity == 1
64
+ end
65
+
66
+ #
67
+ # Return ture if the event is of medium severity.
68
+ #
69
+ # @return [Boolean]
70
+ # Return true if the event is medium severity.
71
+ #
72
+ def medium?
73
+ severity == 2
74
+ end
75
+
76
+ #
77
+ # Return ture if the event is of high severity.
78
+ #
79
+ # @return [Boolean]
80
+ # Return true if the event is high severity.
81
+ #
82
+ def high?
83
+ severity == 3
84
+ end
85
+
86
+ #
87
+ # Return the event object nessus plugin id
88
+ #
89
+ # @return [String]
90
+ # Return the event object nessus plugin id
91
+ #
92
+ # @example
93
+ # event.plugin_id #=> 3245
94
+ #
95
+ def id
96
+ @plugin_id ||= @event.at('@pluginID').inner_text.to_i
97
+ end
98
+ alias plugin_id id
99
+
100
+ #
101
+ # Return the event object plugin family name.
102
+ #
103
+ # @return [String]
104
+ # Return the event object plugin family name.
105
+ #
106
+ # @example
107
+ # event.family #=> "Service detection"
108
+ #
109
+ def family
110
+ @plugin_family ||= @event.at('@pluginFamily').inner_text
111
+ end
112
+ alias plugin_family family
113
+
114
+ #
115
+ # Return the event name (plugin_name)
116
+ #
117
+ # @return [String, false]
118
+ # Return the event name (plugin_name)
119
+ #
120
+ # @example
121
+ # event.plugin_name #=> "PHP < 5.2.4 Multiple Vulnerabilities"
122
+ # event.name #=> "PHP < 5.2.4 Multiple Vulnerabilities"
123
+ #
124
+ def plugin_name
125
+ s = @event.at('@pluginName').inner_text
126
+
127
+ @plugin_name ||= if s.empty?
128
+ false
129
+ else
130
+ @event.at('@pluginName').inner_text
131
+ end
132
+
133
+ return @plugin_name
134
+ end
135
+ alias name plugin_name
136
+
137
+ #
138
+ # Return the event synopsis.
139
+ #
140
+ # @return [String, false]
141
+ # Return the event synopsis.
142
+ #
143
+ def synopsis
144
+ @synopsis ||= if @event.at('synopsis')
145
+ @event.at('synopsis').inner_text
146
+ else
147
+ false
148
+ end
149
+ end
150
+
151
+ #
152
+ # Return the event description.
153
+ #
154
+ # @return [String, false]
155
+ # Return the event description.
156
+ #
157
+ def description
158
+ @description ||= if @event.at('description')
159
+ @event.at('description').inner_text
160
+ else
161
+ false
162
+ end
163
+ end
164
+
165
+ #
166
+ # Return the event solution.
167
+ #
168
+ # @return [String, false]
169
+ # Return the event solution.
170
+ #
171
+ def solution
172
+ @solution ||= if @event.at('solution')
173
+ @event.at('solution').inner_text
174
+ else
175
+ false
176
+ end
177
+ end
178
+
179
+ #
180
+ # Return the event risk.
181
+ #
182
+ # @return [String, false]
183
+ # Return the event risk.
184
+ #
185
+ def risk
186
+ @risk_factor ||= if @event.at('risk_factor')
187
+ @event.at('risk_factor').inner_text
188
+ else
189
+ false
190
+ end
191
+ end
192
+
193
+ #
194
+ # Return the event plugin output.
195
+ #
196
+ # @return [String, false]
197
+ # Return the event plugin output.
198
+ #
199
+ def output
200
+ @plugin_output ||= if @event.at('plugin_output')
201
+ @event.at('plugin_output').inner_text
202
+ else
203
+ false
204
+ end
205
+ end
206
+ alias data output
207
+ alias plugin_output output
208
+
209
+ #
210
+ # Return the event plugin version.
211
+ #
212
+ # @return [String, false]
213
+ # Return the event plugin version.
214
+ #
215
+ def version
216
+ @plugin_version ||= if @event.at('plugin_version')
217
+ @event.at('plugin_version').inner_text
218
+ else
219
+ false
220
+ end
221
+ end
222
+ alias plugin_version version
223
+
224
+ #
225
+ # Return the event reference links.
226
+ #
227
+ # @return [String, false]
228
+ # Return the event reference links.
229
+ #
230
+ def see_also
231
+ unless @see_also
232
+ @see_also = []
233
+ @event.xpath("see_also").each do |see_also|
234
+ @see_also << see_also.inner_text
235
+ end
236
+ end
237
+ @see_also
238
+ end
239
+ alias links see_also
240
+ alias more see_also
241
+ alias references see_also
242
+
243
+ #
244
+ # Return the event patch publication date.
245
+ #
246
+ # @return [String, false]
247
+ # Return the event patch publication date.
248
+ #
249
+ def patch_publication_date
250
+ @patch_publication_date ||= if @event.at('patch_publication_date')
251
+ DateTime.strptime(@event.at('patch_publication_date').inner_text, fmt='%Y/%m/%d')
252
+ else
253
+ false
254
+ end
255
+ end
256
+
257
+ #
258
+ # Return the event cvss base score.
259
+ #
260
+ # @return [String, false]
261
+ # Return the event cvss base score.
262
+ #
263
+ def cvss_base_score
264
+ @cvss_base_score ||= if @event.at('cvss_base_score')
265
+ @event.at('cvss_base_score').inner_text.to_f
266
+ else
267
+ false
268
+ end
269
+ end
270
+
271
+ #
272
+ # Return the event cve.
273
+ #
274
+ # @return [String, false]
275
+ # Return the event cvss base score.
276
+ #
277
+ def cve
278
+ @cve ||= if @event.at('cve')
279
+ @event.at('cve').inner_text
280
+ else
281
+ false
282
+ end
283
+ end
284
+
285
+ #
286
+ # Return the event bid.
287
+ #
288
+ # @return [String, false]
289
+ # Return the event bid.
290
+ #
291
+ def bid
292
+ @bid ||= if @event.at('bid')
293
+ @event.at('bid').inner_text.to_i
294
+ else
295
+ false
296
+ end
297
+ end
298
+
299
+ #
300
+ # Return other event related references.
301
+ #
302
+ # @return [String, false]
303
+ # Return the event related references.
304
+ #
305
+ def xref
306
+ unless @xref
307
+ @xref = []
308
+ @event.xpath("xref").each do |xref|
309
+ @xref << xref.inner_text
310
+ end
311
+ end
312
+ @xref
313
+ end
314
+
315
+ #
316
+ # Return other event cvss vector.
317
+ #
318
+ # @return [String, false]
319
+ # Return the event cvss vector.
320
+ #
321
+ def cvss_vector
322
+ @cvss_vector ||= if @event.at('cvss_vector')
323
+ @event.at('cvss_vector').inner_text
324
+ else
325
+ false
326
+ end
327
+ end
328
+
329
+ end
330
+
331
+ end
332
+
333
+ end
@@ -0,0 +1,528 @@
1
+ module Nessus
2
+ module Version2
3
+
4
+ class Host
5
+ include Enumerable
6
+
7
+ # Host
8
+ attr_reader :host
9
+
10
+ #
11
+ # Creates A New Host Object
12
+ #
13
+ # @param [Object] Host Object
14
+ #
15
+ # @example
16
+ # Host.new(object)
17
+ #
18
+ def initialize(host)
19
+ @host = host
20
+ end
21
+
22
+ #
23
+ # Return the Host Object hostname.
24
+ #
25
+ # @return [String]
26
+ # The Host Object Hostname
27
+ #
28
+ # @example
29
+ # host.hostname #=> "example.com"
30
+ #
31
+ def hostname
32
+ @hostname ||= @host.at('tag[name=host-fqdn]').inner_text
33
+ end
34
+ alias name hostname
35
+ alias fqdn hostname
36
+ alias dns_name hostname
37
+
38
+ #
39
+ # Return the Host Object IP.
40
+ #
41
+ # @return [String]
42
+ # The Host Object IP
43
+ #
44
+ # @example
45
+ # host.ip #=> "127.0.0.1"
46
+ #
47
+ def ip
48
+ @ip ||= @host.at('tag[name=host-ip]').inner_text
49
+ end
50
+
51
+ #
52
+ # Return the host scan start time.
53
+ #
54
+ # @return [DateTime]
55
+ # The Host Scan Start Time
56
+ #
57
+ # @example
58
+ # scan.scan_start_time #=> 'Fri Nov 11 23:36:54 1985'
59
+ #
60
+ def start_time
61
+ if @host.at('tag[name=HOST_START]').inner_text.blank?
62
+ return false
63
+ else
64
+ @host_scan_start_time = DateTime.strptime(@host.at('tag[name=HOST_START]').inner_text, fmt='%a %b %d %H:%M:%S %Y')
65
+ end
66
+ end
67
+
68
+ #
69
+ # Return the host scan stop time.
70
+ #
71
+ # @return [DateTime]
72
+ # The Host Scan Stop Time
73
+ #
74
+ # @example
75
+ # scan.scan_start_time #=> 'Fri Nov 11 23:36:54 1985'
76
+ #
77
+ def stop_time
78
+ if @host.at('tag[name=HOST_END]').inner_text.blank?
79
+ return false
80
+ else
81
+ @host_scan_stop_time = DateTime.strptime(@host.at('tag[name=HOST_END]').inner_text, fmt='%a %b %d %H:%M:%S %Y')
82
+ end
83
+ end
84
+
85
+ #
86
+ # Return the host run time.
87
+ #
88
+ # @return [String]
89
+ # The Host Scan Run Time
90
+ #
91
+ # @example
92
+ # scan.scan_run_time #=> '2 hours 5 minutes and 16 seconds'
93
+ #
94
+ def runtime
95
+ h = ("#{Time.parse(stop_time.to_s).strftime('%H').to_i - Time.parse(start_time.to_s).strftime('%H').to_i}").gsub('-', '')
96
+ m = ("#{Time.parse(stop_time.to_s).strftime('%M').to_i - Time.parse(start_time.to_s).strftime('%M').to_i}").gsub('-', '')
97
+ s = ("#{Time.parse(stop_time.to_s).strftime('%S').to_i - Time.parse(start_time.to_s).strftime('%S').to_i}").gsub('-', '')
98
+ return "#{h} hours #{m} minutes and #{s} seconds"
99
+ end
100
+
101
+ #
102
+ # Return the Host Netbios Name.
103
+ #
104
+ # @return [String]
105
+ # The Host Netbios Name
106
+ #
107
+ # @example
108
+ # host.netbios_name #=> "SOMENAME4243"
109
+ #
110
+ def netbios_name
111
+ @netbios_name ||= @host.at('tag[name=netbios-name]').inner_text
112
+ end
113
+
114
+ #
115
+ # Return the Host Mac Address.
116
+ #
117
+ # @return [String]
118
+ # Return the Host Mac Address
119
+ #
120
+ # @example
121
+ # host.mac_addr #=> "00:11:22:33:44:55"
122
+ #
123
+ def mac_addr
124
+ @mac_addr ||= @host.at('tag[name=mac-addr]').inner_text
125
+ end
126
+ alias mac_address mac_addr
127
+
128
+ #
129
+ # Return the Host OS Name.
130
+ #
131
+ # @return [String]
132
+ # Return the Host OS Name
133
+ #
134
+ # @example
135
+ # host.dns_name #=> "Microsoft Windows 2000, Microsoft Windows Server 2003"
136
+ #
137
+ def os_name
138
+ @os_name ||= @host.at('tag[name=operating-system]').inner_text
139
+ end
140
+ alias os os_name
141
+ alias operating_system os_name
142
+
143
+ #
144
+ # Return the open ports for a given host object.
145
+ #
146
+ # @return [Integer]
147
+ # Return the open ports for a given host object.
148
+ #
149
+ # @example
150
+ # host.open_ports #=> 213
151
+ #
152
+ def open_ports
153
+ @scanned_ports ||= host_stats[:open_ports].to_i
154
+ end
155
+
156
+ #
157
+ # Returns All Informational Event Objects For A Given Host.
158
+ #
159
+ # @yield [prog] If a block is given, it will be passed the newly
160
+ # created Event object.
161
+ #
162
+ # @yieldparam [EVENT] prog The newly created Event object.
163
+ #
164
+ # @return [Integer]
165
+ # Return The Informational Event Count For A Given Host.
166
+ #
167
+ # @example
168
+ # host.informational_severity_events do |info|
169
+ # puts info.port
170
+ # puts info.data if info.data
171
+ # end
172
+ #
173
+ def informational_severity_events(&block)
174
+ unless @informational_events
175
+ @informational_events = []
176
+
177
+ @host.xpath("ReportItem").each do |event|
178
+ next if event['severity'].to_i != 0
179
+ @informational_events << Event.new(event)
180
+ end
181
+
182
+ end
183
+
184
+ @informational_events.each(&block)
185
+ end
186
+
187
+ #
188
+ # Returns All Low Event Objects For A Given Host.
189
+ #
190
+ # @yield [prog] If a block is given, it will be passed the newly
191
+ # created Event object.
192
+ #
193
+ # @yieldparam [EVENT] prog The newly created Event object.
194
+ #
195
+ # @return [Integer]
196
+ # Return The Low Event Count For A Given Host.
197
+ #
198
+ # @example
199
+ # host.low_severity_events do |low|
200
+ # puts low.name if low.name
201
+ # end
202
+ #
203
+ def low_severity_events(&block)
204
+
205
+ unless @low_severity_events
206
+ @low_severity_events = []
207
+
208
+ @host.xpath("ReportItem").each do |event|
209
+ next if event['severity'].to_i != 1
210
+ @low_severity_events << Event.new(event)
211
+ end
212
+
213
+ end
214
+
215
+ @low_severity_events.each(&block)
216
+ end
217
+
218
+ #
219
+ # Returns All Medium Event Objects For A Given Host.
220
+ #
221
+ # @yield [prog] If a block is given, it will be passed the newly
222
+ # created Event object.
223
+ # @yieldparam [EVENT] prog The newly created Event object.
224
+ #
225
+ # @return [Integer]
226
+ # Return The Medium Event Count For A Given Host.
227
+ #
228
+ # @example
229
+ # host.medium_severity_events do |medium|
230
+ # puts medium.name if medium.name
231
+ # end
232
+ #
233
+ def medium_severity_events(&block)
234
+
235
+ unless @medium_severity_events
236
+ @medium_severity_events = []
237
+
238
+ @host.xpath("ReportItem").each do |event|
239
+ next if event['severity'].to_i != 2
240
+ @medium_severity_events << Event.new(event)
241
+ end
242
+
243
+ end
244
+
245
+ @medium_severity_events.each(&block)
246
+ end
247
+
248
+ def medium_severity
249
+ Enumerator.new(self,:medium_severity_events).to_a
250
+ end
251
+
252
+ #
253
+ # Returns All High Event Objects For A Given Host.
254
+ #
255
+ # @yield [prog] If a block is given, it will be passed the newly
256
+ # created Event object.
257
+ #
258
+ # @yieldparam [EVENT] prog The newly created Event object.
259
+ #
260
+ # @return [Integer]
261
+ # Return The High Event Count For A Given Host.
262
+ #
263
+ # @example
264
+ # host.high_severity_events do |high|
265
+ # puts high.name if high.name
266
+ # end
267
+ #
268
+ def high_severity_events(&block)
269
+
270
+ unless @high_severity_events
271
+ @high_severity_events = []
272
+
273
+ @host.xpath("ReportItem").each do |event|
274
+ next if event['severity'].to_i != 3
275
+ @high_severity_events << Event.new(event)
276
+ end
277
+
278
+ end
279
+
280
+ @high_severity_events.each(&block)
281
+ end
282
+
283
+ #
284
+ # Return the total event count for a given host.
285
+ #
286
+ # @return [Integer]
287
+ # Return the total event count for a given host.
288
+ #
289
+ # @example
290
+ # host.event_count #=> 3456
291
+ #
292
+ def event_count
293
+ ((low_severity_events.to_i) + (medium_severity_events.to_i) + (high_severity_events.to_i)).to_i
294
+ end
295
+
296
+ #
297
+ # Creates a new Event object to be parser
298
+ #
299
+ # @yield [prog] If a block is given, it will be passed the newly
300
+ # created Event object.
301
+ # @yieldparam [EVENT] prog The newly created Event object.
302
+ #
303
+ # @example
304
+ # host.each_event do |event|
305
+ # puts event.name if event.name
306
+ # puts event.port
307
+ # end
308
+ #
309
+ def each_event(&block)
310
+ @host.xpath("ReportItem").each do |event|
311
+ block.call(Event.new(event)) if block
312
+ end
313
+ end
314
+
315
+ #
316
+ # Parses the events of the host.
317
+ #
318
+ # @return [Array<String>]
319
+ # The events of the host.
320
+ #
321
+ def events
322
+ Enumerator.new(self,:each_event).to_a
323
+ end
324
+
325
+ #
326
+ # Return the Open Ports count.
327
+ #
328
+ # @return [Array]
329
+ # The Open Ports Count
330
+ #
331
+ # @example
332
+ # scan.ports #=> ['22', '80', '443']
333
+ #
334
+ def ports
335
+ unless @ports
336
+ @ports = []
337
+ @host.xpath("ReportItem").each do |port|
338
+ @ports << port['port']
339
+ end
340
+ @ports.uniq!
341
+ @ports.sort!
342
+ end
343
+ @ports
344
+ end
345
+
346
+ #
347
+ # Return the TCP Event Count.
348
+ #
349
+ # @return [Integer]
350
+ # The TCP Event Count
351
+ #
352
+ # @example
353
+ # scan.tcp_count #=> 3
354
+ #
355
+ def tcp_count
356
+ host_stats[:tcp].to_i
357
+ end
358
+
359
+ #
360
+ # Return the UDP Event Count.
361
+ #
362
+ # @return [Integer]
363
+ # The UDP Event Count
364
+ #
365
+ # @example
366
+ # scan.udp_count #=> 3
367
+ #
368
+ def udp_count
369
+ host_stats[:udp].to_i
370
+ end
371
+
372
+ #
373
+ # Return the ICMP Event Count.
374
+ #
375
+ # @return [Integer]
376
+ # The ICMP Event Count
377
+ #
378
+ # @example
379
+ # scan.icmp_count #=> 3
380
+ #
381
+ def icmp_count
382
+ host_stats[:icmp].to_i
383
+ end
384
+
385
+ #
386
+ # Return the informational severity count.
387
+ #
388
+ # @return [Integer]
389
+ # The Informational Severity Count
390
+ #
391
+ # @example
392
+ # scan.informational_severity_count #=> 1203
393
+ #
394
+ def informational_severity_count
395
+ host_stats[:informational].to_i
396
+ end
397
+
398
+ #
399
+ # Return the High severity count.
400
+ #
401
+ # @return [Integer]
402
+ # The High Severity Count
403
+ #
404
+ # @example
405
+ # scan.high_severity_count #=> 10
406
+ #
407
+ def high_severity_count
408
+ host_stats[:high].to_i
409
+ end
410
+
411
+ #
412
+ # Return the Medium severity count.
413
+ #
414
+ # @return [Integer]
415
+ # The Medium Severity Count
416
+ #
417
+ # @example
418
+ # scan.medium_severity_count #=> 234
419
+ #
420
+ def medium_severity_count
421
+ host_stats[:medium].to_i
422
+ end
423
+
424
+ #
425
+ # Return the Low severity count.
426
+ #
427
+ # @return [Integer]
428
+ # The Low Severity Count
429
+ #
430
+ # @example
431
+ # scan.low_severity_count #=> 114
432
+ #
433
+ def low_severity_count
434
+ host_stats[:low].to_i
435
+ end
436
+
437
+ #
438
+ # Return the Total severity count. [high, medium, low, informational]
439
+ #
440
+ # @return [Integer]
441
+ # The Total Severity Count
442
+ #
443
+ # @example
444
+ # scan.total_event_count #=> 1561
445
+ #
446
+ def total_event_count(count_informational = false)
447
+ if count_informational
448
+ host_stats[:all].to_i + informational_severity_count
449
+ else
450
+ host_stats[:all].to_i
451
+ end
452
+ end
453
+
454
+ #
455
+ # Return the Total severity count.
456
+ #
457
+ # @param [String] severity the severity in which to calculate percentage for.
458
+ #
459
+ # @param [true, false] round round the result to the nearest whole number.
460
+ #
461
+ # @raise [ExceptionClass] One of the following severity options must be passed. [high, medium, low, informational, all]
462
+ #
463
+ # @return [Integer]
464
+ # The Percentage Of Events For A Passed Severity
465
+ #
466
+ # @example
467
+ # scan.event_percentage_for("low", true) #=> 11%
468
+ #
469
+ def event_percentage_for(type, round_percentage=false)
470
+ @sc ||= host_stats
471
+ if %W(high medium low tcp udp icmp all).include?(type)
472
+ calc = ((@sc[:"#{type}"].to_f / (@sc[:all].to_f)) * 100)
473
+ if round_percentage
474
+ return "#{calc.round}"
475
+ else
476
+ return "#{calc}"
477
+ end
478
+ else
479
+ raise "Error: #{type} is not an acceptable severity. Possible options include: all, tdp, udp, icmp, high, medium and low."
480
+ end
481
+ end
482
+
483
+ private
484
+
485
+ def host_stats
486
+
487
+ unless @host_stats
488
+ @host_stats = {}
489
+ @open_ports, @tcp, @udp, @icmp, @informational, @low, @medium, @high = 0,0,0,0,0,0,0,0
490
+
491
+ @host.xpath("ReportItem").each do |s|
492
+ case s['severity'].to_i
493
+ when 0
494
+ @informational += 1
495
+ when 1
496
+ @low += 1
497
+ when 2
498
+ @medium += 1
499
+ when 3
500
+ @high += 1
501
+ end
502
+
503
+ unless s['severity'].to_i == 0
504
+ @tcp += 1 if s['protocol'] == 'tcp'
505
+ @udp += 1 if s['protocol'] == 'udp'
506
+ @icmp += 1 if s['protocol'] == 'icmp'
507
+ end
508
+
509
+ @open_ports += 1 if s['port'].to_i != 0
510
+ end
511
+
512
+ @host_stats = {:open_ports => @open_ports,
513
+ :tcp => @tcp,
514
+ :udp => @udp,
515
+ :icmp => @icmp,
516
+ :informational => @informational,
517
+ :low => @low,
518
+ :medium => @medium,
519
+ :high => @high,
520
+ :all => (@low + @medium + @high)}
521
+
522
+ end
523
+ @host_stats
524
+ end
525
+
526
+ end
527
+ end
528
+ end