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