nexpose 7.0.0 → 7.0.1

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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2 -3
  3. data/Gemfile.lock +1 -1
  4. data/lib/nexpose/ajax.rb +12 -16
  5. data/lib/nexpose/alert.rb +20 -21
  6. data/lib/nexpose/api.rb +3 -3
  7. data/lib/nexpose/asset.rb +23 -23
  8. data/lib/nexpose/blackout.rb +6 -14
  9. data/lib/nexpose/common.rb +87 -92
  10. data/lib/nexpose/connection.rb +8 -10
  11. data/lib/nexpose/console.rb +9 -9
  12. data/lib/nexpose/dag.rb +2 -2
  13. data/lib/nexpose/data_table.rb +8 -12
  14. data/lib/nexpose/device.rb +35 -34
  15. data/lib/nexpose/discovery.rb +69 -69
  16. data/lib/nexpose/discovery/filter.rb +7 -8
  17. data/lib/nexpose/engine.rb +22 -21
  18. data/lib/nexpose/error.rb +7 -5
  19. data/lib/nexpose/external.rb +21 -16
  20. data/lib/nexpose/filter.rb +51 -52
  21. data/lib/nexpose/global_blackout.rb +6 -7
  22. data/lib/nexpose/global_settings.rb +2 -3
  23. data/lib/nexpose/group.rb +25 -19
  24. data/lib/nexpose/json_serializer.rb +4 -14
  25. data/lib/nexpose/maint.rb +8 -9
  26. data/lib/nexpose/manage.rb +2 -2
  27. data/lib/nexpose/multi_tenant_user.rb +42 -42
  28. data/lib/nexpose/password_policy.rb +14 -14
  29. data/lib/nexpose/pool.rb +6 -5
  30. data/lib/nexpose/report.rb +30 -34
  31. data/lib/nexpose/report_template.rb +17 -18
  32. data/lib/nexpose/role.rb +64 -55
  33. data/lib/nexpose/scan.rb +77 -60
  34. data/lib/nexpose/scan_template.rb +17 -17
  35. data/lib/nexpose/scheduled_backup.rb +8 -8
  36. data/lib/nexpose/scheduled_maintenance.rb +9 -9
  37. data/lib/nexpose/shared_credential.rb +30 -33
  38. data/lib/nexpose/shared_secret.rb +5 -5
  39. data/lib/nexpose/silo.rb +68 -66
  40. data/lib/nexpose/silo_profile.rb +47 -50
  41. data/lib/nexpose/site.rb +101 -123
  42. data/lib/nexpose/site_credentials.rb +15 -17
  43. data/lib/nexpose/tag.rb +73 -80
  44. data/lib/nexpose/ticket.rb +45 -42
  45. data/lib/nexpose/user.rb +45 -45
  46. data/lib/nexpose/util.rb +1 -1
  47. data/lib/nexpose/version.rb +1 -1
  48. data/lib/nexpose/vuln.rb +45 -43
  49. data/lib/nexpose/vuln_def.rb +7 -7
  50. data/lib/nexpose/vuln_exception.rb +35 -36
  51. data/lib/nexpose/wait.rb +32 -28
  52. data/lib/nexpose/web_credentials.rb +34 -36
  53. metadata +2 -2
@@ -8,7 +8,7 @@ module Nexpose
8
8
  # @return [Array[VulnerabilityDefinition]] Collection of vulnerability definitions.
9
9
  #
10
10
  def all_vulns
11
- uri = '/api/2.0/vulnerability_definitions'
11
+ uri = '/api/2.0/vulnerability_definitions'
12
12
  resp = AJAX.get(self, uri, AJAX::CONTENT_TYPE::JSON, per_page: 2_147_483_647)
13
13
  json = JSON.parse(resp, symbolize_names: true)
14
14
  json[:resources].map { |e| VulnerabilityDefinition.new.object_from_hash(self, e) }
@@ -20,7 +20,7 @@ module Nexpose
20
20
  # @return [Array[VulnerabilityDefinition]] A list of vuln definitions which check the CVE.
21
21
  #
22
22
  def find_vulns_by_cve(cve)
