nexpose 0.2.8 → 0.5.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.
@@ -0,0 +1,368 @@
1
+ module Nexpose
2
+ module NexposeAPI
3
+ include XMLUtils
4
+
5
+ # Retrieve vulnerability exceptions.
6
+ #
7
+ # @param [String] status Filter exceptions by the current status.
8
+ # @see Nexpose::VulnException::Status
9
+ # @param [String] duration A time interval in the format "PnYnMnDTnHnMnS".
10
+ # @return [Array[VulnException]] List of matching vulnerability exceptions.
11
+ #
12
+ def list_vuln_exceptions(status = nil, duration = nil)
13
+ option = {}
14
+ option['status'] = status if status
15
+ option['time-duration'] = duration if duration
16
+ xml = make_xml('VulnerabilityExceptionListingRequest', option)
17
+ response = execute(xml, '1.2')
18
+
19
+ xs = []
20
+ if response.success
21
+ response.res.elements.each('//VulnerabilityException') do |ve|
22
+ xs << VulnException.parse(ve)
23
+ end
24
+ end
25
+ xs
26
+ end
27
+
28
+ alias_method :vuln_exceptions, :list_vuln_exceptions
29
+
30
+ # Resubmit a vulnerability exception request with a new comment and reason
31
+ # after an exception has been rejected.
32
+ #
33
+ # You can only resubmit a request that has a "Rejected" status; if an
34
+ # exception is "Approved" or "Under Review" you will receive an error
35
+ # message stating that the exception request cannot be resubmitted.
36
+ #
37
+ # @param [Fixnum] id Unique identifier of the exception to resubmit.
38
+ # @param [String] comment Comment to justify the exception resubmission.
39
+ # @param [String] reason The reason for the exception status, if changing.
40
+ # @see Nexpose::VulnException::Reason
41
+ # @return [Boolean] Whether or not the resubmission was valid.
42
+ #
43
+ def resubmit_vuln_exception(id, comment, reason = nil)
44
+ options = { 'exception-id' => id }
45
+ options['reason'] = reason if reason
46
+ xml = make_xml('VulnerabilityExceptionResubmitRequest', options)
47
+ comment_xml = make_xml('comment', {}, comment, false)
48
+ xml.add_element(comment_xml)
49
+ r = execute(xml, '1.2')
50
+ r.success
51
+ end
52
+
53
+ # Recall a vulnerability exception. Recall is used by a submitter to undo an
54
+ # exception request that has not been approved yet.
55
+ #
56
+ # You can only recall a vulnerability exception that has 'Under Review'
57
+ # status.
58
+ #
59
+ # @param [Fixnum] id Unique identifier of the exception to resubmit.
60
+ # @return [Boolean] Whether or not the recall was accepted by the console.
61
+ #
62
+ def recall_vuln_exception(id)
63
+ xml = make_xml('VulnerabilityExceptionRecallRequest',
64
+ { 'exception-id' => id })
65
+ execute(xml, '1.2').success
66
+ end
67
+
68
+ # Delete an existing vulnerability exception.
69
+ #
70
+ # @param [Fixnum] id The ID of a vuln exception.
71
+ # @return [Boolean] Whether or not deletion was successful.
72
+ #
73
+ def delete_vuln_exception(id)
74
+ xml = make_xml('VulnerabilityExceptionDeleteRequest',
75
+ { 'exception-id' => id })
76
+ execute(xml, '1.2').success
77
+ end
78
+ end
79
+
80
+ # A vulnerability exception.
81
+ #
82
+ # Certain attributes are necessary for some exception scopes, even though
83
+ # they are optional otherwise.
84
+ # • An exception for all instances of a vulnerability on all assets only
85
+ # requires the vuln_id attribute. The device_id, vuln_key and port
86
+ # attributes are ignored for this scope type.
87
+ # • An exception for all instances on a specific asset requires the vuln_id
88
+ # and device_id attributes. The vuln_key and port attributes are ignored for
89
+ # this scope type.
90
+ # • An exception for a specific instance of a vulnerability on a specific
91
+ # asset requires the vuln_id, device_id. Additionally, the port and/or the
92
+ # key attribute must be specified.
93
+ #
94
+ class VulnException
95
+
96
+ # Unique identifier assigned to an exception.
97
+ attr_accessor :id
98
+ # Unique identifier of a vulnerability.
99
+ attr_accessor :vuln_id
100
+ # The name of submitter of the exception.
101
+ attr_accessor :submitter
102
+ # The name of the reviewer of the exception.
103
+ attr_accessor :reviewer
104
+ # The state of the exception in the work flow process.
105
+ # @see Nexpose::VulnException::Status
106
+ attr_accessor :status
107
+ # The reason for the exception status.
108
+ # @see Nexpose::VulnException::Reason
109
+ attr_accessor :reason
110
+ # The scope of the exception.
111
+ # @see Nexpose::VulnException::Scope
112
+ attr_accessor :scope
113
+ # ID of device, if this exception applies to only one device.
114
+ attr_accessor :device_id
115
+ # Port on a device, if this exception applies to a specific port.
116
+ attr_accessor :port
117
+ # The specific vulnerable component in a discovered instance of the
118
+ # vulnerability referenced by the vuln_id, such as a program, file or user
119
+ # account.
120
+ attr_accessor :vuln_key
121
+ # The date an exception will expire, causing the vulnerability to be
122
+ # included in report risk scores.
123
+ attr_accessor :expiration
124
+ # Any comment provided by the submitter.
125
+ attr_accessor :submitter_comment
126
+ # Any comment provided by the reviewer.
127
+ attr_accessor :reviewer_comment
128
+
129
+ def initialize(vuln_id, scope, reason, status = nil)
130
+ @vuln_id, @scope, @reason, @status = vuln_id, scope, reason, status
131
+ end
132
+
133
+ # Submit this exception on the security console.
134
+ #
135
+ # @param [Connection] connection Connection to security console.
136
+ # @return [Fixnum] Newly assigned exception ID.
137
+ #
138
+ def save(connection, comment = nil)
139
+ validate
140
+
141
+ xml = connection.make_xml('VulnerabilityExceptionCreateRequest')
142
+ xml.add_attributes({ 'vuln-id' => @vuln_id,
143
+ 'scope' => @scope,
144
+ 'reason' => @reason })
145
+ case @scope
146
+ when Scope::ALL_INSTANCES_ON_A_SPECIFIC_ASSET
147
+ xml.add_attributes({ 'device-id' => @device_id })
148
+ when Scope::SPECIFIC_INSTANCE_OF_SPECIFIC_ASSET
149
+ xml.add_attributes({ 'device-id' => @device_id,
150
+ 'port-no' => @port,
151
+ 'vuln-key' => @vuln_key })
152
+ end
153
+
154
+ @submitter_comment = comment if comment
155
+ if @submitter_comment
156
+ comment = REXML::Element.new('comment')
157
+ comment.add_text(comment)
158
+ xml.add_element(comment)
159
+ end
160
+
161
+ response = connection.execute(xml, '1.2')
162
+ @id = response.attributes['exception-id'].to_i if response.success
163
+ end
164
+
165
+ # Resubmit a vulnerability exception request with a new comment and reason
166
+ # after an exception has been rejected.
167
+ #
168
+ # You can only resubmit a request that has a "Rejected" status; if an
169
+ # exception is "Approved" or "Under Review" you will receive an error
170
+ # message stating that the exception request cannot be resubmitted.
171
+ #
172
+ # This call will use the object's current state to resubmit.
173
+ #
174
+ # @param [Connection] connection Connection to security console.
175
+ # @return [Boolean] Whether or not the resubmission was valid.
176
+ #
177
+ def resubmit(connection)
178
+ raise ArgumentError.new('Only Rejected exceptions can be resubmitted.') unless @status == Status::REJECTED
179
+ connection.resubmit_vuln_exception(@id, @submitter_comment, @reason)
180
+ end
181
+
182
+ # Recall a vulnerability exception. Recall is used by a submitter to undo an
183
+ # exception request that has not been approved yet.
184
+ #
185
+ # You can only recall a vulnerability exception that has 'Under Review'
186
+ # status.
187
+ #
188
+ # @param [Connection] connection Connection to security console.
189
+ # @return [Boolean] Whether or not the recall was accepted by the console.
190
+ #
191
+ def recall(connection)
192
+ connection.recall_vuln_exception(id)
193
+ end
194
+
195
+ # Approve a vulnerability exception request, update comments and expiration
196
+ # dates on vulnerability exceptions that are "Under Review".
197
+ #
198
+ # @param [Connection] connection Connection to security console.
199
+ # @param [String] comment Comment to accompany the approval.
200
+ # @return [Boolean] Whether or not the approval was accepted by the console.
201
+ #
202
+ def approve(connection, comment = nil)
203
+ xml = connection.make_xml('VulnerabilityExceptionApproveRequest',
204
+ { 'exception-id' => @id })
205
+ if comment
206
+ cxml = REXML::Element.new('comment')
207
+ cxml.add_text(comment)
208
+ xml.add_element(cxml)
209
+ @reviewer_comment = comment
210
+ end
211
+
212
+ connection.execute(xml, '1.2').success
213
+ end
214
+
215
+ # Reject a vulnerability exception request and update comments for the
216
+ # vulnerability exception request.
217
+ #
218
+ # @param [Connection] connection Connection to security console.
219
+ # @param [String] comment Comment to accompany the rejection.
220
+ # @return [Boolean] Whether or not the reject was accepted by the console.
221
+ #
222
+ def reject(connection, comment = nil)
223
+ xml = connection.make_xml('VulnerabilityExceptionRejectRequest',
224
+ { 'exception-id' => @id })
225
+ if comment
226
+ cxml = REXML::Element.new('comment')
227
+ cxml.add_text(comment)
228
+ xml.add_element(cxml)
229
+ end
230
+
231
+ connection.execute(xml, '1.2').success
232
+ end
233
+
234
+ # Deletes this vulnerability exception.
235
+ #
236
+ # @param [Connection] connection Connection to security console.
237
+ # @return [Boolean] Whether or not deletion was successful.
238
+ #
239
+ def delete(connection)
240
+ connection.delete_vuln_exception(@id)
241
+ end
242
+
243
+ # Update security console with submitter comment on this vulnerability
244
+ # exceptions.
245
+ #
246
+ # Cannot update a submit comment unless exception is under review or has
247
+ # expired.
248
+ #
249
+ # @param [Connection] connection Connection to security console.
250
+ # @param [String] comment Submitter comment on this exception.
251
+ # @return [Boolean] Whether the comment was successfully submitted.
252
+ #
253
+ def update_submitter_comment(connection, comment)
254
+ xml = connection.make_xml('VulnerabilityExceptionUpdateCommentRequest',
255
+ { 'exception-id' => @id })
256
+ cxml = REXML::Element.new('submitter-comment')
257
+ cxml.add_text(comment)
258
+ xml.add_element(cxml)
259
+ @submitter_comment = comment
260
+
261
+ connection.execute(xml, '1.2').success
262
+ end
263
+
264
+ # Update security console with reviewer comment on this vulnerability
265
+ # exceptions.
266
+ #
267
+ # @param [Connection] connection Connection to security console.
268
+ # @param [String] comment Reviewer comment on this exception.
269
+ # @return [Boolean] Whether the comment was successfully submitted.
270
+ #
271
+ def update_reviewer_comment(connection, comment)
272
+ xml = connection.make_xml('VulnerabilityExceptionUpdateCommentRequest',
273
+ { 'exception-id' => @id })
274
+ cxml = REXML::Element.new('reviewer-comment')
275
+ cxml.add_text(comment)
276
+ xml.add_element(cxml)
277
+ @reviewer_comment = comment
278
+
279
+ connection.execute(xml, '1.2').success
280
+ end
281
+
282
+ # Update the expiration date for this exception.
283
+ # The expiration time cannot be in the past.
284
+ #
285
+ # @param [Connection] connection Connection to security console.
286
+ # @param [String] new_date Date in the format "YYYY-MM-DD".
287
+ # @return [Boolean] Whether the update was successfully submitted.
288
+ #
289
+ def update_expiration_date(connection, new_date)
290
+ xml = connection.make_xml('VulnerabilityExceptionUpdateExpirationDateRequest',
291
+ { 'exception-id' => @id,
292
+ 'expiration-date' => new_date })
293
+ connection.execute(xml, '1.2').success
294
+ end
295
+
296
+ # Validate that this exception meets to requires for the assigned scope.
297
+ #
298
+ def validate
299
+ raise ArgumentError.new('No vuln_id.') unless @vuln_id
300
+ raise ArgumentError.new('No scope.') unless @scope
301
+ raise ArgumentError.new('No reason.') unless @reason
302
+
303
+ case @scope
304
+ when Scope::ALL_INSTANCES
305
+ @device_id = @port = @vuln_key = nil
306
+ when Scope::ALL_INSTANCES_ON_A_SPECIFIC_ASSET
307
+ raise ArgumentError.new('No device_id.') unless @device_id
308
+ @port = @vuln_key = nil
309
+ when Scope::SPECIFIC_INSTANCE_OF_SPECIFIC_ASSET
310
+ raise ArgumentError.new('No device_id.') unless @device_id
311
+ raise ArgumentError.new('Port or vuln_key is required.') unless @port || @vuln_key
312
+ else
313
+ raise ArgumentError.new("Invalid scope: #{@scope}")
314
+ end
315
+ end
316
+
317
+ def self.parse(xml)
318
+ exception = new(xml.attributes['vuln-id'],
319
+ xml.attributes['scope'],
320
+ xml.attributes['reason'],
321
+ xml.attributes['status'])
322
+
323
+ exception.id = xml.attributes['exception-id']
324
+ exception.submitter = xml.attributes['submitter']
325
+ exception.reviewer = xml.attributes['reviewer']
326
+ exception.device_id = xml.attributes['device-id']
327
+ exception.port = xml.attributes['port-no']
328
+ exception.vuln_key = xml.attributes['vuln-key']
329
+ # TODO: Convert to Date/Time object?
330
+ exception.expiration = xml.attributes['expiration-date']
331
+
332
+ submitter_comment = xml.elements['submitter-comment']
333
+ exception.submitter_comment = submitter_comment.text if submitter_comment
334
+ reviewer_comment = xml.elements['reviewer-comment']
335
+ exception.reviewer_comment = reviewer_comment.text if reviewer_comment
336
+
337
+ exception
338
+ end
339
+
340
+ # The state of a vulnerability exception in the work flow process.
341
+ #
342
+ module Status
343
+ UNDER_REVIEW = 'Under Review'
344
+ APPROVED = 'Approved'
345
+ REJECTED = 'Rejected'
346
+ DELETED = 'Deleted'
347
+ end
348
+
349
+ # The reason for the exception status.
350
+ #
351
+ module Reason
352
+ FALSE_POSITIVE = 'False Positive'
353
+ COMPENSATING_CONTROL = 'Compensating Control'
354
+ ACCEPTABLE_USE = 'Acceptable Use'
355
+ ACCEPTABLE_RISK = 'Acceptable Risk'
356
+ OTHER = 'Other'
357
+ end
358
+
359
+ # The scope of the exception.
360
+ #
361
+ module Scope
362
+ ALL_INSTANCES = 'All Instances'
363
+ ALL_INSTANCES_ON_A_SPECIFIC_ASSET = 'All Instances on a Specific Asset'
364
+ ALL_INSTANCES_IN_A_SPECIFIC_SITE = 'All Instances in a Specific Site'
365
+ SPECIFIC_INSTANCE_OF_SPECIFIC_ASSET = 'Specific Instance of Specific Asset'
366
+ end
367
+ end
368
+ end
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.2.8
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - HD Moore
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-07-30 00:00:00.000000000 Z
13
+ date: 2013-09-12 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: librex
@@ -54,25 +54,33 @@ files:
54
54
  - README.markdown
