nexpose 0.2.8 → 0.5.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.
- checksums.yaml +4 -4
- data/lib/nexpose.rb +18 -9
- data/lib/nexpose/ajax.rb +127 -0
- data/lib/nexpose/alert.rb +29 -36
- data/lib/nexpose/common.rb +13 -12
- data/lib/nexpose/connection.rb +18 -13
- data/lib/nexpose/creds.rb +16 -55
- data/lib/nexpose/dag.rb +73 -0
- data/lib/nexpose/data_table.rb +134 -0
- data/lib/nexpose/device.rb +111 -0
- data/lib/nexpose/engine.rb +194 -0
- data/lib/nexpose/filter.rb +341 -0
- data/lib/nexpose/group.rb +33 -37
- data/lib/nexpose/manage.rb +4 -0
- data/lib/nexpose/pool.rb +142 -0
- data/lib/nexpose/report.rb +72 -278
- data/lib/nexpose/report_template.rb +249 -0
- data/lib/nexpose/scan.rb +196 -54
- data/lib/nexpose/scan_template.rb +103 -0
- data/lib/nexpose/site.rb +91 -237
- data/lib/nexpose/ticket.rb +173 -119
- data/lib/nexpose/user.rb +11 -3
- data/lib/nexpose/vuln.rb +81 -338
- data/lib/nexpose/vuln_exception.rb +368 -0
- metadata +12 -4
- data/lib/nexpose/misc.rb +0 -35
- data/lib/nexpose/scan_engine.rb +0 -325
@@ -0,0 +1,249 @@
|
|
1
|
+
module Nexpose
|
2
|
+
|
3
|
+
# NexposeAPI module is mixed into the Connection object, and all methods are
|
4
|
+
# expected to be called from there.
|
5
|
+
#
|
6
|
+
module NexposeAPI
|
7
|
+
include XMLUtils
|
8
|
+
|
9
|
+
# Provide a list of all report templates the user can access on the
|
10
|
+
# Security Console.
|
11
|
+
#
|
12
|
+
# @return [Array[ReportTemplateSummary]] List of current report templates.
|
13
|
+
#
|
14
|
+
def list_report_templates
|
15
|
+
r = execute(make_xml('ReportTemplateListingRequest', {}))
|
16
|
+
templates = []
|
17
|
+
if r.success
|
18
|
+
r.res.elements.each('//ReportTemplateSummary') do |template|
|
19
|
+
templates << ReportTemplateSummary.parse(template)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
templates
|
23
|
+
end
|
24
|
+
|
25
|
+
alias_method :report_templates, :list_report_templates
|
26
|
+
|
27
|
+
# Deletes an existing, custom report template.
|
28
|
+
# Cannot delete built-in templates.
|
29
|
+
#
|
30
|
+
# @param [String] template_id Unique identifier of the report template to remove.
|
31
|
+
#
|
32
|
+
def delete_report_template(template_id)
|
33
|
+
AJAX.delete(self, "/data/report/templates/#{template_id}")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Data object for report template summary information.
|
38
|
+
# Not meant for use in creating new templates.
|
39
|
+
#
|
40
|
+
class ReportTemplateSummary
|
41
|
+
|
42
|
+
# The ID of the report template.
|
43
|
+
attr_reader :id
|
44
|
+
# The name of the report template.
|
45
|
+
attr_reader :name
|
46
|
+
# One of: data|document. With a data template, you can export
|
47
|
+
# comma-separated value (CSV) files with vulnerability-based data.
|
48
|
+
# With a document template, you can create PDF, RTF, HTML, or XML reports
|
49
|
+
# with asset-based information.
|
50
|
+
attr_reader :type
|
51
|
+
# The visibility (scope) of the report template. One of: global|silo
|
52
|
+
attr_reader :scope
|
53
|
+
# Whether the report template is built-in, and therefore cannot be modified.
|
54
|
+
attr_reader :built_in
|
55
|
+
# Description of the report template.
|
56
|
+
attr_reader :description
|
57
|
+
|
58
|
+
def initialize(id, name, type, scope, built_in, description)
|
59
|
+
@id = id
|
60
|
+
@name = name
|
61
|
+
@type = type
|
62
|
+
@scope = scope
|
63
|
+
@built_in = built_in
|
64
|
+
@description = description
|
65
|
+
end
|
66
|
+
|
67
|
+
def delete(connection)
|
68
|
+
connection.delete_report_template(@id)
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.parse(xml)
|
72
|
+
description = nil
|
73
|
+
xml.elements.each('description') { |desc| description = desc.text }
|
74
|
+
ReportTemplateSummary.new(xml.attributes['id'],
|
75
|
+
xml.attributes['name'],
|
76
|
+
xml.attributes['type'],
|
77
|
+
xml.attributes['scope'],
|
78
|
+
xml.attributes['builtin'] == '1',
|
79
|
+
description)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Definition object for a report template.
|
84
|
+
#
|
85
|
+
class ReportTemplate
|
86
|
+
|
87
|
+
# The ID of the report template.
|
88
|
+
attr_accessor :id
|
89
|
+
# The name of the report template.
|
90
|
+
attr_accessor :name
|
91
|
+
# With a data template, you can export comma-separated value (CSV) files
|
92
|
+
# with vulnerability-based data. With a document template, you can create
|
93
|
+
# PDF, RTF, HTML, or XML reports with asset-based information. When you
|
94
|
+
# retrieve a report template, the type will always be visible even though
|
95
|
+
# type is implied. When ReportTemplate is sent as a request, and the type
|
96
|
+
# attribute is not provided, the type attribute defaults to document,
|
97
|
+
# allowing for backward compatibility with existing API clients.
|
98
|
+
attr_accessor :type
|
99
|
+
# The visibility (scope) of the report template.
|
100
|
+
# One of: global|silo
|
101
|
+
attr_accessor :scope
|
102
|
+
# The report template is built-in, and cannot be modified.
|
103
|
+
attr_accessor :built_in
|
104
|
+
# Description of this report template.
|
105
|
+
attr_accessor :description
|
106
|
+
|
107
|
+
# Array of report sections.
|
108
|
+
attr_accessor :sections
|
109
|
+
# Map of report properties.
|
110
|
+
attr_accessor :properties
|
111
|
+
# Array of report attributes, in the order they will be present in a report.
|
112
|
+
attr_accessor :attributes
|
113
|
+
# Display asset names with IPs.
|
114
|
+
attr_accessor :show_device_names
|
115
|
+
|
116
|
+
def initialize(name, type = 'document', id = -1, scope = 'silo', built_in = false)
|
117
|
+
@name = name
|
118
|
+
@type = type
|
119
|
+
@id = id
|
120
|
+
@scope = scope
|
121
|
+
@built_in = built_in
|
122
|
+
|
123
|
+
@sections = []
|
124
|
+
@properties = {}
|
125
|
+
@attributes = []
|
126
|
+
@show_device_names = false
|
127
|
+
end
|
128
|
+
|
129
|
+
# Save the configuration for a report template.
|
130
|
+
def save(connection)
|
131
|
+
xml = %(<ReportTemplateSaveRequest session-id='#{connection.session_id}' scope='#{@scope}'>)
|
132
|
+
xml << to_xml
|
133
|
+
xml << '</ReportTemplateSaveRequest>'
|
134
|
+
response = connection.execute(xml)
|
135
|
+
if response.success
|
136
|
+
@id = response.attributes['template-id']
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Retrieve the configuration for a report template.
|
141
|
+
def self.load(connection, template_id)
|
142
|
+
xml = %(<ReportTemplateConfigRequest session-id='#{connection.session_id}' template-id='#{template_id}'/>)
|
143
|
+
ReportTemplate.parse(connection.execute(xml))
|
144
|
+
end
|
145
|
+
|
146
|
+
def delete(connection)
|
147
|
+
connection.delete_report_template(@id)
|
148
|
+
end
|
149
|
+
|
150
|
+
include Sanitize
|
151
|
+
|
152
|
+
def to_xml
|
153
|
+
xml = %(<ReportTemplate id='#{@id}' name='#{@name}' type='#{@type}')
|
154
|
+
xml << %( scope='#{@scope}') if @scope
|
155
|
+
xml << %( builtin='#{@built_in}') if @built_in
|
156
|
+
xml << '>'
|
157
|
+
xml << %(<description>#{@description}</description>) if @description
|
158
|
+
|
159
|
+
unless @attributes.empty?
|
160
|
+
xml << '<ReportAttributes>'
|
161
|
+
@attributes.each do |attr|
|
162
|
+
xml << %(<ReportAttribute name='#{attr}'/>)
|
163
|
+
end
|
164
|
+
xml << '</ReportAttributes>'
|
165
|
+
end
|
166
|
+
|
167
|
+
unless @sections.empty?
|
168
|
+
xml << '<ReportSections>'
|
169
|
+
properties.each_pair do |name, value|
|
170
|
+
xml << %(<property name='#{name}'>#{replace_entities(value)}</property>)
|
171
|
+
end
|
172
|
+
@sections.each { |section| xml << section.to_xml }
|
173
|
+
xml << '</ReportSections>'
|
174
|
+
end
|
175
|
+
|
176
|
+
xml << %(<Settings><showDeviceNames enabled='#{@show_device_names ? 1 : 0}' /></Settings>)
|
177
|
+
xml << '</ReportTemplate>'
|
178
|
+
end
|
179
|
+
|
180
|
+
def self.parse(xml)
|
181
|
+
xml.res.elements.each('//ReportTemplate') do |tmp|
|
182
|
+
template = ReportTemplate.new(tmp.attributes['name'],
|
183
|
+
tmp.attributes['type'],
|
184
|
+
tmp.attributes['id'],
|
185
|
+
tmp.attributes['scope'] || 'silo',
|
186
|
+
tmp.attributes['builtin'])
|
187
|
+
tmp.elements.each('//description') do |desc|
|
188
|
+
template.description = desc.text
|
189
|
+
end
|
190
|
+
|
191
|
+
tmp.elements.each('//ReportAttributes/ReportAttribute') do |attr|
|
192
|
+
template.attributes << attr.attributes['name']
|
193
|
+
end
|
194
|
+
|
195
|
+
tmp.elements.each('//ReportSections/property') do |property|
|
196
|
+
template.properties[property.attributes['name']] = property.text
|
197
|
+
end
|
198
|
+
|
199
|
+
tmp.elements.each('//ReportSection') do |section|
|
200
|
+
template.sections << Section.parse(section)
|
201
|
+
end
|
202
|
+
|
203
|
+
tmp.elements.each('//showDeviceNames') do |show|
|
204
|
+
template.show_device_names = show.attributes['enabled'] == '1'
|
205
|
+
end
|
206
|
+
|
207
|
+
return template
|
208
|
+
end
|
209
|
+
nil
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# Section specific content to include in a report template.
|
214
|
+
#
|
215
|
+
class Section
|
216
|
+
|
217
|
+
# Name of the report section.
|
218
|
+
attr_accessor :name
|
219
|
+
# Map of properties specific to the report section.
|
220
|
+
attr_accessor :properties
|
221
|
+
|
222
|
+
def initialize(name)
|
223
|
+
@name = name
|
224
|
+
@properties = {}
|
225
|
+
end
|
226
|
+
|
227
|
+
include Sanitize
|
228
|
+
|
229
|
+
def to_xml
|
230
|
+
xml = %(<ReportSection name='#{@name}'>)
|
231
|
+
properties.each_pair do |name, value|
|
232
|
+
xml << %(<property name='#{name}'>#{replace_entities(value)}</property>)
|
233
|
+
end
|
234
|
+
xml << '</ReportSection>'
|
235
|
+
end
|
236
|
+
|
237
|
+
def self.parse(xml)
|
238
|
+
name = xml.attributes['name']
|
239
|
+
xml.elements.each("//ReportSection[@name='#{name}']") do |elem|
|
240
|
+
section = Section.new(name)
|
241
|
+
elem.elements.each("//ReportSection[@name='#{name}']/property") do |property|
|
242
|
+
section.properties[property.attributes['name']] = property.text
|
243
|
+
end
|
244
|
+
return section
|
245
|
+
end
|
246
|
+
nil
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
data/lib/nexpose/scan.rb
CHANGED
@@ -2,12 +2,141 @@ module Nexpose
|
|
2
2
|
module NexposeAPI
|
3
3
|
include XMLUtils
|
4
4
|
|
5
|
+
# Perform an ad hoc scan of a single device.
|
6
|
+
#
|
7
|
+
# @param [Device] device Device to scan.
|
8
|
+
# @return [Scan] Scan launch information.
|
9
|
+
#
|
10
|
+
def scan_device(device)
|
11
|
+
scan_devices([device])
|
12
|
+
end
|
13
|
+
|
14
|
+
# Perform an ad hoc scan of a subset of devices for a site.
|
15
|
+
# Nexpose only allows devices from a single site to be submitted per
|
16
|
+
# request.
|
17
|
+
# Method is designed to take objects from a Device listing.
|
18
|
+
#
|
19
|
+
# For example:
|
20
|
+
# devices = nsc.devices(5)
|
21
|
+
# nsc.scan_devices(devices.take(10))
|
22
|
+
#
|
23
|
+
# @param [Array[Device]] devices List of devices to scan.
|
24
|
+
# @return [Scan] Scan launch information.
|
25
|
+
#
|
26
|
+
def scan_devices(devices)
|
27
|
+
site_id = devices.map { |d| d.site_id }.uniq.first
|
28
|
+
xml = make_xml('SiteDevicesScanRequest', { 'site-id' => site_id })
|
29
|
+
elem = REXML::Element.new('Devices')
|
30
|
+
devices.each do |device|
|
31
|
+
elem.add_element('device', { 'id' => "#{device.id}" })
|
32
|
+
end
|
33
|
+
xml.add_element(elem)
|
34
|
+
|
35
|
+
_scan_ad_hoc(xml)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Perform an ad hoc scan of a single asset of a site.
|
39
|
+
#
|
40
|
+
# @param [Fixnum] site_id Site ID that the assets belong to.
|
41
|
+
# @param [HostName|IPRange] asset Asset to scan.
|
42
|
+
# @return [Scan] Scan launch information.
|
43
|
+
#
|
44
|
+
def scan_asset(site_id, asset)
|
45
|
+
scan_assets(site_id, [asset])
|
46
|
+
end
|
47
|
+
|
48
|
+
# Perform an ad hoc scan of a subset of assets for a site.
|
49
|
+
# Only assets from a single site should be submitted per request.
|
50
|
+
# Method is designed to take objects filtered from Site#assets.
|
51
|
+
#
|
52
|
+
# For example:
|
53
|
+
# site = Site.load(nsc, 5)
|
54
|
+
# nsc.scan_assets(5, site.assets.take(10))
|
55
|
+
#
|
56
|
+
# @param [Fixnum] site_id Site ID that the assets belong to.
|
57
|
+
# @param [Array[HostName|IPRange]] assets List of assets to scan.
|
58
|
+
# @return [Scan] Scan launch information.
|
59
|
+
#
|
60
|
+
def scan_assets(site_id, assets)
|
61
|
+
xml = make_xml('SiteDevicesScanRequest', { 'site-id' => site_id })
|
62
|
+
hosts = REXML::Element.new('Hosts')
|
63
|
+
assets.each { |asset| _append_asset!(hosts, asset) }
|
64
|
+
xml.add_element(hosts)
|
65
|
+
|
66
|
+
_scan_ad_hoc(xml)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Perform an ad hoc scan of a subset of IP addresses for a site.
|
70
|
+
# Only IPs from a single site can be submitted per request,
|
71
|
+
# and IP addresses must already be included in the site configuration.
|
72
|
+
# Method is designed for scanning when the targets are coming from an
|
73
|
+
# external source that does not have access to internal identfiers.
|
74
|
+
#
|
75
|
+
# For example:
|
76
|
+
# to_scan = ['192.168.2.1', '192.168.2.107']
|
77
|
+
# nsc.scan_ips(5, to_scan)
|
78
|
+
#
|
79
|
+
# @param [Fixnum] site_id Site ID that the assets belong to.
|
80
|
+
# @param [Array[String]] ip_addresses Array of IP addresses to scan.
|
81
|
+
# @return [Scan] Scan launch information.
|
82
|
+
#
|
83
|
+
def scan_ips(site_id, ip_addresses)
|
84
|
+
xml = make_xml('SiteDevicesScanRequest', { 'site-id' => site_id })
|
85
|
+
hosts = REXML::Element.new('Hosts')
|
86
|
+
ip_addresses.each do |ip|
|
87
|
+
xml.add_element('range', { 'from' => ip })
|
88
|
+
end
|
89
|
+
xml.add_element(hosts)
|
90
|
+
|
91
|
+
_scan_ad_hoc(xml)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Initiate a site scan.
|
95
|
+
#
|
96
|
+
# @param [Fixnum] site_id Site ID to scan.
|
97
|
+
# @return [Scan] Scan launch information.
|
98
|
+
#
|
99
|
+
def scan_site(site_id)
|
100
|
+
xml = make_xml('SiteScanRequest', { 'site-id' => site_id })
|
101
|
+
response = execute(xml)
|
102
|
+
Scan.parse(response.res) if response.success
|
103
|
+
end
|
104
|
+
|
105
|
+
# Utility method for appending a HostName or IPRange object into an
|
106
|
+
# XML object, in preparation for ad hoc scanning.
|
107
|
+
#
|
108
|
+
# @param [REXML::Document] xml Prepared API call to execute.
|
109
|
+
# @param [HostName|IPRange] asset Asset to append to XML.
|
110
|
+
#
|
111
|
+
def _append_asset!(xml, asset)
|
112
|
+
if asset.kind_of? Nexpose::IPRange
|
113
|
+
xml.add_element('range', { 'from' => asset.from, 'to' => asset.to })
|
114
|
+
else # Assume HostName
|
115
|
+
host = REXML::Element.new('host')
|
116
|
+
host.text = asset.host
|
117
|
+
xml.add_element(host)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Utility method for executing prepared XML and extracting Scan launch
|
122
|
+
# information.
|
123
|
+
#
|
124
|
+
# @param [REXML::Document] xml Prepared API call to execute.
|
125
|
+
# @return [Scan] Scan launch information.
|
126
|
+
#
|
127
|
+
def _scan_ad_hoc(xml)
|
128
|
+
r = execute(xml)
|
129
|
+
Scan.parse(r.res)
|
130
|
+
end
|
131
|
+
|
5
132
|
# Stop a running or paused scan.
|
6
133
|
#
|
7
134
|
# @param [Fixnum] scan_id ID of the scan to stop.
|
8
|
-
# @param [Fixnum] wait_sec Number of seconds to wait for status to be
|
9
|
-
|
10
|
-
|
135
|
+
# @param [Fixnum] wait_sec Number of seconds to wait for status to be
|
136
|
+
# updated.
|
137
|
+
#
|
138
|
+
def stop_scan(scan_id, wait_sec = 0)
|
139
|
+
r = execute(make_xml('ScanStopRequest', { 'scan-id' => scan_id }))
|
11
140
|
if r.success
|
12
141
|
so_far = 0
|
13
142
|
while so_far < wait_sec
|
@@ -20,36 +149,36 @@ module Nexpose
|
|
20
149
|
r.success
|
21
150
|
end
|
22
151
|
|
23
|
-
|
24
|
-
|
152
|
+
# Retrieve the status of a scan.
|
153
|
+
#
|
154
|
+
# @param [Fixnum] scan_id The scan ID.
|
155
|
+
# @return [String] Current status of the scan.
|
156
|
+
#
|
157
|
+
def scan_status(scan_id)
|
158
|
+
r = execute(make_xml('ScanStatusRequest', { 'scan-id' => scan_id }))
|
25
159
|
r.success ? r.attributes['status'] : nil
|
26
160
|
end
|
27
161
|
|
28
|
-
#----------------------------------------------------------------
|
29
162
|
# Resumes a scan.
|
30
163
|
#
|
31
|
-
# @param scan_id The scan ID.
|
32
|
-
#
|
33
|
-
|
34
|
-
|
35
|
-
r = execute(make_xml('ScanResumeRequest', {'scan-id' => scan_id}))
|
164
|
+
# @param [Fixnum] scan_id The scan ID.
|
165
|
+
#
|
166
|
+
def resume_scan(scan_id)
|
167
|
+
r = execute(make_xml('ScanResumeRequest', { 'scan-id' => scan_id }))
|
36
168
|
r.success ? r.attributes['success'] : nil
|
37
169
|
end
|
38
170
|
|
39
|
-
|
40
|
-
#----------------------------------------------------------------
|
41
171
|
# Pauses a scan.
|
42
172
|
#
|
43
|
-
# @param scan_id The scan ID.
|
44
|
-
#
|
45
|
-
|
46
|
-
|
47
|
-
r = execute(make_xml('ScanPauseRequest',{ 'scan-id' => scan_id}))
|
173
|
+
# @param [Fixnum] scan_id The scan ID.
|
174
|
+
#
|
175
|
+
def pause_scan(scan_id)
|
176
|
+
r = execute(make_xml('ScanPauseRequest', { 'scan-id' => scan_id }))
|
48
177
|
r.success ? r.attributes['success'] : nil
|
49
178
|
end
|
50
179
|
|
51
|
-
# Retrieve a list of current scan activities across all Scan Engines
|
52
|
-
# by Nexpose.
|
180
|
+
# Retrieve a list of current scan activities across all Scan Engines
|
181
|
+
# managed by Nexpose.
|
53
182
|
#
|
54
183
|
# @return [Array[ScanSummary]] Array of ScanSummary objects associated with
|
55
184
|
# each active scan on the engines.
|
@@ -67,10 +196,11 @@ module Nexpose
|
|
67
196
|
|
68
197
|
# Get scan statistics, including node and vulnerability breakdowns.
|
69
198
|
#
|
199
|
+
# @param [Fixnum] scan_id Scan ID to retrieve statistics for.
|
70
200
|
# @return [ScanSummary] ScanSummary object providing statistics for the scan.
|
71
201
|
#
|
72
202
|
def scan_statistics(scan_id)
|
73
|
-
r = execute(make_xml('ScanStatisticsRequest', {'scan-id' => scan_id}))
|
203
|
+
r = execute(make_xml('ScanStatisticsRequest', { 'scan-id' => scan_id }))
|
74
204
|
if r.success
|
75
205
|
ScanSummary.parse(r.res.elements['//ScanSummary'])
|
76
206
|
else
|
@@ -79,7 +209,6 @@ module Nexpose
|
|
79
209
|
end
|
80
210
|
end
|
81
211
|
|
82
|
-
# === Description
|
83
212
|
# Object that represents a summary of a scan.
|
84
213
|
#
|
85
214
|
class ScanSummary
|
@@ -129,28 +258,33 @@ module Nexpose
|
|
129
258
|
start_time = nil
|
130
259
|
unless xml.attributes['startTime'] == ''
|
131
260
|
start_time = DateTime.parse(xml.attributes['startTime'].to_s).to_time
|
261
|
+
# Timestamp is UTC, but parsed as local time.
|
262
|
+
start_time -= start_time.gmt_offset
|
132
263
|
end
|
133
264
|
|
134
265
|
# End time is often not present, since reporting on running scans.
|
135
266
|
end_time = nil
|
136
267
|
if xml.attributes['endTime']
|
137
268
|
end_time = DateTime.parse(xml.attributes['endTime'].to_s).to_time
|
269
|
+
# Timestamp is UTC, but parsed as local time.
|
270
|
+
end_time -= end_time.gmt_offset
|
138
271
|
end
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
272
|
+
ScanSummary.new(xml.attributes['scan-id'].to_i,
|
273
|
+
xml.attributes['site-id'].to_i,
|
274
|
+
xml.attributes['engine-id'].to_i,
|
275
|
+
xml.attributes['status'],
|
276
|
+
start_time,
|
277
|
+
end_time,
|
278
|
+
msg,
|
279
|
+
tasks,
|
280
|
+
nodes,
|
281
|
+
vulns)
|
149
282
|
end
|
150
283
|
|
151
284
|
# Value class to tracking task counts.
|
152
285
|
#
|
153
286
|
class Tasks
|
287
|
+
|
154
288
|
attr_reader :pending, :active, :completed
|
155
289
|
|
156
290
|
def initialize(pending, active, completed)
|
@@ -164,15 +298,16 @@ module Nexpose
|
|
164
298
|
#
|
165
299
|
def self.parse(rexml)
|
166
300
|
return nil unless rexml
|
167
|
-
|
168
|
-
|
169
|
-
|
301
|
+
Tasks.new(rexml.attributes['pending'].to_i,
|
302
|
+
rexml.attributes['active'].to_i,
|
303
|
+
rexml.attributes['completed'].to_i)
|
170
304
|
end
|
171
305
|
end
|
172
306
|
|
173
307
|
# Value class for tracking node counts.
|
174
308
|
#
|
175
309
|
class Nodes
|
310
|
+
|
176
311
|
attr_reader :live, :dead, :filtered, :unresolved, :other
|
177
312
|
|
178
313
|
def initialize(live, dead, filtered, unresolved, other)
|
@@ -186,20 +321,21 @@ module Nexpose
|
|
186
321
|
#
|
187
322
|
def self.parse(rexml)
|
188
323
|
return nil unless rexml
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
324
|
+
Nodes.new(rexml.attributes['live'].to_i,
|
325
|
+
rexml.attributes['dead'].to_i,
|
326
|
+
rexml.attributes['filtered'].to_i,
|
327
|
+
rexml.attributes['unresolved'].to_i,
|
328
|
+
rexml.attributes['other'].to_i)
|
194
329
|
end
|
195
330
|
end
|
196
331
|
|
197
332
|
# Value class for tracking vulnerability counts.
|
198
333
|
#
|
199
334
|
class Vulnerabilities
|
335
|
+
|
200
336
|
attr_reader :vuln_exploit, :vuln_version, :vuln_potential,
|
201
|
-
|
202
|
-
|
337
|
+
:not_vuln_exploit, :not_vuln_version,
|
338
|
+
:error, :disabled, :other
|
203
339
|
|
204
340
|
def initialize(vuln_exploit, vuln_version, vuln_potential,
|
205
341
|
not_vuln_exploit, not_vuln_version,
|
@@ -246,6 +382,7 @@ module Nexpose
|
|
246
382
|
# and vuln-potential.
|
247
383
|
#
|
248
384
|
class Status
|
385
|
+
|
249
386
|
attr_reader :severities, :count
|
250
387
|
|
251
388
|
def initialize(severity = nil, count = 0)
|
@@ -269,19 +406,24 @@ module Nexpose
|
|
269
406
|
end
|
270
407
|
end
|
271
408
|
|
272
|
-
#
|
273
|
-
# <scanFilter scanStop='0' scanFailed='0' scanStart='1'/>
|
274
|
-
# === Description
|
409
|
+
# Struct class for tracking scan launch information.
|
275
410
|
#
|
276
|
-
class
|
277
|
-
|
278
|
-
|
279
|
-
attr_reader :
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
@
|
411
|
+
class Scan
|
412
|
+
|
413
|
+
# The scan ID when a scan is successfully launched.
|
414
|
+
attr_reader :id
|
415
|
+
# The engine the scan was dispatched to.
|
416
|
+
attr_reader :engine
|
417
|
+
|
418
|
+
def initialize(scan_id, engine_id)
|
419
|
+
@id, @engine = scan_id, engine_id
|
420
|
+
end
|
421
|
+
|
422
|
+
def self.parse(xml)
|
423
|
+
xml.elements.each('//Scan') do |scan|
|
424
|
+
return new(scan.attributes['scan-id'].to_i,
|
425
|
+
scan.attributes['engine-id'].to_i)
|
426
|
+
end
|
285
427
|
end
|
286
428
|
end
|
287
429
|
end
|