nexpose 0.0.98 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.markdown +17 -7
- data/Rakefile +17 -22
- data/lib/README.md +5 -0
- data/lib/nexpose.rb +104 -130
- data/lib/nexpose/api_request.rb +133 -144
- data/lib/nexpose/common.rb +138 -0
- data/lib/nexpose/connection.rb +117 -106
- data/lib/nexpose/creds.rb +292 -279
- data/lib/nexpose/error.rb +21 -21
- data/lib/nexpose/manage.rb +83 -0
- data/lib/nexpose/misc.rb +85 -122
- data/lib/nexpose/report.rb +783 -603
- data/lib/nexpose/role.rb +27 -0
- data/lib/nexpose/scan.rb +264 -285
- data/lib/nexpose/scan_engine.rb +344 -350
- data/lib/nexpose/silo.rb +348 -347
- data/lib/nexpose/site.rb +826 -898
- data/lib/nexpose/ticket.rb +108 -108
- data/lib/nexpose/user.rb +223 -221
- data/lib/nexpose/util.rb +36 -36
- data/lib/nexpose/vuln.rb +510 -520
- metadata +37 -23
- data/README +0 -0
- data/nexpose.gemspec +0 -20
data/lib/nexpose/scan_engine.rb
CHANGED
@@ -1,350 +1,344 @@
|
|
1
|
-
module Nexpose
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
#
|
16
|
-
attr_reader :
|
17
|
-
#
|
18
|
-
attr_reader :
|
19
|
-
# The
|
20
|
-
attr_reader :
|
21
|
-
|
22
|
-
|
23
|
-
#
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
#
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
#
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
# A
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
#
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
xml <<
|
165
|
-
xml
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
xml
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
@engines
|
219
|
-
end
|
220
|
-
|
221
|
-
#
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
xml
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
xml
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
def
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
@error = true
|
346
|
-
@error_msg = 'EnginePoolListingResponse Parse Error'
|
347
|
-
end
|
348
|
-
end
|
349
|
-
end
|
350
|
-
end
|
1
|
+
module Nexpose
|
2
|
+
module NexposeAPI
|
3
|
+
include XMLUtils
|
4
|
+
|
5
|
+
# Removes a scan engine from the list of available engines.
|
6
|
+
def delete_engine(engine_id)
|
7
|
+
xml = make_xml('EngineDeleteRequest', {'engine-id' => engine_id})
|
8
|
+
execute(xml, '1.2')
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# ==== Description
|
13
|
+
# Object that represents a listing of all of the scan engines available on to an NSC.
|
14
|
+
class EngineListing
|
15
|
+
# true if an error condition exists; false otherwise
|
16
|
+
attr_reader :error
|
17
|
+
# Error message string
|
18
|
+
attr_reader :error_msg
|
19
|
+
# The last XML request sent by this object
|
20
|
+
attr_reader :connection
|
21
|
+
# Array containing (EngineSummary*)
|
22
|
+
attr_reader :engines
|
23
|
+
# The number of scan engines
|
24
|
+
attr_reader :engine_count
|
25
|
+
|
26
|
+
# Constructor
|
27
|
+
# EngineListing (connection)
|
28
|
+
def initialize(connection)
|
29
|
+
@connection = connection
|
30
|
+
@engines = []
|
31
|
+
@engine_count = 0
|
32
|
+
@error = false
|
33
|
+
r = @connection.execute('<EngineListingRequest session-id="' + @connection.session_id + '"/>', '1.2')
|
34
|
+
|
35
|
+
if r.success
|
36
|
+
r.res.elements.each('EngineListingResponse/EngineSummary') do |v|
|
37
|
+
@engines.push(EngineSummary.new(v.attributes['id'], v.attributes['name'], v.attributes['address'],
|
38
|
+
v.attributes['port'], v.attributes['status']))
|
39
|
+
end
|
40
|
+
else
|
41
|
+
@error = true
|
42
|
+
@error_msg = 'EngineListingRequest Parse Error'
|
43
|
+
end
|
44
|
+
@engine_count = @engines.length
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# TODO
|
49
|
+
class EngineActivity
|
50
|
+
end
|
51
|
+
|
52
|
+
# ==== Description
|
53
|
+
# Object that represents the summary of a scan engine.
|
54
|
+
#
|
55
|
+
# ==== Examples
|
56
|
+
#
|
57
|
+
# # Create a new Nexpose Connection on the default port and Login
|
58
|
+
# nsc = Connection.new("10.1.40.10","nxadmin","password")
|
59
|
+
# nsc.login()
|
60
|
+
#
|
61
|
+
# # Get the engine listing for the connection
|
62
|
+
# enginelisting = EngineListing.new(nsc)
|
63
|
+
#
|
64
|
+
# # Print out the status of the first scan engine
|
65
|
+
# puts enginelisting.engines[0].status
|
66
|
+
#
|
67
|
+
class EngineSummary
|
68
|
+
# A unique ID that identifies this scan engine
|
69
|
+
attr_reader :id
|
70
|
+
# The name of this scan engine
|
71
|
+
attr_reader :name
|
72
|
+
# The hostname or IP address of the engine
|
73
|
+
attr_reader :address
|
74
|
+
# The port there the engine is listening
|
75
|
+
attr_reader :port
|
76
|
+
# The engine status (active|pending-auth| incompatible|not-responding|unknown)
|
77
|
+
attr_reader :status
|
78
|
+
# A parameter that specifies whether the engine has a global
|
79
|
+
# or silo-specific scope.
|
80
|
+
attr_reader :scope
|
81
|
+
|
82
|
+
# Constructor
|
83
|
+
# EngineSummary(id, name, address, port, status, scope)
|
84
|
+
def initialize(id, name, address, port, status, scope = 'silo')
|
85
|
+
@id = id
|
86
|
+
@name = name
|
87
|
+
@address = address
|
88
|
+
@port = port
|
89
|
+
@status = status
|
90
|
+
@scope = scope
|
91
|
+
end
|
92
|
+
|
93
|
+
def to_s
|
94
|
+
"Engine: #@name [ID: #@id] #@address:#@port, Status: #@status, Scope: #@scope"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
#-------------------------------------------------------------------------------------------------------------------
|
99
|
+
#
|
100
|
+
#-------------------------------------------------------------------------------------------------------------------
|
101
|
+
class EngineConfig
|
102
|
+
attr_accessor :id
|
103
|
+
attr_accessor :address
|
104
|
+
attr_accessor :name
|
105
|
+
attr_accessor :port
|
106
|
+
attr_accessor :scope
|
107
|
+
attr_accessor :priority
|
108
|
+
|
109
|
+
# An array of site IDs. Currently do not support 'name' value,
|
110
|
+
# which is optional in the API.
|
111
|
+
attr_accessor :sites
|
112
|
+
|
113
|
+
def initialize(connection, id = -1)
|
114
|
+
@connection = connection
|
115
|
+
@id = id
|
116
|
+
@address = nil
|
117
|
+
@name = nil
|
118
|
+
@port = 40814
|
119
|
+
@scope = 'silo'
|
120
|
+
@priority = 'normal'
|
121
|
+
@sites = []
|
122
|
+
|
123
|
+
# If valid ID provided, retrieve data from server.
|
124
|
+
if id > 0
|
125
|
+
xml = '<EngineConfigRequest session-id="' + @connection.session_id + '"'
|
126
|
+
xml << %Q{ engine-id="#{id}"}
|
127
|
+
xml << ' />'
|
128
|
+
r = @connection.execute(xml, '1.2')
|
129
|
+
|
130
|
+
if r.success
|
131
|
+
r.res.elements.each('EngineConfigResponse/EngineConfig') do |v|
|
132
|
+
@id = v.attributes['id']
|
133
|
+
@address = v.attributes['address']
|
134
|
+
@name = v.attributes['name']
|
135
|
+
@port = v.attributes['port']
|
136
|
+
@scope = v.attributes['scope']
|
137
|
+
v.elements.each('Site') do |s|
|
138
|
+
@sites << s.attributes['id']
|
139
|
+
end
|
140
|
+
end
|
141
|
+
else
|
142
|
+
@error = true
|
143
|
+
@error_msg = 'EngineConfigRequest Parse Error'
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def add_site(site_id)
|
149
|
+
sites << site_id
|
150
|
+
end
|
151
|
+
|
152
|
+
def to_xml
|
153
|
+
xml = '<EngineConfig'
|
154
|
+
xml << %Q{ id="#{id}"}
|
155
|
+
xml << %Q{ address="#{address}"}
|
156
|
+
xml << %Q{ name="#{name}"}
|
157
|
+
xml << %Q{ port="#{port}"}
|
158
|
+
xml << %Q{ scope="#{scope}"}
|
159
|
+
xml << %Q{ priority="#{priority}"} if (priority)
|
160
|
+
xml << '>'
|
161
|
+
sites.each do |site|
|
162
|
+
xml << %Q{<Site id="#{site}" />}
|
163
|
+
end
|
164
|
+
xml << '</EngineConfig>'
|
165
|
+
xml
|
166
|
+
end
|
167
|
+
|
168
|
+
# Save this engine configuration
|
169
|
+
# Example usage:
|
170
|
+
# engine = EngineConfig.new(@nsc)
|
171
|
+
# engine.address = 'atlanta.company.com'
|
172
|
+
# engine.name = 'Atlanta Engine'
|
173
|
+
# engine.save()
|
174
|
+
def save
|
175
|
+
xml = '<EngineSaveRequest session-id="' + @connection.session_id + '">'
|
176
|
+
xml << to_xml
|
177
|
+
xml << '</EngineSaveRequest>'
|
178
|
+
|
179
|
+
r = @connection.execute(xml, '1.2')
|
180
|
+
if r.success
|
181
|
+
r.res.elements.each('EngineSaveResponse/EngineConfig') do |v|
|
182
|
+
return @id = v.attributes['id']
|
183
|
+
end
|
184
|
+
else (r.success)
|
185
|
+
@error = true
|
186
|
+
@error_msg = 'EngineSaveRequest Parse Error'
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def delete
|
191
|
+
@connection.delete_engine(@id)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# Core objects for creating an engine pool
|
196
|
+
# Example usage:
|
197
|
+
# pool = EnginePool.new('East Coast Pool')
|
198
|
+
# pool.add('New York Engine')
|
199
|
+
# pool.add('Georgia Engine')
|
200
|
+
# pool.create(@nsc)
|
201
|
+
class EnginePool
|
202
|
+
attr_accessor :id
|
203
|
+
attr_accessor :name
|
204
|
+
attr_accessor :scope
|
205
|
+
# Array containing (EngineSummary*)
|
206
|
+
attr_accessor :engines
|
207
|
+
|
208
|
+
def initialize(name, id = -1, scope = 'silo')
|
209
|
+
@name = name
|
210
|
+
@id = id
|
211
|
+
@scope = scope
|
212
|
+
@engines = []
|
213
|
+
end
|
214
|
+
|
215
|
+
# Add an engine to the pool by name (not ID).
|
216
|
+
# Only use this for creating pools.
|
217
|
+
def add(engine)
|
218
|
+
@engines << EngineSummary.new(-1, engine, 'nowhere', 40814, 'unknown')
|
219
|
+
end
|
220
|
+
|
221
|
+
# Creates a new engine pool, and adds scan engines to the pool.
|
222
|
+
def create(connection)
|
223
|
+
xml = '<EnginePoolCreateRequest session-id="' + connection.session_id + '">'
|
224
|
+
xml << %Q{<EnginePool name="#@name" scope="#@scope">}
|
225
|
+
@engines.each do |engine|
|
226
|
+
xml << %Q{<Engine name="#{engine.name}" />}
|
227
|
+
end
|
228
|
+
xml << '</EnginePool>'
|
229
|
+
xml << '</EnginePoolCreateRequest>'
|
230
|
+
|
231
|
+
r = connection.execute(xml, '1.2')
|
232
|
+
if r.success
|
233
|
+
r.res.elements.each('EnginePoolCreateResponse') do |v|
|
234
|
+
@id = v.attributes['id']
|
235
|
+
end
|
236
|
+
else
|
237
|
+
@error = true
|
238
|
+
@error_msg = 'EnginePoolCreateResponse Parse Error'
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
# Deletes an engine pool
|
243
|
+
def delete(connection)
|
244
|
+
xml = '<EnginePoolDeleteRequest session-id="' + connection.session_id + '">'
|
245
|
+
xml << %Q{<EnginePool name="#@name" scope="#@scope" />}
|
246
|
+
xml << '</EnginePoolDeleteRequest>'
|
247
|
+
|
248
|
+
r = connection.execute(xml, '1.2')
|
249
|
+
unless r.success
|
250
|
+
@error = true
|
251
|
+
@error_msg = 'EnginePoolDeleteResponse Parse Error'
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
# Updates a specific role with new information. An EnginePoolUpdate is
|
256
|
+
# similar to an EnginePoolCreate, except that an EnginePoolUpdate replaces
|
257
|
+
# any previously existing information with the new information specified in
|
258
|
+
# the EnginePoolUpdateRequest.
|
259
|
+
def update(connection)
|
260
|
+
xml = '<EnginePoolUpdateRequest session-id="' + connection.session_id + '">'
|
261
|
+
xml << %Q{<EnginePool id="#@id" name="#@name" scope="#@scope">}
|
262
|
+
@engines.each do |engine|
|
263
|
+
xml << %Q{<Engine name="#{engine.name}" />}
|
264
|
+
end
|
265
|
+
xml << '</EnginePool>'
|
266
|
+
xml << '</EnginePoolUpdateRequest>'
|
267
|
+
|
268
|
+
r = connection.execute(xml, '1.2')
|
269
|
+
if r.success
|
270
|
+
r.res.elements.each('EnginePoolUpdateResponse') do |v|
|
271
|
+
@id = v.attributes['id']
|
272
|
+
end
|
273
|
+
else
|
274
|
+
@error = true
|
275
|
+
@error_msg = 'EnginePoolCreateResponse Parse Error'
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
# Returns detailed information about a single engine pool.
|
280
|
+
def load_details(connection)
|
281
|
+
xml = '<EnginePoolDetailsRequest session-id="' + connection.session_id + '">'
|
282
|
+
xml << %Q{<EnginePool name="#@name" scope="#@scope" />}
|
283
|
+
xml << '</EnginePoolDetailsRequest>'
|
284
|
+
|
285
|
+
r = connection.execute(xml, '1.2')
|
286
|
+
if r.success
|
287
|
+
r.res.elements.each('EnginePoolDetailsResponse/EnginePool') do |pool|
|
288
|
+
@id = pool.attributes['id']
|
289
|
+
@name = pool.attributes['name']
|
290
|
+
@scope = pool.attributes['scope']
|
291
|
+
@engines = []
|
292
|
+
r.res.elements.each('EnginePoolDetailsResponse/EnginePool/EngineSummary') do |summary|
|
293
|
+
@engines.push(EngineSummary.new(summary.attributes['id'],
|
294
|
+
summary.attributes['name'],
|
295
|
+
summary.attributes['address'],
|
296
|
+
summary.attributes['port'],
|
297
|
+
summary.attributes['status'],
|
298
|
+
summary.attributes['scope']))
|
299
|
+
end
|
300
|
+
end
|
301
|
+
else
|
302
|
+
@error = true
|
303
|
+
@error_msg = 'EnginePoolListingResponse Parse Error'
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
def to_s
|
308
|
+
"Engine Pool: #@name [ID: #@id], Scope: #@scope\n" + @engines.map { |engine| " #{engine}" }.join("\n")
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
# A summary of an engine pool.
|
313
|
+
class EnginePoolSummary
|
314
|
+
attr_reader :id
|
315
|
+
attr_reader :name
|
316
|
+
attr_reader :scope
|
317
|
+
|
318
|
+
def initialize(id, name, scope = 'silo')
|
319
|
+
@id = id
|
320
|
+
@name = name
|
321
|
+
@scope = scope
|
322
|
+
end
|
323
|
+
|
324
|
+
def to_s
|
325
|
+
"Engine Pool: #@name [ID: #@id], scope: #@scope"
|
326
|
+
end
|
327
|
+
|
328
|
+
# Returns a summary list of all engine pools.
|
329
|
+
def self.listing(connection)
|
330
|
+
xml = '<EnginePoolListingRequest session-id="' + connection.session_id + '" />'
|
331
|
+
r = connection.execute(xml, '1.2')
|
332
|
+
if r.success
|
333
|
+
list = []
|
334
|
+
r.res.elements.each('EnginePoolListingResponse/EnginePoolSummary') do |eps|
|
335
|
+
list << EnginePoolSummary.new(eps.attributes['id'], eps.attributes['name'], eps.attributes['scope'])
|
336
|
+
end
|
337
|
+
list
|
338
|
+
else
|
339
|
+
@error = true
|
340
|
+
@error_msg = 'EnginePoolListingResponse Parse Error'
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|