23
- uri = '/api/2.0/vulnerability_definitions'
23
+ uri = '/api/2.0/vulnerability_definitions'
24
24
  resp = AJAX.get(self, uri, AJAX::CONTENT_TYPE::JSON, cve: cve)
25
25
  json = JSON.parse(resp, symbolize_names: true)
26
26
  json[:resources].map { |e| VulnerabilityDefinition.new.object_from_hash(self, e) }
@@ -40,7 +40,7 @@ module Nexpose
40
40
  # check the vulnerability.
41
41
  #
42
42
  def find_vulns_by_ref(source, id)
43
- uri = '/api/2.0/vulnerability_definitions'
43
+ uri = '/api/2.0/vulnerability_definitions'
44
44
  resp = AJAX.get(self,
45
45
  uri,
46
46
  AJAX::CONTENT_TYPE::JSON,
@@ -62,10 +62,10 @@ module Nexpose
62
62
  # the provided value.
63
63
  #
64
64
  def find_vulns_by_title(title, all_words = true)
65
- uri = '/api/2.0/vulnerability_definitions'
65
+ uri = '/api/2.0/vulnerability_definitions'
66
66
  params = { title: title, all_words: all_words }
67
- resp = AJAX.get(self, uri, AJAX::CONTENT_TYPE::JSON, params)
68
- json = JSON.parse(resp, symbolize_names: true)
67
+ resp = AJAX.get(self, uri, AJAX::CONTENT_TYPE::JSON, params)
68
+ json = JSON.parse(resp, symbolize_names: true)
69
69
  json[:resources].map { |e| VulnerabilityDefinition.new.object_from_hash(self, e) }
70
70
  end
71
71
  end
@@ -121,7 +121,7 @@ module Nexpose
121
121
  # @return [VulnerabilityDefinition] The requested vulnerability definition, if found.
122
122
  #
123
123
  def self.load(nsc, id)
124
- uri = "/api/2.0/vulnerability_definitions/#{id}"
124
+ uri = "/api/2.0/vulnerability_definitions/#{id}"
125
125
  resp = AJAX.get(nsc, uri, AJAX::CONTENT_TYPE::JSON)
126
126
  hash = JSON.parse(resp, symbolize_names: true)
127
127
  new.object_from_hash(nsc, hash)
@@ -60,11 +60,9 @@ module Nexpose
60
60
  results << ve
61
61
  end
62
62
  results.keep_if { |v| v.status == status } unless status.nil?
63
- return results
63
+ results
64
64
  end
65
-
66
- alias_method :vuln_exceptions, :list_vuln_exceptions
67
-
65
+ alias vuln_exceptions list_vuln_exceptions
68
66
 
69
67
  # Resubmit a vulnerability exception request with a new comment and reason
70
68
  # after an exception has been rejected.
@@ -80,10 +78,10 @@ module Nexpose
80
78
  # @return [Boolean] Whether or not the resubmission was valid.
81
79
  #
82
80
  def resubmit_vuln_exception(id, comment, reason = nil)
83
- options = { 'exception-id' => id }
81
+ options = { 'exception-id' => id }
84
82
  options['reason'] = reason if reason
85
- xml = make_xml('VulnerabilityExceptionResubmitRequest', options)
86
- comment_xml = make_xml('comment', {}, comment, false)
83
+ xml = make_xml('VulnerabilityExceptionResubmitRequest', options)
84
+ comment_xml = make_xml('comment', {}, comment, false)
87
85
  xml.add_element(comment_xml)
88
86
  r = execute(xml, '1.2')
89
87
  r.success
@@ -115,22 +113,21 @@ module Nexpose
115
113
  execute(xml, '1.2').success
116
114
  end
117
115
 
118
-
119
116
  private
120
117
 
121
- def is_valid_vuln_exception_status?(status)
122
- return true if status.nil?
123
- valid_status = []
124
- Nexpose::VulnException::Status.constants.each {|con| valid_status << Nexpose::VulnException::Status.const_get(con) }
125
- valid_status << Nexpose::VulnException::Status.constants.map(&:to_s).map(&:downcase)
126
- valid_status.flatten.map(&:downcase).include?(status.downcase)
127
- end
118
+ def is_valid_vuln_exception_status?(status)
119
+ return true if status.nil?
120
+ valid_status = []
121
+ Nexpose::VulnException::Status.constants.each { |con| valid_status << Nexpose::VulnException::Status.const_get(con) }
122
+ valid_status << Nexpose::VulnException::Status.constants.map(&:to_s).map(&:downcase)
123
+ valid_status.flatten.map(&:downcase).include?(status.downcase)
124
+ end
128
125
 
129
- def status_string_to_constant(status)
130
- Nexpose::VulnException::Status.constants.find do |name|
131
- Nexpose::VulnException::Status.const_get(name).to_s.downcase==status.downcase || status.to_sym.downcase == name.downcase
132
- end
126
+ def status_string_to_constant(status)
127
+ Nexpose::VulnException::Status.constants.find do |name|
128
+ Nexpose::VulnException::Status.const_get(name).to_s.downcase == status.downcase || status.to_sym.downcase == name.downcase
133
129
  end
130
+ end
134
131
 
135
132
  end
136
133
 
@@ -138,13 +135,13 @@ module Nexpose
138
135
  #
139
136
  # Certain attributes are necessary for some exception scopes, even though
140
137
  # they are optional otherwise.
141
- # An exception for all instances of a vulnerability on all assets only
138
+ # - An exception for all instances of a vulnerability on all assets only
142
139
  # requires the vuln_id attribute. The asset_id, vuln_key and port
143
140
  # attributes are ignored for this scope type.
144
- # An exception for all instances on a specific asset requires the vuln_id
141
+ # - An exception for all instances on a specific asset requires the vuln_id
145
142
  # and asset_id attributes. The vuln_key and port attributes are ignored for
146
143
  # this scope type.
147
- # An exception for a specific instance of a vulnerability on a specific
144
+ # - An exception for a specific instance of a vulnerability on a specific
148
145
  # asset requires the vuln_id, asset_id. Additionally, the port and/or the
149
146
  # key attribute must be specified.
150
147
  #
@@ -169,8 +166,8 @@ module Nexpose
169
166
  attr_accessor :scope
170
167
  # ID of asset, if this exception applies to only one asset.
171
168
  attr_accessor :asset_id
172
- alias :device_id :asset_id
173
- alias :device_id= :asset_id=
169
+ alias device_id asset_id
170
+ alias device_id= asset_id=
174
171
  # Id of the site, if this exception applies to all instances on a site
175
172
  attr_accessor :site_id
176
173
  # ID of the Asset Group, if this exception applies to all instances on an asset group
@@ -193,9 +190,11 @@ module Nexpose
193
190
  # Date when Submit occurred [Time]
194
191
  attr_accessor :submit_date
195
192
 
196
-
197
193
  def initialize(vuln_id, scope, reason, status = nil)
198
- @vuln_id, @scope, @reason, @status = vuln_id, scope, reason, status
194
+ @vuln_id = vuln_id
195
+ @scope = scope
196
+ @reason = reason
197
+ @status = status
199
198
  end
200
199
 
201
200
  # Submit this exception on the security console.
@@ -413,27 +412,27 @@ module Nexpose
413
412
  #
414
413
  module Status
415
414
  UNDER_REVIEW = 'Under Review'
416
- APPROVED = 'Approved'
417
- REJECTED = 'Rejected'
418
- DELETED = 'Deleted'
415
+ APPROVED = 'Approved'
416
+ REJECTED = 'Rejected'
417
+ DELETED = 'Deleted'
419
418
  end
420
419
 
421
420
  # The reason for the exception status.
422
421
  #
423
422
  module Reason
424
- FALSE_POSITIVE = 'False Positive'
423
+ FALSE_POSITIVE = 'False Positive'
425
424
  COMPENSATING_CONTROL = 'Compensating Control'
426
- ACCEPTABLE_USE = 'Acceptable Use'
427
- ACCEPTABLE_RISK = 'Acceptable Risk'
428
- OTHER = 'Other'
425
+ ACCEPTABLE_USE = 'Acceptable Use'
426
+ ACCEPTABLE_RISK = 'Acceptable Risk'
427
+ OTHER = 'Other'
429
428
  end
430
429
 
431
430
  # The scope of the exception.
432
431
  #
433
432
  module Scope
434
- ALL_INSTANCES = 'All Instances'
435
- ALL_INSTANCES_ON_A_SPECIFIC_ASSET = 'All Instances on a Specific Asset'
436
- ALL_INSTANCES_IN_A_SPECIFIC_SITE = 'All Instances in a Specific Site'
433
+ ALL_INSTANCES = 'All Instances'
434
+ ALL_INSTANCES_ON_A_SPECIFIC_ASSET = 'All Instances on a Specific Asset'
435
+ ALL_INSTANCES_IN_A_SPECIFIC_SITE = 'All Instances in a Specific Site'
437
436
  SPECIFIC_INSTANCE_OF_SPECIFIC_ASSET = 'Specific Instance of Specific Asset'
438
437
  end
439
438
  end
@@ -1,12 +1,13 @@
1
1
  module Nexpose
2
+
2
3
  class Wait
3
4
  attr_reader :error_message, :ready, :retry_count, :timeout, :polling_interval
4
5
 
5
6
  def initialize(retry_count: nil, timeout: nil, polling_interval: nil)
6
- @error_message = 'Default General Failure in Nexpose::Wait'
7
- @ready = false
8
- @retry_count = retry_count.to_i
9
- @timeout = timeout
7
+ @error_message = 'Default General Failure in Nexpose::Wait'
8
+ @ready = false
9
+ @retry_count = retry_count.to_i
10
+ @timeout = timeout
10
11
  @polling_interval = polling_interval
11
12
  end
12
13
 
@@ -18,49 +19,49 @@ module Nexpose
18
19
  poller = Nexpose::Poller.new(timeout: @timeout, polling_interval: @polling_interval)
19
20
  poller.wait(report_status_proc(nexpose_connection: nexpose_connection, report_id: report_id))
20
21
  @ready = true
21
- rescue TimeoutError
22
- @ready = false
23
- retry if timeout_retry?
24
- @error_message = "Timeout Waiting for Report to Generate - Report Config ID: #{report_id}"
25
- rescue NoMethodError => error
26
- @ready = false
27
- @error_message = "Error Report Config ID: #{report_id} :: Report Probably Does Not Exist :: #{error}"
28
- rescue => error
29
- @ready = false
30
- @error_message = "Error Report Config ID: #{report_id} :: #{error}"
22
+ rescue Timeout::Error
23
+ @ready = false
24
+ retry if timeout_retry?
25
+ @error_message = "Timeout Waiting for Report to Generate - Report Config ID: #{report_id}"
26
+ rescue NoMethodError => error
27
+ @ready = false
28
+ @error_message = "Error Report Config ID: #{report_id} :: Report Probably Does Not Exist :: #{error}"
29
+ rescue => error
30
+ @ready = false
31
+ @error_message = "Error Report Config ID: #{report_id} :: #{error}"
31
32
  end
32
33
 
33
34
  def for_integration(nexpose_connection:, scan_id:, status: 'finished')
34
35
  poller = Nexpose::Poller.new(timeout: @timeout, polling_interval: @polling_interval)
35
36
  poller.wait(integration_status_proc(nexpose_connection: nexpose_connection, scan_id: scan_id, status: status))
36
37
  @ready = true
37
- rescue TimeoutError
38
- @ready = false
39
- retry if timeout_retry?
40
- @error_message = "Timeout Waiting for Integration Status of '#{status}' - Scan ID: #{scan_id}"
41
- rescue Nexpose::APIError => error
42
- @ready = false
43
- @error_message = "API Error Waiting for Integration Scan ID: #{scan_id} :: #{error.req.error}"
38
+ rescue Timeout::Error
39
+ @ready = false
40
+ retry if timeout_retry?
41
+ @error_message = "Timeout Waiting for Integration Status of '#{status}' - Scan ID: #{scan_id}"
42
+ rescue Nexpose::APIError => error
43
+ @ready = false
44
+ @error_message = "API Error Waiting for Integration Scan ID: #{scan_id} :: #{error.req.error}"
44
45
  end
45
46
 
46
47
  def for_judgment(proc:, desc:)
47
48
  poller = Nexpose::Poller.new(timeout: @timeout, polling_interval: @polling_interval)
48
49
  poller.wait(proc)
49
50
  @ready = true
50
- rescue TimeoutError
51
- @ready = false
52
- retry if timeout_retry?
53
- @error_message = "Timeout Waiting for Judgment to Judge. #{desc}"
51
+ rescue Timeout::Error
52
+ @ready = false
53
+ retry if timeout_retry?
54
+ @error_message = "Timeout Waiting for Judgment to Judge. #{desc}"
54
55
  end
55
56
 
56
57
  private
57
58
 
58
59
  def report_status_proc(nexpose_connection:, report_id:)
59
- Proc.new { nexpose_connection.last_report(report_id).status == 'Generated' }
60
+ proc { nexpose_connection.last_report(report_id).status == 'Generated' }
60
61
  end
61
62
 
62
63
  def integration_status_proc(nexpose_connection:, scan_id:, status:)
63
- Proc.new { nexpose_connection.scan_status(scan_id).downcase == status.downcase }
64
+ proc { nexpose_connection.scan_status(scan_id).downcase == status.downcase }
64
65
  end
65
66
 
66
67
  def timeout_retry?
@@ -71,6 +72,7 @@ module Nexpose
71
72
  false
72
73
  end
73
74
  end
75
+
74
76
  end
75
77
 
76
78
  class Poller
@@ -89,7 +91,7 @@ module Nexpose
89
91
  @poll_begin = Time.now
90
92
  loop do
91
93
  break if condition.call
92
- raise TimeoutError if @poll_begin + @timeout < Time.now
94
+ raise Timeout::Error if @poll_begin + @timeout < Time.now
93
95
  sleep @polling_interval
94
96
  end
95
97
  end
@@ -105,5 +107,7 @@ module Nexpose
105
107
  default_polling = 1
106
108
  ENV['GLOBAL_POLLING_INTERVAL'].nil? ? default_polling : ENV['GLOBAL_POLLING_INTERVAL']
107
109
  end
110
+
108
111
  end
112
+
109
113
  end
@@ -4,14 +4,13 @@ module Nexpose
4
4
  module WebCredentials
5
5
 
6
6
  module WebAppAuthType
7
- HTML_FORM = 'htmlform' # Represent HTML form credentials.
7
+ HTML_FORM = 'htmlform' # Represent HTML form credentials.
8
8
  HTTP_HEADER = 'httpheaders' # Represent HTTP header credentials.
9
9
  end
10
10
 
11
11
  # Object that represents Header name-value pairs, associated with Web Session Authentication.
12
12
  #
13
13
  class Header
14
-
15
14
  # Name, one per Header
16
15
  attr_reader :name
17
16
  # Value, one per Header
@@ -19,7 +18,7 @@ module Nexpose
19
18
 
20
19
  # Construct with name value pair
21
20
  def initialize(name, value)
22
- @name = name
21
+ @name = name
23
22
  @value = value
24
23
  end
25
24
 
@@ -28,16 +27,16 @@ module Nexpose
28
27
  end
29
28
 
30
29
  def to_h
31
- header = Hash.new
30
+ header = {}
32
31
  header[@name] = @value
33
32
  header
34
33
  end
34
+
35
35
  end
36
36
 
37
37
  # Object that represents Headers, associated with Web Session Authentication.
38
38
  #
39
39
  class Headers < APIObject
40
-
41
40
  # A regular expression used to match against the response to identify authentication failures.
42
41
  attr_reader :soft403Pattern
43
42
  # Base URL of the application for which the form authentication applies.
@@ -48,20 +47,19 @@ module Nexpose
48
47
  attr_reader :name
49
48
  # is this enable for the site configuration
50
49
  attr_accessor :enabled
51
- #service type of header
50
+ # service type of header
52
51
  attr_reader :service
53
52
  # id of the header
54
53
  attr_reader :id
55
54
 
56
-
57
55
  def initialize(name, baseURL, soft403Pattern, id = -1, enabled = true)
58
- @headers = {}
59
- @name = name
60
- @baseURL = baseURL
56
+ @headers = {}
57
+ @name = name
58
+ @baseURL = baseURL
61
59
  @soft403Pattern = soft403Pattern
62
- @service = WebAppAuthType::HTTP_HEADER
63
- @enabled = enabled
64
- @id = id
60
+ @service = WebAppAuthType::HTTP_HEADER
61
+ @enabled = enabled
62
+ @id = id
65
63
  end
66
64
 
67
65
  def add_header(header)
@@ -79,8 +77,7 @@ module Nexpose
79
77
  name: name,
80
78
  headers: headers,
81
79
  baseURL: baseURL,
82
- soft403Pattern: soft403Pattern
83
- }
80
+ soft403Pattern: soft403Pattern }
84
81
  end
