nexpose 0.1.8 → 0.1.9
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.
- data/lib/nexpose.rb +3 -24
- data/lib/nexpose/common.rb +2 -2
- data/lib/nexpose/connection.rb +1 -1
- data/lib/nexpose/group.rb +1 -1
- data/lib/nexpose/manage.rb +5 -6
- data/lib/nexpose/report.rb +13 -14
- data/lib/nexpose/scan.rb +10 -33
- data/lib/nexpose/scan_engine.rb +12 -12
- data/lib/nexpose/site.rb +53 -75
- data/lib/nexpose/ticket.rb +5 -5
- data/lib/nexpose/user.rb +10 -11
- data/lib/nexpose/util.rb +7 -6
- data/lib/nexpose/vuln.rb +7 -8
- metadata +3 -2
data/lib/nexpose.rb
CHANGED
@@ -75,31 +75,10 @@ require 'nexpose/group'
|
|
75
75
|
|
76
76
|
module Nexpose
|
77
77
|
|
78
|
-
# TODO add
|
79
|
-
def self.site_device_scan(connection, site_id, device_array, host_array, debug = false)
|
80
|
-
|
81
|
-
request_xml = '<SiteDevicesScanRequest session-id="' + connection.session_id.to_s + '" site-id="' + site_id.to_s + '">'
|
82
|
-
request_xml += '<Devices>'
|
83
|
-
device_array.each do |d|
|
84
|
-
request_xml += '<device id="' + d.to_s + '"/>'
|
85
|
-
end
|
86
|
-
request_xml += '</Devices>'
|
87
|
-
request_xml += '<Hosts>'
|
88
|
-
# The host array can only by single IP addresses for now. TODO: Expand to full API Spec.
|
89
|
-
host_array.each do |h|
|
90
|
-
request_xml += '<range from="' + h.to_s + '"/>'
|
91
|
-
end
|
92
|
-
request_xml += '</Hosts>'
|
93
|
-
request_xml += '</SiteDevicesScanRequest>'
|
94
|
-
|
95
|
-
r = connection.execute(request_xml)
|
96
|
-
r.success ? {:engine_id => r.attributes['engine_id'], :scan_id => r.attributes['scan-id']} : nil
|
97
|
-
end
|
98
|
-
|
99
78
|
# ==== Description
|
100
79
|
# Echos the last XML API request and response for the specified object. (Useful for debugging)
|
101
|
-
def self.
|
102
|
-
puts
|
103
|
-
puts
|
80
|
+
def self.print_xml(object)
|
81
|
+
puts 'request: ' + object.request_xml.to_s
|
82
|
+
puts 'response: ' + object.response_xml.to_s
|
104
83
|
end
|
105
84
|
end
|
data/lib/nexpose/common.rb
CHANGED
@@ -39,8 +39,8 @@ module Nexpose
|
|
39
39
|
def initialize(to_all_authorized, send_to_owner_as, send_to_acl_as, send_as)
|
40
40
|
@to_all_authorized = to_all_authorized
|
41
41
|
@send_to_owner_as = send_to_owner_as
|
42
|
-
@send_to_acl_as = send_to_acl_as
|
43
|
-
@send_as = send_as
|
42
|
+
@send_to_acl_as = send_to_acl_as
|
43
|
+
@send_as = send_as
|
44
44
|
|
45
45
|
@recipients = []
|
46
46
|
end
|
data/lib/nexpose/connection.rb
CHANGED
@@ -105,7 +105,7 @@ module Nexpose
|
|
105
105
|
http.use_ssl = true
|
106
106
|
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # XXX: security issue
|
107
107
|
headers = {'Cookie' => "nexposeCCSessionID=#{@session_id}"}
|
108
|
-
resp = http.get(uri.
|
108
|
+
resp = http.get(uri.to_s, headers)
|
109
109
|
|
110
110
|
if file_name
|
111
111
|
File.open(file_name, 'w') { |file| file.write(resp.body) }
|
data/lib/nexpose/group.rb
CHANGED
@@ -79,7 +79,7 @@ module Nexpose
|
|
79
79
|
scans = []
|
80
80
|
sites_ids.each do |id|
|
81
81
|
dev_ids = @devices.select { |d| d.site_id == id }.map { |d| d.id }
|
82
|
-
scans << connection.site_device_scan_start(id, dev_ids)
|
82
|
+
scans << connection.site_device_scan_start(id, dev_ids).merge(:site_id => id)
|
83
83
|
end
|
84
84
|
scans
|
85
85
|
end
|
data/lib/nexpose/manage.rb
CHANGED
@@ -5,7 +5,7 @@ module Nexpose
|
|
5
5
|
|
6
6
|
# Execute an arbitrary console command that is supplied as text via the
|
7
7
|
# supplied parameter. Console commands are documented in the
|
8
|
-
# administrator's guide. If you use a command that is not listed in the
|
8
|
+
# administrator's guide. If you use a command that is not listed in the
|
9
9
|
# administrator's guide, the application will return the XMLResponse.
|
10
10
|
def console_command(cmd_string)
|
11
11
|
xml = make_xml('ConsoleCommandRequest', {})
|
@@ -14,8 +14,7 @@ module Nexpose
|
|
14
14
|
xml << cmd
|
15
15
|
|
16
16
|
r = execute(xml)
|
17
|
-
if
|
18
|
-
res = ''
|
17
|
+
if r.success
|
19
18
|
r.res.elements.each('//Output') do |out|
|
20
19
|
return out.text.to_s
|
21
20
|
end
|
@@ -30,9 +29,9 @@ module Nexpose
|
|
30
29
|
def system_information
|
31
30
|
r = execute(make_xml('SystemInformationRequest', {}))
|
32
31
|
|
33
|
-
if
|
32
|
+
if r.success
|
34
33
|
res = {}
|
35
|
-
r.res.elements.each(
|
34
|
+
r.res.elements.each('//Statistic') do |stat|
|
36
35
|
res[stat.attributes['name'].to_s] = stat.text.to_s
|
37
36
|
end
|
38
37
|
res
|
@@ -48,7 +47,7 @@ module Nexpose
|
|
48
47
|
end
|
49
48
|
|
50
49
|
# Restart the application.
|
51
|
-
#
|
50
|
+
#
|
52
51
|
# There is no response to a RestartRequest. When the application
|
53
52
|
# shuts down as part of the restart process, it terminates any active
|
54
53
|
# connections. Therefore, the application cannot issue a response when it
|
data/lib/nexpose/report.rb
CHANGED
@@ -6,7 +6,6 @@ module Nexpose
|
|
6
6
|
def generate_report(report_id, wait = false)
|
7
7
|
xml = make_xml('ReportGenerateRequest', {'report-id' => report_id})
|
8
8
|
response = execute(xml)
|
9
|
-
summary = nil
|
10
9
|
if response.success
|
11
10
|
response.res.elements.each('//ReportSummary') do |summary|
|
12
11
|
summary = ReportSummary.parse(summary)
|
@@ -58,7 +57,7 @@ module Nexpose
|
|
58
57
|
def report_template_listing
|
59
58
|
r = execute(make_xml('ReportTemplateListingRequest', {}))
|
60
59
|
templates = []
|
61
|
-
if
|
60
|
+
if r.success
|
62
61
|
r.res.elements.each('//ReportTemplateSummary') do |template|
|
63
62
|
templates << ReportTemplateSummary.parse(template)
|
64
63
|
end
|
@@ -77,7 +76,7 @@ module Nexpose
|
|
77
76
|
def report_listing
|
78
77
|
r = execute(make_xml('ReportListingRequest', {}))
|
79
78
|
reports = []
|
80
|
-
if
|
79
|
+
if r.success
|
81
80
|
r.res.elements.each('//ReportConfigSummary') do |report|
|
82
81
|
reports << ReportConfigSummary.parse(report)
|
83
82
|
end
|
@@ -113,10 +112,10 @@ module Nexpose
|
|
113
112
|
def initialize(config_id, template_id, status, generated_on, uri, scope)
|
114
113
|
@config_id = config_id
|
115
114
|
@template_id = template_id
|
116
|
-
@status = status
|
117
|
-
@generated_on = generated_on
|
118
|
-
@uri = uri
|
119
|
-
@scope = scope
|
115
|
+
@status = status
|
116
|
+
@generated_on = generated_on
|
117
|
+
@uri = uri
|
118
|
+
@scope = scope
|
120
119
|
end
|
121
120
|
|
122
121
|
def self.parse(xml)
|
@@ -162,7 +161,7 @@ module Nexpose
|
|
162
161
|
|
163
162
|
def self.parse_all(response)
|
164
163
|
summaries = []
|
165
|
-
if
|
164
|
+
if response.success
|
166
165
|
response.res.elements.each('//ReportSummary') do |summary|
|
167
166
|
summaries << ReportSummary.parse(summary)
|
168
167
|
end
|
@@ -193,8 +192,8 @@ module Nexpose
|
|
193
192
|
attr_accessor :baseline
|
194
193
|
|
195
194
|
def initialize(template_id, format, site_id = nil, owner = nil, time_zone = nil)
|
196
|
-
@template_id = template_id
|
197
|
-
@format = format
|
195
|
+
@template_id = template_id
|
196
|
+
@format = format
|
198
197
|
@owner = owner
|
199
198
|
@time_zone = time_zone
|
200
199
|
|
@@ -385,7 +384,7 @@ module Nexpose
|
|
385
384
|
|
386
385
|
# Object that represents a report filter which determines which sites, asset
|
387
386
|
# groups, and/or devices that a report is run against.
|
388
|
-
#
|
387
|
+
#
|
389
388
|
# The configuration must include at least one of device (asset), site,
|
390
389
|
# group (asset group) or scan filter to define the scope of report.
|
391
390
|
# The vuln-status filter can be used only with raw report formats: csv
|
@@ -416,7 +415,7 @@ module Nexpose
|
|
416
415
|
def self.parse(xml)
|
417
416
|
filters = []
|
418
417
|
xml.res.elements.each('//Filters/filter') do |filter|
|
419
|
-
filters << Filter.new(filter.attributes['type'], filter.attributes['id'])
|
418
|
+
filters << Filter.new(filter.attributes['type'], filter.attributes['id'])
|
420
419
|
end
|
421
420
|
filters
|
422
421
|
end
|
@@ -485,7 +484,7 @@ module Nexpose
|
|
485
484
|
end
|
486
485
|
|
487
486
|
def self.parse(xml)
|
488
|
-
xml.elements.each('//Delivery') do
|
487
|
+
xml.elements.each('//Delivery') do
|
489
488
|
on_server = false
|
490
489
|
location = nil
|
491
490
|
xml.elements.each('//Storage') do |storage|
|
@@ -529,7 +528,7 @@ module Nexpose
|
|
529
528
|
def self.parse(xml)
|
530
529
|
xml.elements.each('//DBExport') do |dbexport|
|
531
530
|
config = DBExport.new(dbexport.attributes['type'])
|
532
|
-
config.credentials = ExportCredential.parse(xml)
|
531
|
+
config.credentials = ExportCredential.parse(xml)
|
533
532
|
xml.elements.each('//param') do |param|
|
534
533
|
config.parameters[param.attributes['name']] = param.text
|
535
534
|
end
|
data/lib/nexpose/scan.rb
CHANGED
@@ -57,8 +57,8 @@ module Nexpose
|
|
57
57
|
def scan_activity
|
58
58
|
r = execute(make_xml('ScanActivityRequest', {}))
|
59
59
|
res = []
|
60
|
-
if
|
61
|
-
r.res.elements.each(
|
60
|
+
if r.success
|
61
|
+
r.res.elements.each('//ScanSummary') do |scan|
|
62
62
|
res << ScanSummary.parse(scan)
|
63
63
|
end
|
64
64
|
end
|
@@ -139,7 +139,7 @@ module Nexpose
|
|
139
139
|
return ScanSummary.new(rexml.attributes['scan-id'].to_i,
|
140
140
|
rexml.attributes['site-id'].to_i,
|
141
141
|
rexml.attributes['engine-id'].to_i,
|
142
|
-
rexml.attributes['status'],
|
142
|
+
rexml.attributes['status'],
|
143
143
|
start_time,
|
144
144
|
end_time,
|
145
145
|
msg,
|
@@ -211,7 +211,7 @@ module Nexpose
|
|
211
211
|
not_vuln_exploit, not_vuln_version,
|
212
212
|
error, disabled, other
|
213
213
|
end
|
214
|
-
|
214
|
+
|
215
215
|
# Parse REXML to Vulnerabilities object.
|
216
216
|
#
|
217
217
|
# @param [FixNum] scan_id Scan ID to collect vulnerability data for.
|
@@ -269,42 +269,19 @@ module Nexpose
|
|
269
269
|
end
|
270
270
|
end
|
271
271
|
|
272
|
-
# TODO add engineID
|
273
|
-
# === Description
|
274
|
-
# Object that represents the scanning configuration for a Site.
|
275
|
-
#
|
276
|
-
class ScanConfig
|
277
|
-
|
278
|
-
def self.parse(xml)
|
279
|
-
config = ScanConfig.new(xml.attributes['configID'],
|
280
|
-
xml.attributes['name'],
|
281
|
-
xml.attributes['templateID'],
|
282
|
-
xml.attributes['configVersion'],
|
283
|
-
xml.attributes['engineID'])
|
284
|
-
xml.elements.each('Schedules/Schedule') do |sched|
|
285
|
-
schedule = Schedule.new(sched.attributes['type'],
|
286
|
-
sched.attributes['interval'],
|
287
|
-
sched.attributes['start'],
|
288
|
-
sched.attributes['enabled'])
|
289
|
-
config.addSchedule(schedule)
|
290
|
-
end
|
291
|
-
config
|
292
|
-
end
|
293
|
-
end
|
294
|
-
|
295
272
|
# TODO: review
|
296
273
|
# <scanFilter scanStop='0' scanFailed='0' scanStart='1'/>
|
297
274
|
# === Description
|
298
275
|
#
|
299
276
|
class ScanFilter
|
300
|
-
attr_reader :
|
301
|
-
attr_reader :
|
302
|
-
attr_reader :
|
277
|
+
attr_reader :scan_stop
|
278
|
+
attr_reader :scan_failed
|
279
|
+
attr_reader :scan_start
|
303
280
|
|
304
281
|
def initialize(scan_stop, scan_failed, scan_start)
|
305
|
-
@
|
306
|
-
@
|
307
|
-
@
|
282
|
+
@scan_stop = scan_stop
|
283
|
+
@scan_failed = scan_failed
|
284
|
+
@scan_start = scan_start
|
308
285
|
end
|
309
286
|
end
|
310
287
|
end
|
data/lib/nexpose/scan_engine.rb
CHANGED
@@ -18,7 +18,7 @@ module Nexpose
|
|
18
18
|
r = execute(xml)
|
19
19
|
arr = []
|
20
20
|
if r.success
|
21
|
-
r.res.elements.each(
|
21
|
+
r.res.elements.each('//ScanSummary') do |scan_event|
|
22
22
|
arr << ScanSummary.parse(scan_event)
|
23
23
|
end
|
24
24
|
end
|
@@ -34,7 +34,7 @@ module Nexpose
|
|
34
34
|
response = execute(make_xml('EngineListingRequest'))
|
35
35
|
arr = []
|
36
36
|
if response.success
|
37
|
-
response.res.elements.each(
|
37
|
+
response.res.elements.each('//EngineSummary') do |engine|
|
38
38
|
arr << EngineSummary.new(engine.attributes['id'].to_i,
|
39
39
|
engine.attributes['name'],
|
40
40
|
engine.attributes['address'],
|
@@ -202,7 +202,7 @@ module Nexpose
|
|
202
202
|
# Creates a new engine pool, and adds scan engines to the pool.
|
203
203
|
def create(connection)
|
204
204
|
xml = '<EnginePoolCreateRequest session-id="' + connection.session_id + '">'
|
205
|
-
xml << %Q{<EnginePool name="
|
205
|
+
xml << %Q{<EnginePool name="#{@name}" scope="#{@scope}">}
|
206
206
|
@engines.each do |engine|
|
207
207
|
xml << %Q{<Engine name="#{engine.name}" />}
|
208
208
|
end
|
@@ -214,7 +214,7 @@ module Nexpose
|
|
214
214
|
r.res.elements.each('EnginePoolCreateResponse') do |v|
|
215
215
|
@id = v.attributes['id']
|
216
216
|
end
|
217
|
-
else
|
217
|
+
else
|
218
218
|
@error = true
|
219
219
|
@error_msg = 'EnginePoolCreateResponse Parse Error'
|
220
220
|
end
|
@@ -223,7 +223,7 @@ module Nexpose
|
|
223
223
|
# Deletes an engine pool
|
224
224
|
def delete(connection)
|
225
225
|
xml = '<EnginePoolDeleteRequest session-id="' + connection.session_id + '">'
|
226
|
-
xml << %Q{<EnginePool name="
|
226
|
+
xml << %Q{<EnginePool name="#{@name}" scope="#{@scope}" />}
|
227
227
|
xml << '</EnginePoolDeleteRequest>'
|
228
228
|
|
229
229
|
r = connection.execute(xml, '1.2')
|
@@ -239,7 +239,7 @@ module Nexpose
|
|
239
239
|
# the EnginePoolUpdateRequest.
|
240
240
|
def update(connection)
|
241
241
|
xml = '<EnginePoolUpdateRequest session-id="' + connection.session_id + '">'
|
242
|
-
xml << %Q{<EnginePool id="
|
242
|
+
xml << %Q{<EnginePool id="#{@id}" name="#{@name}" scope="#{@scope}">}
|
243
243
|
@engines.each do |engine|
|
244
244
|
xml << %Q{<Engine name="#{engine.name}" />}
|
245
245
|
end
|
@@ -251,7 +251,7 @@ module Nexpose
|
|
251
251
|
r.res.elements.each('EnginePoolUpdateResponse') do |v|
|
252
252
|
@id = v.attributes['id']
|
253
253
|
end
|
254
|
-
else
|
254
|
+
else
|
255
255
|
@error = true
|
256
256
|
@error_msg = 'EnginePoolCreateResponse Parse Error'
|
257
257
|
end
|
@@ -260,7 +260,7 @@ module Nexpose
|
|
260
260
|
# Returns detailed information about a single engine pool.
|
261
261
|
def load_details(connection)
|
262
262
|
xml = '<EnginePoolDetailsRequest session-id="' + connection.session_id + '">'
|
263
|
-
xml << %Q{<EnginePool name="
|
263
|
+
xml << %Q{<EnginePool name="#{@name}" scope="#{@scope}" />}
|
264
264
|
xml << '</EnginePoolDetailsRequest>'
|
265
265
|
|
266
266
|
r = connection.execute(xml, '1.2')
|
@@ -279,14 +279,14 @@ module Nexpose
|
|
279
279
|
summary.attributes['scope']))
|
280
280
|
end
|
281
281
|
end
|
282
|
-
else
|
282
|
+
else
|
283
283
|
@error = true
|
284
284
|
@error_msg = 'EnginePoolListingResponse Parse Error'
|
285
285
|
end
|
286
286
|
end
|
287
287
|
|
288
288
|
def to_s
|
289
|
-
"Engine Pool:
|
289
|
+
"Engine Pool: #{@name} [ID: #{@id}], Scope: #{@scope}\n" + @engines.map { |engine| " #{engine}" }.join("\n")
|
290
290
|
end
|
291
291
|
end
|
292
292
|
|
@@ -303,7 +303,7 @@ module Nexpose
|
|
303
303
|
end
|
304
304
|
|
305
305
|
def to_s
|
306
|
-
"Engine Pool:
|
306
|
+
"Engine Pool: #{@name} [ID: #{@id}], scope: #{@scope}"
|
307
307
|
end
|
308
308
|
|
309
309
|
# Returns a summary list of all engine pools.
|
@@ -316,7 +316,7 @@ module Nexpose
|
|
316
316
|
list << EnginePoolSummary.new(eps.attributes['id'], eps.attributes['name'], eps.attributes['scope'])
|
317
317
|
end
|
318
318
|
list
|
319
|
-
else
|
319
|
+
else
|
320
320
|
@error = true
|
321
321
|
@error_msg = 'EnginePoolListingResponse Parse Error'
|
322
322
|
end
|
data/lib/nexpose/site.rb
CHANGED
@@ -90,8 +90,7 @@ module Nexpose
|
|
90
90
|
# @param [FixNum] site_id Site ID to find latest scan for.
|
91
91
|
#
|
92
92
|
def last_scan(site_id)
|
93
|
-
site_scan_history(site_id).select { |scan| scan.end_time }
|
94
|
-
.max_by { |scan| scan.end_time }
|
93
|
+
site_scan_history(site_id).select { |scan| scan.end_time }.max_by { |scan| scan.end_time }
|
95
94
|
end
|
96
95
|
|
97
96
|
#-----------------------------------------------------------------------
|
@@ -103,7 +102,7 @@ module Nexpose
|
|
103
102
|
def site_device_scan_start(site_id, devices, hosts = nil)
|
104
103
|
|
105
104
|
if hosts == nil and devices == nil
|
106
|
-
raise ArgumentError.new(
|
105
|
+
raise ArgumentError.new('Both the device and host list is nil.')
|
107
106
|
end
|
108
107
|
|
109
108
|
xml = make_xml('SiteDevicesScanRequest', {'site-id' => site_id})
|
@@ -148,6 +147,9 @@ module Nexpose
|
|
148
147
|
false
|
149
148
|
end
|
150
149
|
end
|
150
|
+
|
151
|
+
alias_method :site_device_scan, :site_device_scan_start
|
152
|
+
alias_method :adhoc_device_scan, :site_device_scan_start
|
151
153
|
end
|
152
154
|
|
153
155
|
# Configuration object representing a Nexpose site.
|
@@ -208,7 +210,7 @@ module Nexpose
|
|
208
210
|
# @param [String] name Unique name of the site.
|
209
211
|
# @param [String] scan_template ID of the scan template to use.
|
210
212
|
def initialize(name = nil, scan_template = 'full-audit')
|
211
|
-
@name = name
|
213
|
+
@name = name
|
212
214
|
@scan_template = scan_template
|
213
215
|
|
214
216
|
@id = -1
|
@@ -285,9 +287,8 @@ module Nexpose
|
|
285
287
|
# @return [Fixnum] Site ID assigned to this configuration, if successful.
|
286
288
|
def save(connection)
|
287
289
|
r = connection.execute('<SiteSaveRequest session-id="' + connection.session_id + '">' + to_xml + ' </SiteSaveRequest>')
|
288
|
-
if
|
290
|
+
if r.success
|
289
291
|
@id = r.attributes['site-id']
|
290
|
-
return @id
|
291
292
|
end
|
292
293
|
end
|
293
294
|
|
@@ -296,14 +297,14 @@ module Nexpose
|
|
296
297
|
# @param [Connection] connection Connection to console where this site will be saved.
|
297
298
|
# @return [Boolean] Whether or not the site was successfully deleted.
|
298
299
|
def delete(connection)
|
299
|
-
r = connection.execute(%Q{<SiteDeleteRequest session-id="#{connection.session_id}" site-id="
|
300
|
+
r = connection.execute(%Q{<SiteDeleteRequest session-id="#{connection.session_id}" site-id="#{@id}"/>})
|
300
301
|
r.success
|
301
302
|
end
|
302
303
|
|
303
304
|
# Scan this site.
|
304
305
|
#
|
305
306
|
# @param [Connection] connection Connection to console where scan will be launched.
|
306
|
-
# @param [String] sync_id Optional
|
307
|
+
# @param [String] sync_id Optional synchronization token.
|
307
308
|
# @return [Fixnum, Fixnum] Scan ID and engine ID where the scan was launched.
|
308
309
|
def scan(connection, sync_id = nil)
|
309
310
|
xml = REXML::Element.new('SiteScanRequest')
|
@@ -389,16 +390,16 @@ module Nexpose
|
|
389
390
|
end
|
390
391
|
end
|
391
392
|
|
392
|
-
s.elements.each('Credentials') do |cred|
|
393
|
-
|
394
|
-
end
|
393
|
+
#s.elements.each('Credentials') do |cred|
|
394
|
+
# # TODO
|
395
|
+
#end
|
395
396
|
|
396
397
|
s.elements.each('Alerting/Alert') do |a|
|
397
398
|
a.elements.each('smtpAlert') do |smtp|
|
398
399
|
smtp_alert = SMTPAlert.new(a.attributes['name'], smtp.attributes['sender'], smtp.attributes['limitText'], a.attributes['enabled'])
|
399
400
|
|
400
401
|
smtp.elements.each('recipient') do |recipient|
|
401
|
-
smtp_alert.
|
402
|
+
smtp_alert.add_recipient(recipient.text)
|
402
403
|
end
|
403
404
|
site.alerts << smtp_alert
|
404
405
|
end
|
@@ -413,24 +414,23 @@ module Nexpose
|
|
413
414
|
site.alerts << syslog_alert
|
414
415
|
end
|
415
416
|
|
416
|
-
a.elements.each('
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
end
|
417
|
+
#a.elements.each('vuln_filter') do |vulnFilter|
|
418
|
+
# vulnfilter = new VulnFilter.new(a.attributes["typemask"], a.attributes["severityThreshold"], $attrs["MAXALERTS"])
|
419
|
+
# Pop off the top alert on the stack
|
420
|
+
# $alert = @alerts.pop()
|
421
|
+
# Add the new recipient string to the Alert Object
|
422
|
+
# $alert.setVulnFilter($vulnfilter)
|
423
|
+
# Push the alert back on to the alert stack
|
424
|
+
# array_push($this->alerts, $alert)
|
425
|
+
#end
|
426
|
+
|
427
|
+
#a.elements.each('scanFilter') do |scanFilter|
|
428
|
+
# <scanFilter scanStop='0' scanFailed='0' scanStart='1'/>
|
429
|
+
# scanfilter = ScanFilter.new(scanFilter.attributes['scanStop'],scanFilter.attributes['scanFailed'],scanFilter.attributes['scanStart'])
|
430
|
+
# alert = @alerts.pop()
|
431
|
+
# alert.setScanFilter(scanfilter)
|
432
|
+
# @alerts.push(alert)
|
433
|
+
#end
|
434
434
|
end
|
435
435
|
|
436
436
|
return site
|
@@ -456,14 +456,7 @@ module Nexpose
|
|
456
456
|
# end
|
457
457
|
#
|
458
458
|
class SiteListing
|
459
|
-
|
460
|
-
attr_reader :error
|
461
|
-
# Error message string
|
462
|
-
attr_reader :error_msg
|
463
|
-
# The last XML request sent by this object
|
464
|
-
attr_reader :request_xml
|
465
|
-
# The last XML response received by this object
|
466
|
-
attr_reader :response_xml
|
459
|
+
|
467
460
|
# The NSC Connection associated with this object
|
468
461
|
attr_reader :connection
|
469
462
|
# Array containing SiteSummary objects for each site in the connection
|
@@ -480,10 +473,10 @@ module Nexpose
|
|
480
473
|
|
481
474
|
r = @connection.execute('<SiteListingRequest session-id="' + @connection.session_id.to_s + '"/>')
|
482
475
|
|
483
|
-
if
|
476
|
+
if r.success
|
484
477
|
parse(r.res)
|
485
478
|
else
|
486
|
-
raise APIError.new(r,
|
479
|
+
raise APIError.new(r, 'Failed to get site listing.')
|
487
480
|
end
|
488
481
|
end
|
489
482
|
|
@@ -565,7 +558,7 @@ module Nexpose
|
|
565
558
|
# The Syslog server to sent this alert
|
566
559
|
attr_reader :server
|
567
560
|
# The vulnerability filter to trigger the alert
|
568
|
-
|
561
|
+
attr_accessor :vuln_filter
|
569
562
|
# The alert type
|
570
563
|
attr_reader :type
|
571
564
|
|
@@ -575,24 +568,19 @@ module Nexpose
|
|
575
568
|
@server = server
|
576
569
|
@enabled = enabled
|
577
570
|
# Sets default vuln filter - All Events
|
578
|
-
|
579
|
-
|
580
|
-
end
|
571
|
+
@vuln_filter = VulnFilter.new('50790400', 1)
|
581
572
|
|
582
|
-
# Sets the Vulnerability Filter for this alert.
|
583
|
-
def setVulnFilter(vulnFilter)
|
584
|
-
@vulnFilter = vulnFilter
|
585
573
|
end
|
586
574
|
|
587
575
|
include Sanitize
|
588
576
|
|
589
577
|
def to_xml
|
590
|
-
xml =
|
578
|
+
xml = '<syslogAlert'
|
591
579
|
xml << %Q{ name="#{replace_entities(name)}"}
|
592
580
|
xml << %Q{ enabled="#{replace_entities(enabled)}"}
|
593
581
|
xml << %Q{ server="#{replace_entities(server)}">}
|
594
|
-
xml <<
|
595
|
-
xml <<
|
582
|
+
xml << vuln_filter.to_xml
|
583
|
+
xml << '</syslogAlert>'
|
596
584
|
xml
|
597
585
|
end
|
598
586
|
|
@@ -613,7 +601,7 @@ module Nexpose
|
|
613
601
|
# The SNMP server to sent this alert
|
614
602
|
attr_reader :server
|
615
603
|
# The vulnerability filter to trigger the alert
|
616
|
-
attr_reader :
|
604
|
+
attr_reader :vuln_filter
|
617
605
|
# The alert type
|
618
606
|
attr_reader :type
|
619
607
|
|
@@ -624,22 +612,17 @@ module Nexpose
|
|
624
612
|
@server = server
|
625
613
|
@enabled = enabled
|
626
614
|
# Sets default vuln filter - All Events
|
627
|
-
|
628
|
-
end
|
629
|
-
|
630
|
-
# Sets the Vulnerability Filter for this alert.
|
631
|
-
def setVulnFilter(vulnFilter)
|
632
|
-
@vulnFilter = vulnFilter
|
615
|
+
@vuln_filter = VulnFilter.new('50790400', 1)
|
633
616
|
end
|
634
617
|
|
635
618
|
def to_xml
|
636
|
-
xml =
|
619
|
+
xml = '<snmpAlert'
|
637
620
|
xml << %Q{ name="#{replace_entities(name)}"}
|
638
621
|
xml << %Q{ enabled="#{replace_entities(enabled)}"}
|
639
622
|
xml << %Q{ community="#{replace_entities(community)}"}
|
640
623
|
xml << %Q{ server="#{replace_entities(server)}">}
|
641
|
-
xml <<
|
642
|
-
xml <<
|
624
|
+
xml << vuln_filter.to_xml
|
625
|
+
xml << '</snmpAlert>'
|
643
626
|
xml
|
644
627
|
end
|
645
628
|
|
@@ -656,49 +639,44 @@ module Nexpose
|
|
656
639
|
# The email address of the sender
|
657
640
|
attr_reader :sender
|
658
641
|
# Limit the text for mobile devices
|
659
|
-
attr_reader :
|
642
|
+
attr_reader :limit_text
|
660
643
|
# Array containing Strings of email addresses
|
661
644
|
# Array of strings with the email addresses of the intended recipients
|
662
645
|
attr_reader :recipients
|
663
646
|
# The vulnerability filter to trigger the alert
|
664
|
-
|
647
|
+
attr_accessor :vuln_filter
|
665
648
|
# The alert type
|
666
649
|
attr_reader :type
|
667
650
|
|
668
|
-
def initialize(name, sender,
|
651
|
+
def initialize(name, sender, limit_text, enabled = 1)
|
669
652
|
@type = :smtp
|
670
653
|
@name = name
|
671
654
|
@sender = sender
|
672
655
|
@enabled = enabled
|
673
|
-
@
|
656
|
+
@limit_text = limit_text
|
674
657
|
@recipients = []
|
675
658
|
# Sets default vuln filter - All Events
|
676
|
-
|
659
|
+
@vuln_filter = VulnFilter.new('50790400', 1)
|
677
660
|
end
|
678
661
|
|
679
662
|
# Adds a new Recipient to the recipients array
|
680
|
-
def
|
663
|
+
def add_recipient(recipient)
|
681
664
|
@recipients.push(recipient)
|
682
665
|
end
|
683
666
|
|
684
|
-
# Sets the Vulnerability Filter for this alert.
|
685
|
-
def setVulnFilter(vulnFilter)
|
686
|
-
@vulnFilter = vulnFilter
|
687
|
-
end
|
688
|
-
|
689
667
|
include Sanitize
|
690
668
|
|
691
669
|
def to_xml
|
692
|
-
xml =
|
670
|
+
xml = '<smtpAlert'
|
693
671
|
xml << %Q{ name="#{replace_entities(name)}"}
|
694
672
|
xml << %Q{ enabled="#{replace_entities(enabled)}"}
|
695
673
|
xml << %Q{ sender="#{replace_entities(sender)}"}
|
696
|
-
xml << %Q{ limitText="#{replace_entities(
|
674
|
+
xml << %Q{ limitText="#{replace_entities(limit_text)}">}
|
697
675
|
recipients.each do |recpt|
|
698
676
|
xml << "<recipient>#{replace_entities(recpt)}</recipient>"
|
699
677
|
end
|
700
|
-
xml <<
|
701
|
-
xml <<
|
678
|
+
xml << vuln_filter.to_xml
|
679
|
+
xml << '</smtpAlert>'
|
702
680
|
xml
|
703
681
|
end
|
704
682
|
end
|
data/lib/nexpose/ticket.rb
CHANGED
@@ -16,7 +16,7 @@ module Nexpose
|
|
16
16
|
#
|
17
17
|
# @return The ticket ID if the ticket creation was successful, {@code false} otherwise
|
18
18
|
#
|
19
|
-
def create_ticket
|
19
|
+
def create_ticket(ticket_info)
|
20
20
|
ticket_name = ticket_info[:name]
|
21
21
|
unless ticket_name
|
22
22
|
raise ArgumentError.new 'Ticket name is required'
|
@@ -46,10 +46,10 @@ module Nexpose
|
|
46
46
|
base_xml = make_xml 'TicketCreateRequest'
|
47
47
|
|
48
48
|
required_attributes = {
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
49
|
+
'name' => ticket_name,
|
50
|
+
'priority' => priority,
|
51
|
+
'device-id' => device_id,
|
52
|
+
'assigned-to' => assigned_to
|
53
53
|
}
|
54
54
|
|
55
55
|
create_request_xml = REXML::Element.new 'TicketCreate'
|
data/lib/nexpose/user.rb
CHANGED
@@ -22,9 +22,9 @@ module Nexpose
|
|
22
22
|
def to_s
|
23
23
|
out = "#{@user_name} (#{@full_name}) [ID: #{@id}]"
|
24
24
|
out << " e-mail: #{@email}" unless @email.empty?
|
25
|
-
out <<
|
26
|
-
out <<
|
27
|
-
out <<
|
25
|
+
out << ' Administrator' if @is_admin
|
26
|
+
out << ' Disabled' if @is_disabled
|
27
|
+
out << ' Locked' if @is_locked
|
28
28
|
out << ", sites: #{@site_count}"
|
29
29
|
out << ", groups: #{@group_count}"
|
30
30
|
end
|
@@ -104,16 +104,16 @@ module Nexpose
|
|
104
104
|
end
|
105
105
|
|
106
106
|
def to_s
|
107
|
-
out = "#{@
|
108
|
-
out <<
|
109
|
-
out <<
|
110
|
-
out <<
|
107
|
+
out = "#{@name} (#{@full_name}) [ID: #{@id}, Role: #{@role_name}]"
|
108
|
+
out << ' Disabled' unless @enabled
|
109
|
+
out << ' All-Sites' if @all_sites
|
110
|
+
out << ' All-Groups' if @all_groups
|
111
111
|
out << " e-mail: #{@email}" unless @email.nil? || @email.empty?
|
112
112
|
out
|
113
113
|
end
|
114
114
|
|
115
115
|
def to_xml
|
116
|
-
xml =
|
116
|
+
xml = '<UserConfig'
|
117
117
|
xml << %Q{ id="#{@id}"}
|
118
118
|
xml << %Q{ authsrcid="#{@authsrcid}"}
|
119
119
|
xml << %Q{ name="#{@name}"}
|
@@ -125,7 +125,7 @@ module Nexpose
|
|
125
125
|
# These two fields are keying off role_name to work around a defect.
|
126
126
|
xml << %Q{ allGroups="#{@all_groups || @role_name == 'global-admin'}"}
|
127
127
|
xml << %Q{ allSites="#{@all_sites || @role_name == 'global-admin'}"}
|
128
|
-
xml <<
|
128
|
+
xml << '>'
|
129
129
|
@sites.each do |site|
|
130
130
|
xml << %Q{<site id="#{site}" />}
|
131
131
|
end
|
@@ -142,7 +142,6 @@ module Nexpose
|
|
142
142
|
xml << '</UserSaveRequest>'
|
143
143
|
r = connection.execute(xml, '1.1')
|
144
144
|
if r.success
|
145
|
-
res = []
|
146
145
|
r.res.elements.each('UserSaveResponse') do |attr|
|
147
146
|
@id = attr.attributes['id'].to_i
|
148
147
|
end
|
@@ -162,7 +161,7 @@ module Nexpose
|
|
162
161
|
r.res.elements.each('UserConfigResponse/UserConfig') do |config|
|
163
162
|
id = config.attributes['id']
|
164
163
|
role_name = config.attributes['role-name']
|
165
|
-
authsrcid = config.attributes['authsrcid']
|
164
|
+
#authsrcid = config.attributes['authsrcid']
|
166
165
|
name = config.attributes['name']
|
167
166
|
fullname = config.attributes['fullname']
|
168
167
|
|
data/lib/nexpose/util.rb
CHANGED
@@ -2,23 +2,24 @@ module Nexpose
|
|
2
2
|
module Sanitize
|
3
3
|
def replace_entities(str)
|
4
4
|
ret = str.dup
|
5
|
-
ret.gsub!(/&/,
|
6
|
-
ret.gsub!(/'/,
|
7
|
-
ret.gsub!(/"/,
|
8
|
-
ret.gsub!(/</,
|
9
|
-
ret.gsub!(/>/,
|
5
|
+
ret.gsub!(/&/, '&')
|
6
|
+
ret.gsub!(/'/, ''')
|
7
|
+
ret.gsub!(/"/, '"')
|
8
|
+
ret.gsub!(/</, '<')
|
9
|
+
ret.gsub!(/>/, '>')
|
10
10
|
ret
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
14
|
module XMLUtils
|
15
|
+
|
15
16
|
def parse_xml(xml)
|
16
17
|
::REXML::Document.new(xml.to_s)
|
17
18
|
end
|
18
19
|
|
19
20
|
def make_xml(name, opts={}, data='', append_session_id=true)
|
20
21
|
xml = REXML::Element.new(name)
|
21
|
-
if
|
22
|
+
if @session_id and append_session_id
|
22
23
|
xml.attributes['session-id'] = @session_id
|
23
24
|
end
|
24
25
|
|
data/lib/nexpose/vuln.rb
CHANGED
@@ -29,7 +29,7 @@ module Nexpose
|
|
29
29
|
|
30
30
|
if r.success
|
31
31
|
res = []
|
32
|
-
r.res.elements.each(
|
32
|
+
r.res.elements.each('//VulnerabilityException') do |ve|
|
33
33
|
submitter_comment = ve.elements['submitter-comment']
|
34
34
|
reviewer_comment = ve.elements['reviewer-comment']
|
35
35
|
res << {
|
@@ -112,7 +112,7 @@ module Nexpose
|
|
112
112
|
end
|
113
113
|
|
114
114
|
if scope =~ /All Instances on a Specific Asset/ && (vuln_key || port)
|
115
|
-
raise ArgumentError.new
|
115
|
+
raise ArgumentError.new 'Vulnerability key or port cannot be used with the scope specified'
|
116
116
|
end
|
117
117
|
|
118
118
|
if vuln_key
|
@@ -137,7 +137,7 @@ module Nexpose
|
|
137
137
|
|
138
138
|
r = execute xml, '1.2'
|
139
139
|
if r.success
|
140
|
-
r.res.elements.each(
|
140
|
+
r.res.elements.each('//VulnerabilityExceptionCreateResponse') do |vecr|
|
141
141
|
return vecr.attributes['exception-id']
|
142
142
|
end
|
143
143
|
else
|
@@ -355,7 +355,7 @@ module Nexpose
|
|
355
355
|
|
356
356
|
if r.success
|
357
357
|
r.res.elements.each('VulnerabilityListingResponse/VulnerabilitySummary') do |v|
|
358
|
-
@vulnerability_summaries.push(VulnerabilitySummary.new(v.attributes['id'], v.attributes[
|
358
|
+
@vulnerability_summaries.push(VulnerabilitySummary.new(v.attributes['id'], v.attributes['title'], v.attributes['severity']))
|
359
359
|
end
|
360
360
|
else
|
361
361
|
@error = true
|
@@ -438,8 +438,8 @@ module Nexpose
|
|
438
438
|
if r.success
|
439
439
|
r.res.elements.each('VulnerabilityDetailsResponse/Vulnerability') do |v|
|
440
440
|
@id = v.attributes['id']
|
441
|
-
@title = v.attributes[
|
442
|
-
@severity = v.attributes[
|
441
|
+
@title = v.attributes['title']
|
442
|
+
@severity = v.attributes['severity']
|
443
443
|
@pciSeverity = v.attributes['pciSeverity']
|
444
444
|
@cvssScore = v.attributes['cvssScore']
|
445
445
|
@cvssVector = v.attributes['cvssVector']
|
@@ -498,13 +498,12 @@ module Nexpose
|
|
498
498
|
include Sanitize
|
499
499
|
|
500
500
|
def to_xml
|
501
|
-
xml = '<
|
501
|
+
xml = '<vuln_filter '
|
502
502
|
xml << %Q{ type_mask="#{replace_entities(typeMask)}"}
|
503
503
|
xml << %Q{ maxAlerts="#{replace_entities(maxAlerts)}"}
|
504
504
|
xml << %Q{ severity_threshold="#{replace_entities(severityThreshold)}"}
|
505
505
|
xml << '/>'
|
506
506
|
xml
|
507
507
|
end
|
508
|
-
|
509
508
|
end
|
510
509
|
end
|
metadata
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nexpose
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.9
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- HD Moore
|
9
9
|
- Chris Lee
|
10
|
+
- Michael Daines
|
10
11
|
autorequire:
|
11
12
|
bindir: bin
|
12
13
|
cert_chain: []
|
13
|
-
date: 2013-03-
|
14
|
+
date: 2013-03-08 00:00:00.000000000 Z
|
14
15
|
dependencies:
|
15
16
|
- !ruby/object:Gem::Dependency
|
16
17
|
name: librex
|