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,79 @@
1
+ module Nessus
2
+ module Version2
3
+
4
+ class Port
5
+
6
+ # Port Service
7
+ attr_reader :service
8
+ # Port number
9
+ attr_reader :number
10
+ # Port Protocol
11
+ attr_reader :protocol
12
+
13
+ #
14
+ # Creates A New Port Object
15
+ #
16
+ # @param [String] number The port number.
17
+ #
18
+ # @param [String] service The port service.
19
+ #
20
+ # @param [String] protocol The port protocol.
21
+ #
22
+ # @example
23
+ # Port.new(number, service, )
24
+ #
25
+ def initialize(number, service, protocol)
26
+ @number = number
27
+ @service = service
28
+ @protocol = protocol
29
+ end
30
+
31
+ #
32
+ # Return true if the port protocol is tcp.
33
+ #
34
+ # @return [Boolean]
35
+ # Return True If The Port Protocol Is TCP.
36
+ #
37
+ def tcp?
38
+ @protocol.to_s == 'tcp'
39
+ end
40
+
41
+ #
42
+ # Return true if the port protocol is udp.
43
+ #
44
+ # @return [Boolean]
45
+ # Return True If The Port Protocol Is UDP.
46
+ #
47
+ def udp?
48
+ @protocol.to_s == 'udp'
49
+ end
50
+
51
+ #
52
+ # Return true if the port protocol is icmp.
53
+ #
54
+ # @return [Boolean]
55
+ # Return True If The Port Protocol Is ICMP.
56
+ #
57
+ def icmp?
58
+ @protocol.to_s == 'icmp'
59
+ end
60
+
61
+ #
62
+ # Return the port as a string.
63
+ #
64
+ # @return [String]
65
+ # Return The Port As A String
66
+ #
67
+ # @example
68
+ # port.to_s #=> https (443/tcp)
69
+ #
70
+ def to_s
71
+ "#{@service} (#{@number}/#{@protocol})"
72
+ end
73
+
74
+
75
+ end
76
+
77
+ end
78
+
79
+ end
@@ -0,0 +1,363 @@
1
+ require 'lib/gemcache/ruby-nessus/ruby-nessus/Version2/host'
2
+ require 'lib/gemcache/ruby-nessus/ruby-nessus/Version2/event'
3
+
4
+ module Nessus
5
+
6
+ # .Nessus Version 2 Schema
7
+ module Version2
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
+ # @yieldparam [XML] prog The newly created XML object.
24
+ #
25
+ # @example
26
+ # Nessus::XML.new(nessus_scan_file) do |scan|
27
+ # scan.report_name
28
+ # end
29
+ #
30
+ def initialize(xml)
31
+ @xml = xml
32
+ raise "Error: Not A Version 2.0 .Nessus file." unless @xml.at('NessusClientData_v2')
33
+ end
34
+
35
+
36
+ def version
37
+ 2
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.at('Report/@name').inner_text
51
+ end
52
+
53
+ #
54
+ # Return the nessus scan policy name. When creating a nessus policy this is usually the title field.
55
+ #
56
+ # @return [String]
57
+ # The Nessus Scan Policy Name
58
+ #
59
+ def policy_title
60
+ @policy_name ||= @xml.at("//Policy/policyName").inner_text
61
+ end
62
+
63
+ #
64
+ # Return the nessus scan policy comments. This is the description field when creating a new policy with the Nessus GUI client.
65
+ #
66
+ # @return [String]
67
+ # The Nessus Scan Policy Comments
68
+ #
69
+ def policy_notes
70
+ @policy_notes ||= @xml.at("//Policy/policyComments").inner_text
71
+ end
72
+
73
+ #
74
+ # Creates a new Host object to be parser
75
+ #
76
+ # @yield [prog] If a block is given, it will be passed the newly
77
+ # created Host object.
78
+ # @yieldparam [XML] prog The newly created Host object.
79
+ #
80
+ # @example
81
+ # scan.hosts do |host|
82
+ # puts host.hostname
83
+ # end
84
+ #
85
+ def each_host(&block)
86
+ hosts = []
87
+ @xml.xpath("//ReportHost").each do |host|
88
+ hosts << host['name'] if host['name']
89
+ block.call(Host.new(host)) if block
90
+ end
91
+ hosts
92
+ end
93
+
94
+ #
95
+ # Parses the hosts of the scan.
96
+ #
97
+ # @return [Array<String>]
98
+ # The Hosts of the scan.
99
+ #
100
+ def hosts
101
+ Enumerator.new(self,:each_host).to_a
102
+ end
103
+
104
+ #
105
+ # Return the nessus scan host count.
106
+ #
107
+ # @return [Integer]
108
+ # The Nessus Scan Host Count
109
+ #
110
+ # @example
111
+ # scan.host_count #=> 23
112
+ #
113
+ def host_count
114
+ each_host.size
115
+ end
116
+
117
+ #
118
+ # Retunrs an array of all unique ports.
119
+ #
120
+ # @return [Array]
121
+ #
122
+ # @example
123
+ # scan.unique_ports #=> 234
124
+ #
125
+ def unique_ports
126
+ unless @unique_ports
127
+ @unique_ports = []
128
+ @xml.xpath("//ReportItem").each do |port|
129
+ @unique_ports << port['port']
130
+ end
131
+ @unique_ports.uniq!
132
+ @unique_ports.sort!
133
+ end
134
+ end
135
+
136
+ #
137
+ # Return the Open Ports count.
138
+ #
139
+ # @return [Integer]
140
+ # The Open Ports Count
141
+ #
142
+ # @example
143
+ # scan.open_ports_count #=> 1203
144
+ #
145
+ def open_ports_count
146
+ count_stats[:open_ports].to_i
147
+ end
148
+
149
+ #
150
+ # Return the TCP Event Count.
151
+ #
152
+ # @return [Integer]
153
+ # The TCP Event Count
154
+ #
155
+ # @example
156
+ # scan.tcp_count #=> 3
157
+ #
158
+ def tcp_count
159
+ count_stats[:tcp].to_i
160
+ end
161
+
162
+ #
163
+ # Return the UDP Event Count.
164
+ #
165
+ # @return [Integer]
166
+ # The UDP Event Count
167
+ #
168
+ # @example
169
+ # scan.udp_count #=> 3
170
+ #
171
+ def udp_count
172
+ count_stats[:udp].to_i
173
+ end
174
+
175
+ #
176
+ # Return the ICMP Event Count.
177
+ #
178
+ # @return [Integer]
179
+ # The ICMP Event Count
180
+ #
181
+ # @example
182
+ # scan.icmp_count #=> 3
183
+ #
184
+ def icmp_count
185
+ count_stats[:icmp].to_i
186
+ end
187
+
188
+ #
189
+ # Return the informational severity count.
190
+ #
191
+ # @return [Integer]
192
+ # The Informational Severity Count
193
+ #
194
+ # @example
195
+ # scan.informational_severity_count #=> 1203
196
+ #
197
+ def informational_severity_count
198
+ count_stats[:informational].to_i
199
+ end
200
+
201
+ #
202
+ # Return the High severity count.
203
+ #
204
+ # @return [Integer]
205
+ # The High Severity Count
206
+ #
207
+ # @example
208
+ # scan.high_severity_count #=> 10
209
+ #
210
+ def high_severity_count
211
+ count_stats[:high].to_i
212
+ end
213
+
214
+ #
215
+ # Return the Medium severity count.
216
+ #
217
+ # @return [Integer]
218
+ # The Medium Severity Count
219
+ #
220
+ # @example
221
+ # scan.medium_severity_count #=> 234
222
+ #
223
+ def medium_severity_count
224
+ count_stats[:medium].to_i
225
+ end
226
+
227
+ #
228
+ # Return the Low severity count.
229
+ #
230
+ # @return [Integer]
231
+ # The Low Severity Count
232
+ #
233
+ # @example
234
+ # scan.low_severity_count #=> 114
235
+ #
236
+ def low_severity_count
237
+ count_stats[:low].to_i
238
+ end
239
+
240
+ #
241
+ # Return the Total severity count. [high, medium, low, informational]
242
+ #
243
+ # @param [true, false] argname only true or false
244
+ #
245
+ # @return [Integer]
246
+ # The Total Severity Count
247
+ #
248
+ # @example
249
+ # scan.total_event_count #=> 1561
250
+ #
251
+ def total_event_count(count_informational = false)
252
+ if count_informational
253
+ count_stats[:all].to_i + informational_severity_count
254
+ else
255
+ count_stats[:all].to_i
256
+ end
257
+ end
258
+
259
+ #
260
+ # Return the Total severity count.
261
+ #
262
+ # @param [String] severity the severity in which to calculate percentage for.
263
+ #
264
+ # @param [Boolean] round round the result to the nearest whole number.
265
+ #
266
+ # @raise [ExceptionClass] One of the following severity options must be passed. [high, medium, low, informational, all]
267
+ #
268
+ # @return [Integer]
269
+ # The Percentage Of Events For A Passed Severity
270
+ #
271
+ # @example
272
+ # scan.event_percentage_for("low", true) #=> 11%
273
+ #
274
+ def event_percentage_for(type, round_percentage=false)
275
+ @sc ||= count_stats
276
+ if %W(high medium low tcp udp icmp all).include?(type)
277
+ calc = ((@sc[:"#{type}"].to_f / (@sc[:all].to_f)) * 100)
278
+ if round_percentage
279
+ return "#{calc.round}"
280
+ else
281
+ return "#{calc}"
282
+ end
283
+ else
284
+ raise "Error: #{type} is not an acceptable severity. Possible options include: all, tdp, udp, icmp, high, medium and low."
285
+ end
286
+ end
287
+
288
+ #
289
+ # Creates a new Host object to be parser from a passed search param.
290
+ #
291
+ # @param [String] hostname the hostname to build a Host object for.
292
+ #
293
+ # @yield [prog] If a block is given, it will be passed the newly
294
+ # created Host object.
295
+ #
296
+ # @yieldparam [XML] prog The newly created Host object.
297
+ #
298
+ # @example
299
+ # scan.find_by_hostname('127.0.0.1') do |host|
300
+ # puts host.hostname
301
+ # end
302
+ #
303
+ def find_by_hostname(hostname, &block)
304
+ raise "Error: hostname can't be blank." if hostname.blank?
305
+ @xml.xpath('//ReportHost').each do |host|
306
+ next unless host['name'].match(hostname)
307
+ block.call(Host.new(host)) if block
308
+ end
309
+ end
310
+
311
+ private
312
+
313
+ #
314
+ # Calculates an event hash of totals for severity counts.
315
+ #
316
+ # @return [Hash]
317
+ # The Event Totals For Severity
318
+ #
319
+ def count_stats
320
+ unless @count
321
+ @count = {}
322
+ @open_ports, @tcp, @udp, @icmp, @informational, @low, @medium, @high = 0,0,0,0,0,0,0,0
323
+
324
+ @xml.xpath("//ReportItem").each do |s|
325
+ case s['severity'].to_i
326
+ when 0
327
+ @informational += 1
328
+ when 1
329
+ @low += 1
330
+ when 2
331
+ @medium += 1
332
+ when 3
333
+ @high += 1
334
+ end
335
+
336
+ unless s['severity'].to_i == 0
337
+ @tcp += 1 if s['protocol'] == 'tcp'
338
+ @udp += 1 if s['protocol'] == 'udp'
339
+ @icmp += 1 if s['protocol'] == 'icmp'
340
+ end
341
+
342
+ @open_ports += 1 if s['port'].to_i != 0
343
+ end
344
+
345
+ @count = {:open_ports => @open_ports,
346
+ :tcp => @tcp,
347
+ :udp => @udp,
348
+ :icmp => @icmp,
349
+ :informational => @informational,
350
+ :low => @low,
351
+ :medium => @medium,
352
+ :high => @high,
353
+ :all => (@low + @medium + @high)}
354
+ end
355
+
356
+ return @count
357
+ end
358
+
359
+ end
360
+
361
+
362
+ end
363
+ end
@@ -0,0 +1,132 @@
1
+ require 'rubygems'
2
+ require 'lib/gemcache/ruby-nessus/ruby-nessus/nessus'
3
+ require 'lib/gemcache/ruby-nessus/ruby-nessus/log'
4
+ require 'optparse'
5
+
6
+ require 'pp'
7
+
8
+ module Nessus
9
+
10
+ class CLI
11
+
12
+ def initialize
13
+ @file = nil
14
+ @nessus_version = nil
15
+ @args = []
16
+ end
17
+
18
+ def CLI.run
19
+ self.new.run(*ARGV)
20
+ end
21
+
22
+ def run(*args)
23
+ optparse(*args)
24
+
25
+ Log.it "Recess - Ruby-Nessus CLI"
26
+ Log.it "Version: #{Nessus::VERSION}"
27
+ Log.it
28
+
29
+ Nessus::Parse.new("#{@file}") do |scan|
30
+
31
+ Log.h1 "SCAN Metadata"
32
+ Log.it
33
+ Log.h2 "Scan Title", scan.title
34
+ Log.h2 "Policy Title", scan.policy_title
35
+ Log.it
36
+ Log.h1 "SCAN Statistics"
37
+ Log.it
38
+ Log.h2 "Host Count", scan.host_count
39
+ Log.h2 "Open Port Count", scan.open_ports_count
40
+
41
+ unless scan.version == 1
42
+ Log.h2 "TCP Count", scan.tcp_count
43
+ Log.h2 "UDP Count", scan.udp_count
44
+ Log.h2 "ICMP Count", scan.icmp_count
45
+ end
46
+
47
+ Log.it
48
+ Log.h1 "EVENT Statistics"
49
+ Log.it
50
+
51
+ unless scan.version == 1
52
+ Log.informational "Informational Severity Count", scan.informational_severity_count
53
+ end
54
+
55
+ Log.low "Low Severity Count", scan.low_severity_count
56
+ Log.medium "Medium Severity Count", scan.medium_severity_count
57
+ Log.high "High Severity Count", scan.high_severity_count
58
+ Log.h3 "Total Event Count", scan.total_event_count
59
+ Log.break
60
+ Log.it! "Low Event Percentage: #{scan.event_percentage_for('low', true)}"
61
+ Log.it! "Medium Event Percentage: #{scan.event_percentage_for('medium', true)}"
62
+ Log.it! "High Event Percentage: #{scan.event_percentage_for('high', true)}"
63
+ Log.it
64
+
65
+ Log.h1 "HOSTS"
66
+ Log.it
67
+
68
+ scan.each_host do |host|
69
+ Log.h2 "Hostname", host.hostname
70
+ Log.h5 "IP Address:", host.ip
71
+
72
+ unless scan.version == 1
73
+ Log.h5 "Informational Count", host.informational_severity_count
74
+ Log.h5 "Low Count", host.low_severity_count
75
+ Log.h5 "Medium Count", host.medium_severity_count
76
+ Log.h5 "High Count", host.high_severity_count
77
+ end
78
+ Log.it
79
+ end
80
+
81
+ Log.end
82
+
83
+ end
84
+
85
+ end
86
+
87
+ protected
88
+
89
+ def optparse(*args)
90
+ opts = OptionParser.new
91
+ opts.program_name = "recess"
92
+ opts.banner = "Recess #{Nessus::VERSION}"
93
+ opts.separator "usage: recess FILE [OPTIONS]"
94
+
95
+ opts.on('-f','--file FILE','The .nessus file to parse.') do |file|
96
+ @file = file
97
+ end
98
+
99
+ opts.on('-f','--file FILE','The .nessus file to parse.') do |file|
100
+ @file = file
101
+ end
102
+
103
+ opts.on('-h','--help','This help summary page.') do |help|
104
+ Log.it opts
105
+ Log.it
106
+ exit -1
107
+ end
108
+
109
+ opts.on('-v','--version','Recess Version.') do |version|
110
+ Log.it Nessus::VERSION
111
+ Log.it
112
+ exit -1
113
+ end
114
+
115
+ begin
116
+ @args = opts.parse!(args)
117
+ @file ||= @args[0]
118
+ if @file.nil?
119
+ Log.it opts
120
+ Log.it
121
+ exit -1
122
+ end
123
+ rescue => e
124
+ Log.error e.message
125
+ Log.it opts
126
+ Log.it
127
+ exit -1
128
+ end
129
+ end
130
+
131
+ end
132
+ end