85
82
 
86
83
  def ==(other)
@@ -96,7 +93,8 @@ module Nexpose
96
93
  baseURL.eql?(other.baseURL) &&
97
94
  soft403Pattern.eql?(other.soft403Pattern)
98
95
  end
99
- end
96
+
97
+ end
100
98
 
101
99
  # When using HTML form, this represents the login form information.
102
100
  #
@@ -116,9 +114,9 @@ module Nexpose
116
114
  attr_reader :checked
117
115
 
118
116
  def initialize(name, value, type, dynamic, checked)
119
- @name = name
120
- @value = value
121
- @type = type
117
+ @name = name
118
+ @value = value
119
+ @type = type
122
120
  @dynamic = dynamic
123
121
  @checked = checked
124
122
  end
@@ -136,12 +134,12 @@ module Nexpose
136
134
  checked: checked
137
135
  }
138
136
  end
137
+
139
138
  end
140
139
 
141
140
  # When using HTML form, this represents the login form information.
142
141
  #
143
142
  class HTMLForm
144
-
145
143
  # The name of the form being submitted.
146
144
  attr_reader :name
147
145
  # The HTTP action (URL) through which to submit the login form.
@@ -154,11 +152,11 @@ module Nexpose
154
152
  attr_reader :fields
155
153
 