55
55
  - Rakefile
56
56
  - lib/nexpose.rb
57
+ - lib/nexpose/pool.rb
57
58
  - lib/nexpose/group.rb
59
+ - lib/nexpose/device.rb
60
+ - lib/nexpose/report_template.rb
58
61
  - lib/nexpose/ticket.rb
59
62
  - lib/nexpose/common.rb
60
- - lib/nexpose/scan_engine.rb
63
+ - lib/nexpose/data_table.rb
64
+ - lib/nexpose/dag.rb
61
65
  - lib/nexpose/creds.rb
62
66
  - lib/nexpose/api_request.rb
63
67
  - lib/nexpose/role.rb
68
+ - lib/nexpose/engine.rb
64
69
  - lib/nexpose/manage.rb
65
70
  - lib/nexpose/scan.rb
71
+ - lib/nexpose/scan_template.rb
66
72
  - lib/nexpose/report.rb
67
73
  - lib/nexpose/vuln.rb
74
+ - lib/nexpose/ajax.rb
68
75
  - lib/nexpose/util.rb
69
76
  - lib/nexpose/site.rb
77
+ - lib/nexpose/filter.rb
70
78
  - lib/nexpose/alert.rb
71
79
  - lib/nexpose/silo.rb
80
+ - lib/nexpose/vuln_exception.rb
72
81
  - lib/nexpose/user.rb
