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.
@@ -1,87 +0,0 @@
1
- require 'ruby-nessus/port'
2
-
3
- module Nessus
4
- class Event
5
- # Event
6
- attr_reader :event
7
-
8
- # Return the total event count for a given host.
9
- # @return [Integer]
10
- # Return the total event count for a given host.
11
- # @example
12
- # host.event_count #=> 3456
13
- def initialize(event)
14
- @event = event
15
- end
16
-
17
- # Return the event port.
18
- # @return [Object]
19
- # Return the event port object or port string.
20
- # @example
21
- # event.port #=> "https (443/tcp)"
22
- # event.port.number #=> 443
23
- # event.port.service #=> "https"
24
- # event.port.protocol #=> "tcp"
25
- def port
26
- @port ||= Port.parse(@event.at('port').inner_text)
27
- end
28
-
29
- # Return the event severity.
30
- # @return [String]
31
- # Return the event severity.
32
- # @example
33
- # event.severity #=> 3
34
- # event.severity.in_words #=> "High Severity"
35
- # @see String#in_words
36
- def severity
37
- @severity ||= @event.at('severity').inner_text.to_i
38
- end
39
-
40
- # Return the event object nessus plugin id
41
- # @return [String]
42
- # Return the event object nessus plugin id
43
- # @example
44
- # event.plugin_id #=> 3245
45
- def plugin_id
46
- @plugin_id ||= @event.at('pluginID').inner_text.to_i
47
- end
48
-
49
- # Return the event name (plugin_name)
50
- # @return [String]
51
- # Return the event name (plugin_name)
52
- # @example
53
- # event.plugin_name #=> "PHP < 5.2.4 Multiple Vulnerabilities"
54
- # event.name #=> "PHP < 5.2.4 Multiple Vulnerabilities"
55
- def plugin_name
56
- s = @event.at('pluginName').inner_text
57
-
58
- @plugin_name ||= unless s.empty?
59
- @event.at('pluginName').inner_text || "N/A"
60
- else
61
- false
62
- end
63
-
64
- return @plugin_name
65
- end
66
- alias name plugin_name
67
-
68
- # Return the event plugin output data
69
- # @return [String]
70
- # Return the event plugin output data
71
- # @example
72
- # event.output #=> "..."
73
- # event.data #=> "..."
74
- def data
75
- d = "#{@event.at('data')}" || ""
76
-
77
- @data ||= unless d.empty?
78
- @event.at('data').inner_text || "N/A"
79
- else
80
- false
81
- end
82
- return @data
83
- end
84
- alias output data
85
-
86
- end
87
- end
@@ -1,254 +0,0 @@
1
- module Nessus
2
- class Host
3
- include Enumerable
4
-
5
- # Host
6
- attr_reader :host
7
-
8
- # Creates A New Host Object
9
- # @param [Object] Host Object
10
- # @example
11
- # Host.new(object)
12
- def initialize(host)
13
- @host = host
14
- end
15
-
16
- # Return the Host Object hostname.
17
- # @return [String]
18
- # The Host Object Hostname
19
- # @example
20
- # host.hostname #=> "127.0.0.1"
21
- def hostname
22
- @hostname ||= @host.at('HostName').inner_text
23
- end
24
-
25
- # Return the host scan start time.
26
- # @return [DateTime]
27
- # The Host Scan Start Time
28
- # @example
29
- # scan.scan_start_time #=> 'Fri Nov 11 23:36:54 1985'
30
- def scan_start_time
31
- if @host.at('startTime').inner_text.blank?
32
- return false
33
- else
34
- @host_scan_time = DateTime.strptime(@host.at('startTime').inner_text, fmt='%a %b %d %H:%M:%S %Y')
35
- end
36
- end
37
-
38
- # Return the host scan stop time.
39
- # @return [DateTime]
40
- # The Host Scan Stop Time
41
- # @example
42
- # scan.scan_start_time #=> 'Fri Nov 11 23:36:54 1985'
43
- def scan_stop_time
44
- if @host.at('stopTime').inner_text.blank?
45
- return false
46
- else
47
- @host_scan_time = DateTime.strptime(@host.at('stopTime').inner_text, fmt='%a %b %d %H:%M:%S %Y')
48
- end
49
- end
50
-
51
- # Return the host run time.
52
- # @return [String]
53
- # The Host Scan Run Time
54
- # @example
55
- # scan.scan_run_time #=> '2 hours 5 minutes and 16 seconds'
56
- def scan_runtime
57
- h = ("#{Time.parse(scan_stop_time.to_s).strftime('%H').to_i - Time.parse(scan_start_time.to_s).strftime('%H').to_i}").gsub('-', '')
58
- m = ("#{Time.parse(scan_stop_time.to_s).strftime('%M').to_i - Time.parse(scan_start_time.to_s).strftime('%M').to_i}").gsub('-', '')
59
- s = ("#{Time.parse(scan_stop_time.to_s).strftime('%S').to_i - Time.parse(scan_start_time.to_s).strftime('%S').to_i}").gsub('-', '')
60
- return "#{h} hours #{m} minutes and #{s} seconds"
61
- end
62
-
63
- # Return the Host Netbios Name.
64
- # @return [String]
65
- # The Host Netbios Name
66
- # @example
67
- # host.netbios_name #=> "SOMENAME4243"
68
- def netbios_name
69
- @netbios_name ||= @host.at('netbios_name').inner_text
70
- end
71
-
72
- # Return the Host Mac Address.
73
- # @return [String]
74
- # Return the Host Mac Address
75
- # @example
76
- # host.mac_addr #=> "00:11:22:33:44:55"
77
- def mac_addr
78
- @mac_addr ||= @host.at('mac_addr').inner_text
79
- end
80
- alias mac_address mac_addr
81
-
82
- # Return the Host DNS Name.
83
- # @return [String]
84
- # Return the Host DNS Name
85
- # @example
86
- # host.dns_name #=> "snorby.org"
87
- def dns_name
88
- @dns_name ||= @host.at('dns_name').inner_text
89
- end
90
-
91
- # Return the Host OS Name.
92
- # @return [String]
93
- # Return the Host OS Name
94
- # @example
95
- # host.dns_name #=> "Microsoft Windows 2000, Microsoft Windows Server 2003"
96
- def os_name
97
- @os_name ||= @host.at('os_name').inner_text
98
- end
99
- alias operating_system os_name
100
-
101
- # Return the open ports for a given host object.
102
- # @return [Integer]
103
- # Return the open ports for a given host object.
104
- # @example
105
- # host.open_ports #=> 213
106
- def open_ports
107
- @scanned_ports ||= @host.at('num_ports').inner_text.to_i
108
- end
109
-
110
- # Returns All Informational Event Objects For A Given Host.
111
- # @yield [prog] If a block is given, it will be passed the newly
112
- # created Event object.
113
- # @yieldparam [EVENT] prog The newly created Event object.
114
- # @return [Integer]
115
- # Return The Informational Event Count For A Given Host.
116
- # @example
117
- # host.informational_events do |info|
118
- # puts info.port
119
- # puts info.data if info.data
120
- # end
121
- def informational_events(&block)
122
- unless @informational_events
123
- @informational_events = []
124
- @informational_event_count = 0
125
-
126
- @host.xpath("ReportItem").each do |event|
127
- next if event.at('severity').inner_text.to_i != 0
128
- @informational_events << Event.new(event)
129
- @informational_event_count += 1
130
- end
131
-
132
- end
133
-
134
- @informational_events.each(&block)
135
- return @informational_event_count
136
- end
137
-
138
- # Returns All Low Event Objects For A Given Host.
139
- # @yield [prog] If a block is given, it will be passed the newly
140
- # created Event object.
141
- # @yieldparam [EVENT] prog The newly created Event object.
142
- # @return [Integer]
143
- # Return The Low Event Count For A Given Host.
144
- # @example
145
- # host.low_severity_events do |low|
146
- # puts low.name if low.name
147
- # end
148
- def low_severity_events(&block)
149
-
150
- @low_severity_count = @host.at('num_lo').inner_text.to_i
151
-
152
- unless @low_severity_events
153
- @low_severity_events = []
154
-
155
- @host.xpath("ReportItem").each do |event|
156
- next if event.at('severity').inner_text.to_i != 1
157
- @low_severity_events << Event.new(event)
158
- end
159
-
160
- end
161
-
162
- @low_severity_events.each(&block)
163
- return @low_severity_count
164
- end
165
-
166
- # Returns All Medium Event Objects For A Given Host.
167
- # @yield [prog] If a block is given, it will be passed the newly
168
- # created Event object.
169
- # @yieldparam [EVENT] prog The newly created Event object.
170
- # @return [Integer]
171
- # Return The Medium Event Count For A Given Host.
172
- # @example
173
- # host.medium_severity_events do |medium|
174
- # puts medium.name if medium.name
175
- # end
176
- def medium_severity_events(&block)
177
-
178
- @high_severity_count = @host.at('num_med').inner_text.to_i
179
-
180
- unless @medium_severity_events
181
- @medium_severity_events = []
182
-
183
- @host.xpath("ReportItem").each do |event|
184
- next if event.at('severity').inner_text.to_i != 2
185
- @medium_severity_events << Event.new(event)
186
- end
187
-
188
- end
189
-
190
- @medium_severity_events.each(&block)
191
- return @high_severity_count
192
- end
193
-
194
- # Returns All High Event Objects For A Given Host.
195
- # @yield [prog] If a block is given, it will be passed the newly
196
- # created Event object.
197
- # @yieldparam [EVENT] prog The newly created Event object.
198
- # @return [Integer]
199
- # Return The High Event Count For A Given Host.
200
- # @example
201
- # host.high_severity_events do |high|
202
- # puts high.name if high.name
203
- # end
204
- def high_severity_events(&block)
205
-
206
- @high_severity_count = @host.at('num_hi').inner_text.to_i
207
-
208
- unless @high_severity_events
209
- @high_severity_events = []
210
-
211
- @host.xpath("ReportItem").each do |event|
212
- next if event.at('severity').inner_text.to_i != 3
213
- @high_severity_events << Event.new(event)
214
- end
215
-
216
- end
217
-
218
- @high_severity_events.each(&block)
219
- return @high_severity_count
220
- end
221
-
222
- # Return the total event count for a given host.
223
- # @return [Integer]
224
- # Return the total event count for a given host.
225
- # @example
226
- # host.event_count #=> 3456
227
- def event_count
228
- ((low_severity_events.to_i) + (medium_severity_events.to_i) + (high_severity_events.to_i)).to_i
229
- end
230
-
231
- # Creates a new Event object to be parser
232
- # @yield [prog] If a block is given, it will be passed the newly
233
- # created Event object.
234
- # @yieldparam [EVENT] prog The newly created Event object.
235
- # @example
236
- # host.events do |event|
237
- # puts event.name if event.name
238
- # puts event.port
239
- # end
240
- def events(&block)
241
- @host.xpath("ReportItem").each do |event|
242
- block.call(Event.new(event)) if block
243
- end
244
- end
245
-
246
- # Parses the events of the host.
247
- # @return [Array<String>]
248
- # The events of the host.
249
- def all_events
250
- Enumerator.new(self,:events).to_a
251
- end
252
-
253
- end
254
- end
@@ -1,86 +0,0 @@
1
- module Nessus
2
- class Port
3
-
4
- # Port Service
5
- attr_reader :service
6
- # Port number
7
- attr_reader :number
8
- # Port Protocol
9
- attr_reader :protocol
10
- # Raw output string from nessus
11
- attr_reader :raw_string
12
-
13
- # Creates A New Port Object
14
- # @param [String] service The Port Service.
15
- # @param [Integer] number The Port number.
16
- # @param [String] protocol The Port protocol.
17
- # @param [String] raw output string from nessus.
18
- # @example
19
- # Port.new("ssh",22,"tcp", str)
20
- def initialize(service,number,protocol,raw_string)
21
- @service = service
22
- @number = number
23
- @protocol = protocol
24
- @raw_string = raw_string
25
- end
26
-
27
- # Parse A passed port string and return a Port Object.
28
- # @return [Object]
29
- # New Port Object
30
- # @example
31
- # Port.parse(port)
32
- def Port.parse(str)
33
- begin
34
- @full_port = str
35
- components = str.match(/^([^\(]+)\((\d+)\/([^\)]+)\)/)
36
-
37
- if components
38
- return Port.new(components[1].strip, components[2].strip, components[3].strip, str)
39
- else
40
- return Port.new(false, false, false, str)
41
- end
42
-
43
- end
44
-
45
- end
46
-
47
- # Return true iF port protocol Ii tcp.
48
- # @return [Boolean]
49
- # Return True If The Port Protocol Is TCP.
50
- def tcp?
51
- @protocol == 'tcp'
52
- end
53
-
54
- # Return true iF port protocol Ii udp.
55
- # @return [Boolean]
56
- # Return True If The Port Protocol Is UDP.
57
- def udp?
58
- @protocol == 'udp'
59
- end
60
-
61
- # Return the port as a string.
62
- # @return [String]
63
- # Return The Port As A String
64
- # @example
65
- # port.to_s #=> https (443/tcp)
66
- def to_s
67
- if @service && @number && @protocol
68
- "#{@service} (#{@number}/#{@protocol})"
69
- else
70
- "#{@raw_string}"
71
- end
72
- end
73
-
74
- # Return false if the port object number is nil
75
- # @return [Boolean]
76
- # Return false if the port object number is nil
77
- def number
78
- if @number
79
- return @number
80
- else
81
- false
82
- end
83
- end
84
-
85
- end
86
- end
@@ -1,298 +0,0 @@
1
- require 'ruby-nessus/host'
2
- require 'ruby-nessus/event'
3
-
4
- require 'nokogiri'
5
- require 'enumerator'
6
- require 'time'
7
-
8
- module Nessus
9
- # File to parse
10
- attr_reader :file
11
-
12
- class XML
13
-
14
- include Enumerable
15
-
16
- # Creates a new .Nessus (XML) object to be parser
17
- # @param [String] file The Nessus xml results file to parse.
18
- # @yield [prog] If a block is given, it will be passed the newly
19
- # created XML object.
20
- # @yieldparam [XML] prog The newly created XML object.
21
- # @example
22
- # Nessus::XML.new(nessus_scan_file) do |scan|
23
- # scan.report_name
24
- # end
25
- def initialize(file, &block)
26
-
27
- @file = File.open(file)
28
- @xml = Nokogiri::XML.parse(@file.read)
29
-
30
- block.call(self) if block
31
- end
32
-
33
- # Return the nessus report title.
34
- # @return [String]
35
- # The Nessus Report Title
36
- # @example
37
- # scan.report_name #=> "My Super Cool Nessus Report"
38
- def title
39
- @report_name ||= @xml.xpath("//NessusClientData//Report//ReportName").inner_text.split(' - ').last
40
- end
41
-
42
- # Return the nessus report time.
43
- # @return [String]
44
- # The Nessus Report Time
45
- # @example
46
- # scan.report_time #=> "09/11/08 02:21:22 AM"
47
- def time
48
- #09/11/08 02:21:22 AM
49
- datetime = @xml.xpath("//NessusClientData//Report//ReportName").inner_text.split(' - ').first
50
- @report_time ||= DateTime.strptime(datetime, fmt='%y/%m/%d %I:%M:%S %p')
51
- end
52
-
53
- # Return the scan start time.
54
- # @return [DateTime]
55
- # The Nessus Scan Start Time
56
- # @example
57
- # scan.start_time #=> 'Fri Nov 11 23:36:54 1985'
58
- def start_time
59
- @start_time = DateTime.strptime(@xml.xpath("//NessusClientData//Report//StartTime").inner_text, fmt='%a %b %d %H:%M:%S %Y')
60
- end
61
-
62
- # Return the scan stop time.
63
- # @return [DateTime]
64
- # The Nessus Scan Stop Time
65
- # @example
66
- # scan.stop_time #=> 'Mon Nov 11 23:36:54 1985'
67
- def stop_time
68
- @stop_time = DateTime.strptime(@xml.xpath("//NessusClientData//Report//StopTime").inner_text, fmt='%a %b %d %H:%M:%S %Y')
69
- end
70
-
71
- # Return the scan run time.
72
- # @return [String]
73
- # The Nessus Scan Run Time
74
- # @example
75
- # scan.runtime #=> '2 hours 5 minutes and 16 seconds'
76
- def runtime
77
- h = ("#{Time.parse(stop_time.to_s).strftime('%H').to_i - Time.parse(start_time.to_s).strftime('%H').to_i}").gsub('-', '')
78
- m = ("#{Time.parse(stop_time.to_s).strftime('%M').to_i - Time.parse(start_time.to_s).strftime('%M').to_i}").gsub('-', '')
79
- s = ("#{Time.parse(stop_time.to_s).strftime('%S').to_i - Time.parse(start_time.to_s).strftime('%S').to_i}").gsub('-', '')
80
- return "#{h} hours #{m} minutes and #{s} seconds"
81
- end
82
-
83
- # Return the nessus scan policy name. When creating a nessus policy this is usually the title field.
84
- # @return [String]
85
- # The Nessus Scan Policy Name
86
- def policy_title
87
- @policy_name ||= @xml.xpath("//NessusClientData//Report//policyName").inner_text
88
- end
89
-
90
- # Return the nessus scan policy comments. This is the description field when creating a new policy with the Nessus GUI client.
91
- # @return [String]
92
- # The Nessus Scan Policy Comments
93
- def policy_notes
94
- @policy_comments ||= @xml.xpath("//NessusClientData//Report//policyComments").inner_text
95
- end
96
-
97
- # Returns and array of the plugin ids userd for the passed .nessus scan.
98
- # @return [Array]
99
- # The Nessus Scan Plugin Ids
100
- # @example
101
- # scan.plugin_ids #=> [1234,2343,9742,5452,5343,2423,1233]
102
- def plugin_ids
103
- unless @plugin_ids
104
- @plugin_ids = []
105
-
106
- @xml.xpath("//PluginSelection").last.text.split(';').each do |id|
107
- @plugin_ids << id
108
- end
109
- end
110
-
111
- @plugin_ids
112
- end
113
-
114
- # Returns and array of the plugin names userd for the passed .nessus scan.
115
- # @return [Array]
116
- # The Nessus Scan Plugin Names
117
- # @example
118
- # scan.plugins #=> ["PHP < 5.2.1 Multiple Vulnerabilities", "PHP < 4.4.1 / 5.0.6 Multiple Vulnerabilities"]
119
- def plugins
120
- unless @plugins
121
- # get elements with attribute:
122
- @plugins = []
123
-
124
- @xml.xpath("//pluginName").each do |x|
125
- @plugins << x.inner_text unless x.inner_text.empty?
126
- end
127
-
128
- @plugins.uniq!
129
- @plugins.sort!
130
- end
131
-
132
- return @plugins
133
- end
134
-
135
- # Creates a new Host object to be parser
136
- # @yield [prog] If a block is given, it will be passed the newly
137
- # created Host object.
138
- # @yieldparam [XML] prog The newly created Host object.
139
- # @example
140
- # scan.hosts do |host|
141
- # puts host.hostname
142
- # end
143
- def hosts(&block)
144
- hosts = []
145
- @xml.xpath("//ReportHost").each do |host|
146
- hosts << host.at('HostName').inner_text if host.at('HostName').inner_text
147
- block.call(Host.new(host)) if block
148
- end
149
- hosts
150
- end
151
-
152
- # Parses the hosts of the scan.
153
- # @return [Array<String>]
154
- # The Hosts of the scan.
155
- def all_hosts
156
- Enumerator.new(self,:hosts).to_a
157
- end
158
-
159
- # Return the nessus scan host count.
160
- # @return [Integer]
161
- # The Nessus Scan Host Count
162
- # @example
163
- # scan.host_count #=> 23
164
- def host_count
165
- hosts.size
166
- end
167
-
168
- # Retunrs an array of all unique ports.
169
- # @return [Array]
170
- # @example
171
- # scan.unique_ports #=> 234
172
- def unique_ports
173
- unless @unique_ports
174
- @unique_ports = []
175
- @xml.xpath("//ReportItem//port").each do |port|
176
- @unique_ports << port.inner_text
177
- end
178
- @unique_ports.uniq!
179
- @unique_ports.sort!
180
- end
181
- end
182
-
183
- # Return the informational severity count.
184
- # @return [Integer]
185
- # The Informational Severity Count
186
- # @example
187
- # scan.informational_severity_count #=> 1203
188
- def open_ports_count
189
- count_severity[:open_ports].to_i
190
- end
191
-
192
- # Return the High severity count.
193
- # @return [Integer]
194
- # The High Severity Count
195
- # @example
196
- # scan.high_severity_count #=> 10
197
- def high_severity_count
198
- count_severity[:high].to_i
199
- end
200
-
201
- # Return the Medium severity count.
202
- # @return [Integer]
203
- # The Medium Severity Count
204
- # @example
205
- # scan.medium_severity_count #=> 234
206
- def medium_severity_count
207
- count_severity[:medium].to_i
208
- end
209
-
210
- # Return the Low severity count.
211
- # @return [Integer]
212
- # The Low Severity Count
213
- # @example
214
- # scan.low_severity_count #=> 114
215
- def low_severity_count
216
- count_severity[:low].to_i
217
- end
218
-
219
- # Return the Total severity count. [high, medium, low, informational]
220
- # @return [Integer]
221
- # The Total Severity Count
222
- # @example
223
- # scan.total_event_count #=> 1561
224
- def total_event_count
225
- count_severity[:all].to_i
226
- end
227
-
228
- # Return the Total severity count.
229
- # @param [String] severity the severity in which to calculate percentage for.
230
- # @param [Boolean] round round the result to the nearest whole number.
231
- # @raise [ExceptionClass] One of the following severity options must be passed. [high, medium, low, informational, all]
232
- # @return [Integer]
233
- # The Percentage Of Events For A Passed Severity
234
- # @example
235
- # scan.event_percentage_for("low", true) #=> 11%
236
- def event_percentage_for(type, round_percentage=false)
237
- @sc ||= count_severity
238
- if %W(high medium low all).include?(type)
239
- calc = ((@sc[:"#{type}"].to_f / @sc[:all].to_f) * 100)
240
- if round_percentage
241
- return "#{calc.round}"
242
- else
243
- return "#{calc}"
244
- end
245
- else
246
- raise "Error: #{type} is not an acceptable severity. Possible options include: all, high, medium, low and informational."
247
- end
248
- end
249
-
250
- # Creates a new Host object to be parser from a passed search param.
251
- # @param [String] hostname the hostname to build a Host object for.
252
- # @yield [prog] If a block is given, it will be passed the newly
253
- # created Host object.
254
- # @yieldparam [XML] prog The newly created Host object.
255
- # @example
256
- # scan.find_by_hostname('127.0.0.1') do |host|
257
- # puts host.hostname
258
- # end
259
- def find_by_hostname(hostname, &block)
260
- raise "Error: hostname can't be blank." if hostname.blank?
261
- @xml.xpath('//ReportHost[HostName]').each do |host|
262
- next unless host.inner_text.match(hostname)
263
- block.call(Host.new(host)) if block
264
- end
265
- end
266
-
267
- private
268
-
269
- # Calculates an event hash of totals for severity counts.
270
- # @return [hash]
271
- # The Event Totals For Severity
272
- def count_severity
273
- unless @count
274
- @count = {}
275
- @open_ports = 0
276
- @low = 0
277
- @medium = 0
278
- @high = 0
279
-
280
- @xml.xpath("//ReportHost").each do |s|
281
- @open_ports += s.at('num_ports').inner_text.to_i
282
- @low += s.at('num_lo').inner_text.to_i
283
- @medium += s.at('num_med').inner_text.to_i
284
- @high += s.at('num_hi').inner_text.to_i
285
- end
286
-
287
- @count = { :open_ports => @open_ports,
288
- :low => @low,
289
- :medium => @medium,
290
- :high => @high,
291
- :all => (@low + @medium + @high) }
292
- end
293
-
294
- return @count
295
- end
296
-
297
- end
298
- end