ruby-nessus 0.1.4 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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