73
82
  - lib/nexpose/tags
74
83
  - lib/nexpose/connection.rb
75
- - lib/nexpose/misc.rb
76
84
  - lib/nexpose/error.rb
77
85
  - lib/README.md
78
86
  homepage: https://github.com/rapid7/nexpose-client
data/lib/nexpose/misc.rb DELETED
@@ -1,35 +0,0 @@
1
- module Nexpose
2
- module NexposeAPI
3
- include XMLUtils
4
-
5
- def device_delete(param)
6
- r = execute(make_xml('DeviceDeleteRequest', {'device-id' => param}))
7
- r.success
8
- end
9
-
10
- # Lists all the users for the NSC along with the user details.
11
- #
12
- def list_users
13
- r = execute(make_xml('UserListingRequest'))
14
- if r.success
15
- res = []
16
- r.res.elements.each('//UserSummary') do |user_summary|
17
- res << {
18
- :auth_source => user_summary.attributes['authSource'],
19
- :auth_module => user_summary.attributes['authModule'],
20
- :user_name => user_summary.attributes['userName'],
21
- :full_name => user_summary.attributes['fullName'],
22
- :email => user_summary.attributes['email'],
23
- :is_admin => user_summary.attributes['isAdmin'].to_s.chomp.eql?('1'),
24
- :is_disabled => user_summary.attributes['disabled'].to_s.chomp.eql?('1'),
25
- :site_count => user_summary.attributes['siteCount'],
26
- :group_count => user_summary.attributes['groupCount']
27
- }
28
- end
29
- res
30
- else
31
- false
32
- end
33
- end
34
- end
35
- end