156
154
  def initialize(name, action, method, encType)
157
- @name = name
158
- @action = action
159
- @method = method
155
+ @name = name
156
+ @action = action
157
+ @method = method
160
158
  @encType = encType
161
- @fields = []
159
+ @fields = []
162
160
  end
163
161
 
164
162
  def add_field(field)
@@ -175,9 +173,9 @@ module Nexpose
175
173
  method: method,
176
174
  encType: encType,
177
175
  fields: fields,
178
- parentPage: action
179
- }
176
+ parentPage: action }
180
177
  end
178
+
181
179
  end
182
180
 
183
181
  # When using HTML form, this represents the login form information.
@@ -194,7 +192,7 @@ module Nexpose
194
192
  attr_reader :name
195
193
  # is this enable for the site configuration
196
194
  attr_accessor :enabled
197
- #service type of header
195
+ # service type of header
198
196
  attr_reader :service
199
197
  # id of the header
200
198
  attr_reader :id
@@ -202,13 +200,13 @@ module Nexpose
202
200
  attr_reader :form
203
201
 
204
202
  def initialize(name, baseURL, loginURL, soft403Pattern, id = -1, enabled = true)
205
- @name = name
206
- @baseURL = baseURL
207
- @loginURL = loginURL
203
+ @name = name
204
+ @baseURL = baseURL
205
+ @loginURL = loginURL
208
206
  @soft403Pattern = soft403Pattern
209
- @service = WebAppAuthType::HTML_FORM
210
- @enabled = enabled
211
- @id = id
207
+ @service = WebAppAuthType::HTML_FORM
208
+ @enabled = enabled
209
+ @id = id
212
210
  end
213
211
 
214
212
  def add_html_form(html_form)
@@ -227,8 +225,7 @@ module Nexpose
227
225
  form: form.to_h,
228
226
  baseURL: baseURL,
229
227
  loginURL: loginURL,
230
- soft403Pattern: soft403Pattern
231
- }
228
+ soft403Pattern: soft403Pattern }
232
229
  end
233
230
 
234
231
  def ==(other)
@@ -247,6 +244,7 @@ module Nexpose
247
244
  end
248
245
 
249
246
  end
247
+
250
248
  end
251
- end
252
249
 
250
+ end