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,103 @@
|
|
1
|
+
module Nexpose
|
2
|
+
module NexposeAPI
|
3
|
+
|
4
|
+
# List the scan templates currently configured on the console.
|
5
|
+
#
|
6
|
+
# @return [Array[String]] list of scan templates IDs.
|
7
|
+
#
|
8
|
+
def list_scan_templates
|
9
|
+
templates = JSON.parse(AJAX.get(self, '/data/scan/templates'))
|
10
|
+
templates['valueList']
|
11
|
+
end
|
12
|
+
|
13
|
+
alias_method :scan_templates, :list_scan_templates
|
14
|
+
|
15
|
+
# Delete a scan template from the console.
|
16
|
+
# Cannot be used to delete a built-in template.
|
17
|
+
#
|
18
|
+
# @param [String] id Unique identifier of an existing scan template.
|
19
|
+
#
|
20
|
+
def delete_scan_template(id)
|
21
|
+
AJAX.delete(self, "/data/scan/templates/#{URI.encode(id)}")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Configuration object for a scan template.
|
26
|
+
# This class is only a partial representation of some of the features
|
27
|
+
# available for configuration.
|
28
|
+
#
|
29
|
+
class ScanTemplate
|
30
|
+
|
31
|
+
# Unique identifier of the scan template.
|
32
|
+
attr_accessor :id
|
33
|
+
|
34
|
+
attr_accessor :name
|
35
|
+
attr_accessor :description
|
36
|
+
|
37
|
+
# Whether to correlate reliable checks with regular checks.
|
38
|
+
attr_accessor :correlate
|
39
|
+
|
40
|
+
# Parsed XML of a scan template
|
41
|
+
attr_accessor :xml
|
42
|
+
|
43
|
+
def initialize(xml)
|
44
|
+
@xml = xml
|
45
|
+
|
46
|
+
root = REXML::XPath.first(xml, 'ScanTemplate')
|
47
|
+
@id = root.attributes['id']
|
48
|
+
|
49
|
+
desc = REXML::XPath.first(root, 'templateDescription')
|
50
|
+
@name = desc.attributes['title']
|
51
|
+
@description = desc.text.to_s
|
52
|
+
|
53
|
+
vuln_checks = REXML::XPath.first(root, 'VulnerabilityChecks')
|
54
|
+
@correlate = vuln_checks.attributes['correlate'] == '1'
|
55
|
+
end
|
56
|
+
|
57
|
+
# Save this scan template configuration to a Nexpose console.
|
58
|
+
#
|
59
|
+
def save(nsc)
|
60
|
+
root = REXML::XPath.first(@xml, 'ScanTemplate')
|
61
|
+
existing = root.attributes['id'] == @id
|
62
|
+
root.attributes['id'] = @id unless existing
|
63
|
+
|
64
|
+
desc = REXML::XPath.first(root, 'templateDescription')
|
65
|
+
desc.attributes['title'] = @name
|
66
|
+
desc.text = @description
|
67
|
+
|
68
|
+
vuln_checks = REXML::XPath.first(root, 'VulnerabilityChecks')
|
69
|
+
vuln_checks.attributes['correlate'] = (@correlate ? '1' : '0')
|
70
|
+
|
71
|
+
if existing
|
72
|
+
response = AJAX.put(nsc, "/data/scan/templates/#{URI.encode(id)}", xml)
|
73
|
+
else
|
74
|
+
response = JSON.parse(AJAX.post(nsc, '/data/scan/templates', xml))
|
75
|
+
@id = response['value']
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Load an existing scan template.
|
80
|
+
#
|
81
|
+
# @param [Connection] nsc API connection to a Nexpose console.
|
82
|
+
# @param [String] id Unique identifier of an existing scan template.
|
83
|
+
# @return [ScanTemplate] The requested scan template configuration.
|
84
|
+
#
|
85
|
+
def self.load(nsc, id)
|
86
|
+
response = JSON.parse(AJAX.get(nsc, "/data/scan/templates/#{URI.encode(id)}"))
|
87
|
+
new(REXML::Document.new(response['value']))
|
88
|
+
end
|
89
|
+
|
90
|
+
# Copy an existing scan template, changing the id and title.
|
91
|
+
#
|
92
|
+
# @param [Connection] nsc API connection to a Nexpose console.
|
93
|
+
# @param [String] id Unique identifier of an existing scan template.
|
94
|
+
# @return [ScanTemplate] A copy of the requested scan template configuration.
|
95
|
+
#
|
96
|
+
def self.copy(nsc, id)
|
97
|
+
dupe = load(nsc, id)
|
98
|
+
dupe.id = "#{dupe.id}-copy"
|
99
|
+
dupe.title = "#{dupe.title} Copy"
|
100
|
+
dupe
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
data/lib/nexpose/site.rb
CHANGED
@@ -2,79 +2,15 @@ module Nexpose
|
|
2
2
|
module NexposeAPI
|
3
3
|
include XMLUtils
|
4
4
|
|
5
|
-
# Retrieve a list of all of the assets in a site.
|
6
|
-
#
|
7
|
-
# If no site-id is specified, then return all of the assets
|
8
|
-
# for the Nexpose console, grouped by site-id.
|
9
|
-
#
|
10
|
-
# @param [FixNum] site_id Site ID to request device listing for. Optional.
|
11
|
-
# @return [Array[Device]] Array of devices associated with the site, or
|
12
|
-
# all devices on the console if no site is provided.
|
13
|
-
#
|
14
|
-
def site_device_listing(site_id = nil)
|
15
|
-
r = execute(make_xml('SiteDeviceListingRequest', {'site-id' => site_id}))
|
16
|
-
|
17
|
-
devices = []
|
18
|
-
if r.success
|
19
|
-
r.res.elements.each('SiteDeviceListingResponse/SiteDevices') do |site|
|
20
|
-
site_id = site.attributes['site-id'].to_i
|
21
|
-
site.elements.each('device') do |device|
|
22
|
-
devices << Device.new(device.attributes['id'].to_i,
|
23
|
-
device.attributes['address'],
|
24
|
-
site_id,
|
25
|
-
device.attributes['riskfactor'].to_f,
|
26
|
-
device.attributes['riskscore'].to_f)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
devices
|
31
|
-
end
|
32
|
-
|
33
|
-
alias_method :assets, :site_device_listing
|
34
|
-
alias_method :devices, :site_device_listing
|
35
|
-
alias_method :list_devices, :site_device_listing
|
36
|
-
|
37
|
-
# Find a Device by its address.
|
38
|
-
#
|
39
|
-
# This is a convenience method for finding a single device from a SiteDeviceListing.
|
40
|
-
# If no site_id is provided, the first matching device will be returned when a device
|
41
|
-
# occurs across multiple sites.
|
42
|
-
#
|
43
|
-
# @param [String] address Address of the device to find. Usually the IP address.
|
44
|
-
# @param [FixNum] site_id Site ID to request scan history for.
|
45
|
-
# @return [Device] The first matching Device with the provided address, if found.
|
46
|
-
#
|
47
|
-
def find_device_by_address(address, site_id = nil)
|
48
|
-
r = execute(make_xml('SiteDeviceListingRequest', {'site-id' => site_id}))
|
49
|
-
if r.success
|
50
|
-
device = REXML::XPath.first(r.res, "SiteDeviceListingResponse/SiteDevices/device[@address='#{address}']")
|
51
|
-
return Device.new(device.attributes['id'].to_i,
|
52
|
-
device.attributes['address'],
|
53
|
-
device.parent.attributes['site-id'],
|
54
|
-
device.attributes['riskfactor'].to_f,
|
55
|
-
device.attributes['riskscore'].to_f) if device
|
56
|
-
end
|
57
|
-
nil
|
58
|
-
end
|
59
|
-
|
60
|
-
# Delete the specified site and all associated scan data.
|
61
|
-
#
|
62
|
-
# @return Whether or not the delete request succeeded.
|
63
|
-
#
|
64
|
-
def site_delete(site_id)
|
65
|
-
r = execute(make_xml('SiteDeleteRequest', {'site-id' => site_id}))
|
66
|
-
r.success
|
67
|
-
end
|
68
|
-
|
69
5
|
# Retrieve a list of all sites the user is authorized to view or manage.
|
70
6
|
#
|
71
7
|
# @return [Array[SiteSummary]] Array of SiteSummary objects.
|
72
|
-
#
|
73
|
-
def
|
8
|
+
#
|
9
|
+
def list_sites
|
74
10
|
r = execute(make_xml('SiteListingRequest'))
|
75
11
|
arr = []
|
76
|
-
if
|
77
|
-
r.res.elements.each(
|
12
|
+
if r.success
|
13
|
+
r.res.elements.each('SiteListingResponse/SiteSummary') do |site|
|
78
14
|
arr << SiteSummary.new(site.attributes['id'].to_i,
|
79
15
|
site.attributes['name'],
|
80
16
|
site.attributes['description'],
|
@@ -85,8 +21,16 @@ module Nexpose
|
|
85
21
|
arr
|
86
22
|
end
|
87
23
|
|
88
|
-
alias_method :
|
89
|
-
|
24
|
+
alias_method :sites, :list_sites
|
25
|
+
|
26
|
+
# Delete the specified site and all associated scan data.
|
27
|
+
#
|
28
|
+
# @return Whether or not the delete request succeeded.
|
29
|
+
#
|
30
|
+
def delete_site(site_id)
|
31
|
+
r = execute(make_xml('SiteDeleteRequest', {'site-id' => site_id}))
|
32
|
+
r.success
|
33
|
+
end
|
90
34
|
|
91
35
|
# Retrieve a list of all previous scans of the site.
|
92
36
|
#
|
@@ -111,68 +55,11 @@ module Nexpose
|
|
111
55
|
# Method will not return data on an active scan.
|
112
56
|
#
|
113
57
|
# @param [FixNum] site_id Site ID to find latest scan for.
|
58
|
+
# @return [ScanSummary] details of the last completed scan for a site.
|
114
59
|
#
|
115
60
|
def last_scan(site_id)
|
116
61
|
site_scan_history(site_id).select { |scan| scan.end_time }.max_by { |scan| scan.end_time }
|
117
62
|
end
|
118
|
-
|
119
|
-
#-----------------------------------------------------------------------
|
120
|
-
# Starts device specific site scanning.
|
121
|
-
#
|
122
|
-
# devices - An Array of device IDs
|
123
|
-
# hosts - An Array of Hashes [o]=>{:range=>"from,to"} [1]=>{:host=>host}
|
124
|
-
#-----------------------------------------------------------------------
|
125
|
-
def site_device_scan_start(site_id, devices, hosts = nil)
|
126
|
-
|
127
|
-
if hosts == nil and devices == nil
|
128
|
-
raise ArgumentError.new('Both the device and host list is nil.')
|
129
|
-
end
|
130
|
-
|
131
|
-
xml = make_xml('SiteDevicesScanRequest', {'site-id' => site_id})
|
132
|
-
|
133
|
-
if devices != nil
|
134
|
-
inner_xml = REXML::Element.new 'Devices'
|
135
|
-
for device_id in devices
|
136
|
-
inner_xml.add_element 'device', {'id' => "#{device_id}"}
|
137
|
-
end
|
138
|
-
xml.add_element inner_xml
|
139
|
-
end
|
140
|
-
|
141
|
-
if hosts
|
142
|
-
inner_xml = REXML::Element.new 'Hosts'
|
143
|
-
hosts.each_index do |x|
|
144
|
-
if hosts[x].key? :range
|
145
|
-
from, to = hosts[x][:range].split(',')
|
146
|
-
if to
|
147
|
-
inner_xml.add_element 'range', {'to' => to, 'from' => from}
|
148
|
-
else
|
149
|
-
inner_xml.add_element 'range', {'from' => from}
|
150
|
-
end
|
151
|
-
end
|
152
|
-
if hosts[x].key? :host
|
153
|
-
host_element = REXML::Element.new 'host'
|
154
|
-
host_element.text = "#{hosts[x][:host]}"
|
155
|
-
inner_xml.add_element host_element
|
156
|
-
end
|
157
|
-
end
|
158
|
-
xml.add_element inner_xml
|
159
|
-
end
|
160
|
-
|
161
|
-
r = execute xml
|
162
|
-
if r.success
|
163
|
-
r.res.elements.each('//Scan') do |scan_info|
|
164
|
-
return {
|
165
|
-
:scan_id => scan_info.attributes['scan-id'].to_i,
|
166
|
-
:engine_id => scan_info.attributes['engine-id'].to_i
|
167
|
-
}
|
168
|
-
end
|
169
|
-
else
|
170
|
-
false
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
alias_method :site_device_scan, :site_device_scan_start
|
175
|
-
alias_method :adhoc_device_scan, :site_device_scan_start
|
176
63
|
end
|
177
64
|
|
178
65
|
# Configuration object representing a Nexpose site.
|
@@ -270,6 +157,14 @@ module Nexpose
|
|
270
157
|
@assets << IPRange.new(ip)
|
271
158
|
end
|
272
159
|
|
160
|
+
# Adds assets to this site by IP address range.
|
161
|
+
#
|
162
|
+
# @param [String] from Beginning IP address of a range.
|
163
|
+
# @param [String] to Ending IP address of a range.
|
164
|
+
def add_ip_range(from, to)
|
165
|
+
@assets << IPRange.new(from, to)
|
166
|
+
end
|
167
|
+
|
273
168
|
# Adds an asset to this site, resolving whether an IP or hostname is
|
274
169
|
# provided.
|
275
170
|
#
|
@@ -277,12 +172,14 @@ module Nexpose
|
|
277
172
|
#
|
278
173
|
def add_asset(asset)
|
279
174
|
begin
|
175
|
+
# If the asset registers as a valid IP, store as IP.
|
176
|
+
ip = IPAddr.new(asset)
|
280
177
|
add_ip(asset)
|
281
178
|
rescue ArgumentError => e
|
282
179
|
if e.message == 'invalid address'
|
283
180
|
add_host(asset)
|
284
181
|
else
|
285
|
-
raise "Unable to parse asset: '#{asset}'"
|
182
|
+
raise "Unable to parse asset: '#{asset}'. #{e.message}"
|
286
183
|
end
|
287
184
|
end
|
288
185
|
end
|
@@ -292,16 +189,21 @@ module Nexpose
|
|
292
189
|
# @param [Connection] connection Connection to console where site exists.
|
293
190
|
# @param [Fixnum] id Site ID of an existing site.
|
294
191
|
# @return [Site] Site configuration loaded from a Nexpose console.
|
192
|
+
#
|
295
193
|
def self.load(connection, id)
|
296
|
-
r = APIRequest.execute(connection.url,
|
194
|
+
r = APIRequest.execute(connection.url,
|
195
|
+
%(<SiteConfigRequest session-id="#{connection.session_id}" site-id="#{id}"/>))
|
297
196
|
parse(r.res)
|
298
197
|
end
|
299
198
|
|
300
199
|
# Copy an existing configuration from a Nexpose instance.
|
200
|
+
# Returned object will reset the site ID and append "Copy" to the existing
|
201
|
+
# name.
|
301
202
|
#
|
302
|
-
# @param [Connection] connection Connection to
|
203
|
+
# @param [Connection] connection Connection to the security console.
|
303
204
|
# @param [Fixnum] id Site ID of an existing site.
|
304
205
|
# @return [Site] Site configuration loaded from a Nexpose console.
|
206
|
+
#
|
305
207
|
def self.copy(connection, id)
|
306
208
|
site = self.load(connection, id)
|
307
209
|
site.id = -1
|
@@ -313,19 +215,19 @@ module Nexpose
|
|
313
215
|
#
|
314
216
|
# @param [Connection] connection Connection to console where this site will be saved.
|
315
217
|
# @return [Fixnum] Site ID assigned to this configuration, if successful.
|
218
|
+
#
|
316
219
|
def save(connection)
|
317
220
|
r = connection.execute('<SiteSaveRequest session-id="' + connection.session_id + '">' + to_xml + ' </SiteSaveRequest>')
|
318
|
-
if r.success
|
319
|
-
@id = r.attributes['site-id'].to_i
|
320
|
-
end
|
221
|
+
@id = r.attributes['site-id'].to_i if r.success
|
321
222
|
end
|
322
223
|
|
323
224
|
# Delete this site from a Nexpose console.
|
324
225
|
#
|
325
226
|
# @param [Connection] connection Connection to console where this site will be saved.
|
326
227
|
# @return [Boolean] Whether or not the site was successfully deleted.
|
228
|
+
#
|
327
229
|
def delete(connection)
|
328
|
-
r = connection.execute(%
|
230
|
+
r = connection.execute(%(<SiteDeleteRequest session-id="#{connection.session_id}" site-id="#{@id}"/>))
|
329
231
|
r.success
|
330
232
|
end
|
331
233
|
|
@@ -333,32 +235,34 @@ module Nexpose
|
|
333
235
|
#
|
334
236
|
# @param [Connection] connection Connection to console where scan will be launched.
|
335
237
|
# @param [String] sync_id Optional synchronization token.
|
336
|
-
# @return [
|
238
|
+
# @return [Scan] Scan launch information.
|
239
|
+
#
|
337
240
|
def scan(connection, sync_id = nil)
|
338
241
|
xml = REXML::Element.new('SiteScanRequest')
|
339
|
-
xml.add_attributes({'session-id' => connection.session_id,
|
340
|
-
|
341
|
-
|
242
|
+
xml.add_attributes({ 'session-id' => connection.session_id,
|
243
|
+
'site-id' => @id,
|
244
|
+
'sync-id' => sync_id })
|
342
245
|
|
343
246
|
response = connection.execute(xml)
|
344
|
-
if response.success
|
345
|
-
scan = REXML::XPath.first(response.res, '/SiteScanResponse/Scan/')
|
346
|
-
[scan.attributes['scan-id'].to_i, scan.attributes['engine-id'].to_i]
|
347
|
-
end
|
247
|
+
Scan.parse(response.res) if response.success
|
348
248
|
end
|
349
249
|
|
250
|
+
include Sanitize
|
251
|
+
|
350
252
|
# Generate an XML representation of this site configuration
|
253
|
+
#
|
351
254
|
# @return [String] XML valid for submission as part of other requests.
|
255
|
+
#
|
352
256
|
def to_xml
|
353
|
-
xml = %
|
257
|
+
xml = %(<Site id='#{id}' name='#{replace_entities(name)}' description='#{description}' riskfactor='#{risk_factor}'>)
|
354
258
|
|
355
259
|
xml << '<Hosts>'
|
356
|
-
xml << assets.reduce('') { |
|
260
|
+
xml << assets.reduce('') { |a, e| a << e.to_xml }
|
357
261
|
xml << '</Hosts>'
|
358
262
|
|
359
263
|
unless exclude.empty?
|
360
264
|
xml << '<ExcludedHosts>'
|
361
|
-
xml << exclude.reduce('') { |
|
265
|
+
xml << exclude.reduce('') { |a, e| a << e.to_xml }
|
362
266
|
xml << '</ExcludedHosts>'
|
363
267
|
end
|
364
268
|
|
@@ -378,7 +282,7 @@ module Nexpose
|
|
378
282
|
xml << '</Alerting>'
|
379
283
|
end
|
380
284
|
|
381
|
-
xml << %
|
285
|
+
xml << %(<ScanConfig configID="#{@id}" name="#{@scan_template_name || @scan_template}" templateID="#{@scan_template}" configVersion="#{@config_version || 3}" engineID="#{@engine}">)
|
382
286
|
|
383
287
|
xml << '<Schedules>'
|
384
288
|
@schedules.each do |schedule|
|
@@ -394,6 +298,7 @@ module Nexpose
|
|
394
298
|
# @param [REXML::Document] rexml XML document to parse.
|
395
299
|
# @return [Site] Site object represented by the XML.
|
396
300
|
# ## TODO What is returned on failure?
|
301
|
+
#
|
397
302
|
def self.parse(rexml)
|
398
303
|
rexml.elements.each('SiteConfigResponse/Site') do |s|
|
399
304
|
site = Site.new(s.attributes['name'])
|
@@ -417,8 +322,8 @@ module Nexpose
|
|
417
322
|
end
|
418
323
|
|
419
324
|
s.elements.each('Credentials/adminCredentials') do |credconf|
|
420
|
-
cred =
|
421
|
-
cred.
|
325
|
+
cred = Credential.new
|
326
|
+
cred.service = credconf.attributes['service']
|
422
327
|
cred.set_blob(credconf.get_text)
|
423
328
|
site.credentials << cred
|
424
329
|
end
|
@@ -443,62 +348,6 @@ module Nexpose
|
|
443
348
|
end
|
444
349
|
end
|
445
350
|
|
446
|
-
# === Description
|
447
|
-
# Object that represents a listing of all of the sites available on an NSC.
|
448
|
-
#
|
449
|
-
# === Example
|
450
|
-
# # Create a new Nexpose Connection on the default port and Login
|
451
|
-
# nsc = Connection.new("10.1.40.10","nxadmin","password")
|
452
|
-
# nsc->login;
|
453
|
-
#
|
454
|
-
# # Get Site Listing
|
455
|
-
# sitelisting = SiteListing.new(nsc)
|
456
|
-
#
|
457
|
-
# # Enumerate through all of the SiteSummaries
|
458
|
-
# sitelisting.sites.each do |sitesummary|
|
459
|
-
# # Do some operation on each site
|
460
|
-
# end
|
461
|
-
#
|
462
|
-
class SiteListing
|
463
|
-
|
464
|
-
# The NSC Connection associated with this object
|
465
|
-
attr_reader :connection
|
466
|
-
# Array containing SiteSummary objects for each site in the connection
|
467
|
-
attr_reader :sites
|
468
|
-
# The number of sites
|
469
|
-
attr_reader :site_count
|
470
|
-
|
471
|
-
# Constructor
|
472
|
-
# SiteListing (connection)
|
473
|
-
def initialize(connection)
|
474
|
-
@sites = []
|
475
|
-
|
476
|
-
@connection = connection
|
477
|
-
|
478
|
-
r = @connection.execute('<SiteListingRequest session-id="' + @connection.session_id.to_s + '"/>')
|
479
|
-
|
480
|
-
if r.success
|
481
|
-
parse(r.res)
|
482
|
-
else
|
483
|
-
raise APIError.new(r, 'Failed to get site listing.')
|
484
|
-
end
|
485
|
-
end
|
486
|
-
|
487
|
-
def parse(r)
|
488
|
-
r.elements.each('SiteListingResponse/SiteSummary') do |s|
|
489
|
-
site_summary = SiteSummary.new(
|
490
|
-
s.attributes['id'].to_s,
|
491
|
-
s.attributes['name'],
|
492
|
-
s.attributes['description'],
|
493
|
-
s.attributes['riskfactor'].to_s
|
494
|
-
)
|
495
|
-
@sites.push(site_summary)
|
496
|
-
end
|
497
|
-
@site_count = @sites.length
|
498
|
-
end
|
499
|
-
end
|
500
|
-
|
501
|
-
# === Description
|
502
351
|
# Object that represents the summary of a Nexpose Site.
|
503
352
|
#
|
504
353
|
class SiteSummary
|
@@ -516,7 +365,7 @@ module Nexpose
|
|
516
365
|
|
517
366
|
# Constructor
|
518
367
|
# SiteSummary(id, name, description, riskfactor = 1)
|
519
|
-
def initialize(id, name, description, risk_factor = 1.0, risk_score = 0.0)
|
368
|
+
def initialize(id, name, description = nil, risk_factor = 1.0, risk_score = 0.0)
|
520
369
|
@id = id
|
521
370
|
@name = name
|
522
371
|
@description = description
|
@@ -525,33 +374,10 @@ module Nexpose
|
|
525
374
|
end
|
526
375
|
end
|
527
376
|
|
528
|
-
# === Description
|
529
|
-
# Object that represents a single device in a Nexpose security console.
|
530
|
-
#
|
531
|
-
class Device
|
532
|
-
|
533
|
-
# A unique device ID (assigned automatically by the Nexpose console).
|
534
|
-
attr_reader :id
|
535
|
-
# IP Address or Hostname of this device.
|
536
|
-
attr_reader :address
|
537
|
-
# User assigned risk multiplier.
|
538
|
-
attr_reader :risk_factor
|
539
|
-
# Nexpose risk score.
|
540
|
-
attr_reader :risk_score
|
541
|
-
# Site ID that this device is associated with.
|
542
|
-
attr_reader :site_id
|
543
|
-
|
544
|
-
def initialize(id, address, site_id, risk_factor = 1.0, risk_score = 0.0)
|
545
|
-
@id = id.to_i
|
546
|
-
@address = address
|
547
|
-
@site_id = site_id.to_i
|
548
|
-
@risk_factor = risk_factor.to_f
|
549
|
-
@risk_score = risk_score.to_f
|
550
|
-
end
|
551
|
-
end
|
552
|
-
|
553
377
|
# Object that represents a hostname to be added to a site.
|
378
|
+
#
|
554
379
|
class HostName
|
380
|
+
|
555
381
|
# Named host (usually DNS or Netbios name).
|
556
382
|
attr_accessor :host
|
557
383
|
|
@@ -584,9 +410,9 @@ module Nexpose
|
|
584
410
|
end
|
585
411
|
end
|
586
412
|
|
587
|
-
# === Description
|
588
413
|
# Object that represents a single IP address or an inclusive range of IP addresses.
|
589
414
|
# If to is nil then the from field will be used to specify a single IP Address only.
|
415
|
+
#
|
590
416
|
class IPRange
|
591
417
|
|
592
418
|
# Start of range *Required
|
@@ -595,18 +421,46 @@ module Nexpose
|
|
595
421
|
attr_accessor :to
|
596
422
|
|
597
423
|
def initialize(from, to = nil)
|
598
|
-
@from =
|
599
|
-
@to =
|
424
|
+
@from = from
|
425
|
+
@to = to unless from == to
|
600
426
|
end
|
601
427
|
|
602
428
|
include Comparable
|
603
429
|
|
604
430
|
def <=>(other)
|
605
|
-
|
431
|
+
from = IPAddr.new(@from)
|
432
|
+
to = @to.nil? ? from : IPAddr.new(@to)
|
433
|
+
cf_from = IPAddr.new(other.from)
|
434
|
+
cf_to = IPAddr.new(other.to.nil? ? other.from : other.to)
|
435
|
+
if cf_to < from
|
436
|
+
1
|
437
|
+
elsif to < cf_from
|
438
|
+
-1
|
439
|
+
else # Overlapping
|
440
|
+
0
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
444
|
+
def ==(other)
|
445
|
+
eql?(other)
|
606
446
|
end
|
607
447
|
|
608
448
|
def eql?(other)
|
609
|
-
|
449
|
+
@from == other.from && @to == other.to
|
450
|
+
end
|
451
|
+
|
452
|
+
def include?(single_ip)
|
453
|
+
from = IPAddr.new(@from)
|
454
|
+
to = @to.nil? ? from : IPAddr.new(@to)
|
455
|
+
other = IPAddr.new(single_ip)
|
456
|
+
|
457
|
+
if other < from
|
458
|
+
false
|
459
|
+
elsif to < other
|
460
|
+
false
|
461
|
+
else
|
462
|
+
true
|
463
|
+
end
|
610
464
|
end
|
611
465
|
|
612
466
|
def hash
|
@@ -615,7 +469,7 @@ module Nexpose
|
|
615
469
|
|
616
470
|
def to_xml_elem
|
617
471
|
xml = REXML::Element.new('range')
|
618
|
-
xml.add_attributes({'from' => @from, 'to' => @to})
|
472
|
+
xml.add_attributes({ 'from' => @from, 'to' => @to })
|
619
473
|
xml
|
620
474
|
end
|
621
475
|
|