nexpose 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/nexpose/scan.rb +184 -139
- data/lib/nexpose/scan_engine.rb +17 -4
- data/lib/nexpose/site.rb +12 -63
- metadata +2 -2
data/lib/nexpose/scan.rb
CHANGED
@@ -48,59 +48,31 @@ module Nexpose
|
|
48
48
|
r.success ? r.attributes['success'] : nil
|
49
49
|
end
|
50
50
|
|
51
|
+
# Retrieve a list of current scan activities across all Scan Engines managed
|
52
|
+
# by Nexpose.
|
53
|
+
#
|
54
|
+
# @return [Array[ScanSummary]] Array of ScanSummary objects associated with
|
55
|
+
# each active scan on the engines.
|
56
|
+
#
|
51
57
|
def scan_activity
|
52
58
|
r = execute(make_xml('ScanActivityRequest', {}))
|
59
|
+
res = []
|
53
60
|
if (r.success)
|
54
|
-
res = []
|
55
61
|
r.res.elements.each("//ScanSummary") do |scan|
|
56
|
-
res <<
|
57
|
-
:scan_id => scan.attributes['scan-id'].to_i,
|
58
|
-
:site_id => scan.attributes['site-id'].to_i,
|
59
|
-
:engine_id => scan.attributes['engine-id'].to_i,
|
60
|
-
:status => scan.attributes['status'].to_s,
|
61
|
-
:start_time => Date.parse(scan.attributes['startTime'].to_s).to_time
|
62
|
-
}
|
62
|
+
res << ScanSummary.parse(scan)
|
63
63
|
end
|
64
|
-
res
|
65
|
-
else
|
66
|
-
false
|
67
64
|
end
|
65
|
+
res
|
68
66
|
end
|
69
67
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
end
|
79
|
-
end
|
80
|
-
r.res.elements.each("//ScanSummary/tasks") do |task|
|
81
|
-
res[:task] = {}
|
82
|
-
task.attributes.keys.each do |k|
|
83
|
-
res[:task][k] = task.attributes[k].to_i
|
84
|
-
end
|
85
|
-
end
|
86
|
-
r.res.elements.each("//ScanSummary/vulnerabilities") do |vuln|
|
87
|
-
res[:vulns] ||= {}
|
88
|
-
k = vuln.attributes['status'] + (vuln.attributes['severity'] ? ("-" + vuln.attributes['severity']) : '')
|
89
|
-
res[:vulns][k] = vuln.attributes['count'].to_i
|
90
|
-
end
|
91
|
-
r.res.elements.each("//ScanSummary") do |summ|
|
92
|
-
res[:summary] = {}
|
93
|
-
summ.attributes.keys.each do |k|
|
94
|
-
res[:summary][k] = summ.attributes[k]
|
95
|
-
if (res[:summary][k] =~ /^\d+$/)
|
96
|
-
res[:summary][k] = res[:summary][k].to_i
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
r.res.elements.each("//ScanSummary/message") do |message|
|
101
|
-
res[:message] = message.text
|
102
|
-
end
|
103
|
-
res
|
68
|
+
# Get scan statistics, including node and vulnerability breakdowns.
|
69
|
+
#
|
70
|
+
# @return [ScanSummary] ScanSummary object providing statistics for the scan.
|
71
|
+
#
|
72
|
+
def scan_statistics(scan_id)
|
73
|
+
r = execute(make_xml('ScanStatisticsRequest', {'scan-id' => scan_id}))
|
74
|
+
if r.success
|
75
|
+
ScanSummary.parse(r.res)
|
104
76
|
else
|
105
77
|
false
|
106
78
|
end
|
@@ -111,115 +83,188 @@ module Nexpose
|
|
111
83
|
# Object that represents a summary of a scan.
|
112
84
|
#
|
113
85
|
class ScanSummary
|
86
|
+
|
114
87
|
# The Scan ID of the Scan
|
115
88
|
attr_reader :scan_id
|
116
|
-
# The
|
89
|
+
# The site that was scanned.
|
90
|
+
attr_reader :site_id
|
91
|
+
# The Engine ID the scan was dispatched to.
|
117
92
|
attr_reader :engine_id
|
118
|
-
# TODO: add description
|
119
|
-
attr_reader :name
|
120
93
|
# The scan start time
|
121
|
-
attr_reader :
|
94
|
+
attr_reader :start_time
|
122
95
|
# The scan finish time
|
123
|
-
attr_reader :
|
124
|
-
# The scan status
|
96
|
+
attr_reader :end_time
|
97
|
+
# The scan status.
|
98
|
+
# One of: running|finished|stopped|error|dispatched|paused|aborted|uknown
|
125
99
|
attr_reader :status
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
#
|
131
|
-
attr_reader :
|
132
|
-
#
|
133
|
-
attr_reader :
|
134
|
-
#
|
135
|
-
attr_reader :
|
136
|
-
# The number of filtered nodes
|
137
|
-
attr_reader :nodes_filtered
|
138
|
-
# The number of unresolved nodes
|
139
|
-
attr_reader :nodes_unresolved
|
140
|
-
# The number of "other" nodes
|
141
|
-
attr_reader :nodes_other
|
142
|
-
# Confirmed vulnerabilities found (indexed by severity)
|
143
|
-
# Associative array, indexed by severity
|
144
|
-
attr_reader :vuln_exploit
|
145
|
-
# Unconfirmed vulnerabilities found (indexed by severity)
|
146
|
-
# Associative array, indexed by severity
|
147
|
-
attr_reader :vuln_version
|
148
|
-
# Not vulnerable checks run (confirmed)
|
149
|
-
attr_reader :not_vuln_exploit
|
150
|
-
# Not vulnerable checks run (unconfirmed)
|
151
|
-
attr_reader :not_vuln_version
|
152
|
-
# Vulnerability check errors
|
153
|
-
attr_reader :vuln_error
|
154
|
-
# Vulnerability checks disabled
|
155
|
-
attr_reader :vuln_disabled
|
156
|
-
# Vulnerability checks other
|
157
|
-
attr_reader :vuln_other
|
100
|
+
|
101
|
+
# The reason the scan was stopped or failed, if applicable.
|
102
|
+
attr_reader :message
|
103
|
+
|
104
|
+
# Task statistics, including pending, active, and completed tasks.
|
105
|
+
attr_reader :tasks
|
106
|
+
# Node statistics, including live, dead, filtered, and unresolved.
|
107
|
+
attr_reader :nodes
|
108
|
+
# Vulnerability statistics, including statuses, severities, and counts.
|
109
|
+
attr_reader :vulnerabilities
|
158
110
|
|
159
111
|
# Constructor
|
160
|
-
|
161
|
-
|
112
|
+
def initialize(scan_id, site_id, engine_id, status, start_time, end_time, message, tasks, nodes, vulnerabilities)
|
113
|
+
@scan_id, @site_id, @engine_id, @status, @start_time, @end_time = scan_id, site_id, engine_id, status, start_time, end_time
|
114
|
+
@message, @tasks, @nodes, @vulnerabilities = message, tasks, nodes, vulnerabilities
|
115
|
+
end
|
162
116
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
117
|
+
# Parse a response from a Nexpose console into a valid ScanSummary object.
|
118
|
+
#
|
119
|
+
# @param [REXML::Document] rexml XML document to parse.
|
120
|
+
# @return [ScanSummary] Scan summary represented by the XML.
|
121
|
+
#
|
122
|
+
def self.parse(rexml)
|
123
|
+
tasks = Tasks.parse(rexml)
|
124
|
+
nodes = Nodes.parse(rexml)
|
125
|
+
vulns = Vulnerabilities.parse(rexml)
|
126
|
+
msg = nil
|
127
|
+
rexml.elements.each("//ScanSummary/message") do |message|
|
128
|
+
msg = message.text
|
129
|
+
end
|
169
130
|
|
131
|
+
rexml.elements.each("//ScanSummary") do |scan|
|
132
|
+
start_time = DateTime.parse(scan.attributes['startTime'].to_s).to_time
|
133
|
+
# End time is often not present, since reporting on running scans.
|
134
|
+
if scan.attributes['endTime']
|
135
|
+
end_time = DateTime.parse(scan.attributes['endTime'].to_s).to_time
|
136
|
+
end
|
137
|
+
return ScanSummary.new(scan.attributes['scan-id'].to_i,
|
138
|
+
scan.attributes['site-id'].to_i,
|
139
|
+
scan.attributes['engine-id'].to_i,
|
140
|
+
scan.attributes['status'],
|
141
|
+
start_time,
|
142
|
+
end_time,
|
143
|
+
msg,
|
144
|
+
tasks,
|
145
|
+
nodes,
|
146
|
+
vulns)
|
147
|
+
end
|
170
148
|
end
|
171
149
|
|
172
|
-
|
150
|
+
# Value class to tracking task counts.
|
151
|
+
#
|
152
|
+
class Tasks
|
153
|
+
attr_reader :pending, :active, :completed
|
173
154
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
#
|
197
|
-
# # Print out the number of pending tasks left in the scan
|
198
|
-
# puts scanStatistics.scan_summary.tasks_pending
|
199
|
-
#
|
200
|
-
class ScanStatistics
|
201
|
-
# true if an error condition exists; false otherwise
|
202
|
-
attr_reader :error
|
203
|
-
# Error message string
|
204
|
-
attr_reader :error_msg
|
205
|
-
# The last XML request sent by this object
|
206
|
-
attr_reader :request_xml
|
207
|
-
# The last XML response received by this object
|
208
|
-
attr_reader :reseponse_xml
|
209
|
-
# The Scan ID
|
210
|
-
attr_reader :scan_id
|
211
|
-
# The ScanSummary of the scan
|
212
|
-
attr_reader :scan_summary
|
213
|
-
# The NSC Connection associated with this object
|
214
|
-
attr_reader :connection
|
155
|
+
def initialize(pending, active, completed)
|
156
|
+
@pending, @active, @completed = pending, active, completed
|
157
|
+
end
|
158
|
+
|
159
|
+
# Parse REXML to Tasks object.
|
160
|
+
#
|
161
|
+
# @param [REXML::Document] rexml XML document to parse.
|
162
|
+
# @return [Tasks] Task summary represented by the XML.
|
163
|
+
#
|
164
|
+
def self.parse(rexml)
|
165
|
+
rexml.elements.each("//ScanSummary/tasks") do |task|
|
166
|
+
return Tasks.new(task.attributes['pending'],
|
167
|
+
task.attributes['active'],
|
168
|
+
task.attributes['completed'])
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# Value class for tracking node counts.
|
174
|
+
#
|
175
|
+
class Nodes
|
176
|
+
attr_reader :live, :dead, :filtered, :unresolved, :other
|
215
177
|
|
216
|
-
|
217
|
-
|
178
|
+
def initialize(live, dead, filtered, unresolved, other)
|
179
|
+
@live, @dead, @filtered, @unresolved, @other = live, dead, filtered, unresolved, other
|
180
|
+
end
|
218
181
|
|
219
|
-
|
220
|
-
|
221
|
-
@
|
222
|
-
@
|
182
|
+
# Parse REXML to Nodes object.
|
183
|
+
#
|
184
|
+
# @param [REXML::Document] rexml XML document to parse.
|
185
|
+
# @return [Nodes] Node summary represented by the XML.
|
186
|
+
#
|
187
|
+
def self.parse(rexml)
|
188
|
+
rexml.elements.each("//ScanSummary/nodes") do |node|
|
189
|
+
return Nodes.new(node.attributes['live'],
|
190
|
+
node.attributes['dead'],
|
191
|
+
node.attributes['filtered'],
|
192
|
+
node.attributes['unresolved'],
|
193
|
+
node.attributes['other'])
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# Value class for tracking vulnerability counts.
|
199
|
+
#
|
200
|
+
class Vulnerabilities
|
201
|
+
attr_reader :vuln_exploit, :vuln_version, :vuln_potential,
|
202
|
+
:not_vuln_exploit, :not_vuln_version,
|
203
|
+
:error, :disabled, :other
|
204
|
+
|
205
|
+
def initialize(vuln_exploit, vuln_version, vuln_potential,
|
206
|
+
not_vuln_exploit, not_vuln_version,
|
207
|
+
error, disabled, other)
|
208
|
+
@vuln_exploit, @vuln_version, @vuln_potential,
|
209
|
+
@not_vuln_exploit, @not_vuln_version,
|
210
|
+
@error, @disabled, @other =
|
211
|
+
vuln_exploit, vuln_version, vuln_potential,
|
212
|
+
not_vuln_exploit, not_vuln_version,
|
213
|
+
error, disabled, other
|
214
|
+
end
|
215
|
+
|
216
|
+
# Parse REXML to Vulnerabilities object.
|
217
|
+
#
|
218
|
+
# @param [REXML::Document] rexml XML document to parse.
|
219
|
+
# @return [Vulnerabilities] Vulnerability summary represented by the XML.
|
220
|
+
#
|
221
|
+
def self.parse(rexml)
|
222
|
+
map = {}
|
223
|
+
rexml.elements.each("//ScanSummary/vulnerabilities") do |vuln|
|
224
|
+
status = map[vuln.attributes['status']]
|
225
|
+
if status && vuln.attributes['status'] =~ /^vuln-/
|
226
|
+
status.add_severity(vuln.attributes['severity'].to_i, vuln.attributes['count'].to_i)
|
227
|
+
else
|
228
|
+
map[vuln.attributes['status']] = Status.new(vuln.attributes['severity'], vuln.attributes['count'].to_i)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
Vulnerabilities.new(map['vuln-exploit'],
|
232
|
+
map['vuln-version'],
|
233
|
+
map['vuln-potential'],
|
234
|
+
map['not-vuln-exploit'],
|
235
|
+
map['not-vuln-version'],
|
236
|
+
map['error'],
|
237
|
+
map['disabled'],
|
238
|
+
map['other'])
|
239
|
+
end
|
240
|
+
|
241
|
+
# Value class for tracking vulnerability status counts.
|
242
|
+
#
|
243
|
+
# Severities will only be mapped if they are provided in the response,
|
244
|
+
# which currently only happens for vuln-exploit, vuln-version,
|
245
|
+
# and vuln-potential.
|
246
|
+
#
|
247
|
+
class Status
|
248
|
+
attr_reader :severities, :count
|
249
|
+
|
250
|
+
def initialize(severity = nil, count = 0)
|
251
|
+
if severity
|
252
|
+
@severities = {}
|
253
|
+
@count = 0
|
254
|
+
add_severity(severity.to_i, count)
|
255
|
+
else
|
256
|
+
@severities = nil
|
257
|
+
@count = count
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
# For vuln-exploit, vuln-version, and vuln-potential,
|
262
|
+
# map the count at a severity level, but also maintain an overall count.
|
263
|
+
def add_severity(severity, count)
|
264
|
+
@count += count
|
265
|
+
@severities[severity] = count
|
266
|
+
end
|
267
|
+
end
|
223
268
|
end
|
224
269
|
end
|
225
270
|
|
data/lib/nexpose/scan_engine.rb
CHANGED
@@ -7,6 +7,23 @@ module Nexpose
|
|
7
7
|
xml = make_xml('EngineDeleteRequest', {'engine-id' => engine_id})
|
8
8
|
execute(xml, '1.2')
|
9
9
|
end
|
10
|
+
|
11
|
+
# Provide a list of current scan activities for a specific Scan Engine.
|
12
|
+
#
|
13
|
+
# @return [Array[ScanSummary]] Array of ScanSummary objects associated with
|
14
|
+
# each active scan on the engine.
|
15
|
+
#
|
16
|
+
def engine_activity(engine_id)
|
17
|
+
xml = make_xml('EngineActivityRequest', {'engine-id' => engine_id})
|
18
|
+
r = execute(xml)
|
19
|
+
arr = []
|
20
|
+
if r.success
|
21
|
+
r.res.elements.each("//ScanSummary") do |scan_event|
|
22
|
+
arr << ScanSummary.parse(scan_event)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
arr
|
26
|
+
end
|
10
27
|
end
|
11
28
|
|
12
29
|
# ==== Description
|
@@ -45,10 +62,6 @@ module Nexpose
|
|
45
62
|
end
|
46
63
|
end
|
47
64
|
|
48
|
-
# TODO
|
49
|
-
class EngineActivity
|
50
|
-
end
|
51
|
-
|
52
65
|
# ==== Description
|
53
66
|
# Object that represents the summary of a scan engine.
|
54
67
|
#
|
data/lib/nexpose/site.rb
CHANGED
@@ -56,28 +56,21 @@ module Nexpose
|
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
|
-
|
60
|
-
#
|
61
|
-
#
|
62
|
-
|
59
|
+
# Retrieve a list of all previous scans of the site.
|
60
|
+
#
|
61
|
+
# @param [FixNum] site_id Site ID to request scan history for.
|
62
|
+
# @return [Array[ScanSummary]] Array of ScanSummary objects representing
|
63
|
+
# each scan run to date on the site provided.
|
64
|
+
#
|
63
65
|
def site_scan_history(site_id)
|
64
|
-
r = execute(make_xml('SiteScanHistoryRequest', {'site-id' => site_id
|
65
|
-
|
66
|
-
if
|
67
|
-
res
|
68
|
-
|
69
|
-
res << {
|
70
|
-
:site_id => site_scan_history.attributes['site-id'].to_i,
|
71
|
-
:scan_id => site_scan_history.attributes['scan-id'].to_i,
|
72
|
-
:engine_id => site_scan_history.attributes['engine-id'].to_i,
|
73
|
-
:start_time => site_scan_history.attributes['startTime'].to_s,
|
74
|
-
:end_time => site_scan_history.attributes['endTime'].to_s
|
75
|
-
}
|
66
|
+
r = execute(make_xml('SiteScanHistoryRequest', {'site-id' => site_id}))
|
67
|
+
res = []
|
68
|
+
if r.success
|
69
|
+
r.res.elements.each("//ScanSummary") do |scan_event|
|
70
|
+
res << ScanSummary.parse(scan_event)
|
76
71
|
end
|
77
|
-
res
|
78
|
-
else
|
79
|
-
false
|
80
72
|
end
|
73
|
+
res
|
81
74
|
end
|
82
75
|
|
83
76
|
#-----------------------------------------------------------------------
|
@@ -480,50 +473,6 @@ module Nexpose
|
|
480
473
|
end
|
481
474
|
end
|
482
475
|
|
483
|
-
# === Description
|
484
|
-
# Object that represents the scan history of a site.
|
485
|
-
#
|
486
|
-
class SiteScanHistory
|
487
|
-
# true if an error condition exists; false otherwise
|
488
|
-
attr_reader :error
|
489
|
-
# Error message string
|
490
|
-
attr_reader :error_msg
|
491
|
-
# The last XML request sent by this object
|
492
|
-
attr_reader :request_xml
|
493
|
-
# The last response received by this object
|
494
|
-
attr_reader :response
|
495
|
-
# The NSC Connection associated with this object
|
496
|
-
attr_reader :connection
|
497
|
-
# The Site ID
|
498
|
-
attr_reader :site_id
|
499
|
-
# //Array containing (ScanSummary*)
|
500
|
-
attr_reader :scan_summaries
|
501
|
-
|
502
|
-
def initialize(connection, id)
|
503
|
-
@site_id = id
|
504
|
-
@error = false
|
505
|
-
@connection = connection
|
506
|
-
@scan_summaries = []
|
507
|
-
|
508
|
-
@request_xml = '<SiteScanHistoryRequest' + ' session-id="' + @connection.session_id + '" site-id="' + "#{@site_id}" + '"/>'
|
509
|
-
r = @connection.execute(@request_xml)
|
510
|
-
@response = r
|
511
|
-
|
512
|
-
if r and r.success
|
513
|
-
r.res.elements.each('//ScanSummary') do |summary|
|
514
|
-
scan_id=summary.attributes['scan-id'].to_i
|
515
|
-
engine_id=summary.attributes['engine-id'].to_i
|
516
|
-
name=summary.attributes['name'].to_s
|
517
|
-
start_time=summary.attributes['startTime'].to_s
|
518
|
-
end_time=summary.attributes['endTime'].to_s
|
519
|
-
status=summary.attributes['status'].to_s
|
520
|
-
scan_summary = ScanSummary.new(scan_id, engine_id, name, start_time, end_time, status)
|
521
|
-
scan_summaries << scan_summary
|
522
|
-
end
|
523
|
-
end
|
524
|
-
end
|
525
|
-
end
|
526
|
-
|
527
476
|
# === Description
|
528
477
|
# Object that represents a listing of devices for a site or the entire NSC. Note that only devices which are accessible to the account used to create the connection object will be returned. This object is created and populated automatically with the instantiation of a new Site object.
|
529
478
|
#
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
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.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2013-02-06 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: librex
|