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