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