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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2 -3
- data/Gemfile.lock +1 -1
- data/lib/nexpose/ajax.rb +12 -16
- data/lib/nexpose/alert.rb +20 -21
- data/lib/nexpose/api.rb +3 -3
- data/lib/nexpose/asset.rb +23 -23
- data/lib/nexpose/blackout.rb +6 -14
- data/lib/nexpose/common.rb +87 -92
- data/lib/nexpose/connection.rb +8 -10
- data/lib/nexpose/console.rb +9 -9
- data/lib/nexpose/dag.rb +2 -2
- data/lib/nexpose/data_table.rb +8 -12
- data/lib/nexpose/device.rb +35 -34
- data/lib/nexpose/discovery.rb +69 -69
- data/lib/nexpose/discovery/filter.rb +7 -8
- data/lib/nexpose/engine.rb +22 -21
- data/lib/nexpose/error.rb +7 -5
- data/lib/nexpose/external.rb +21 -16
- data/lib/nexpose/filter.rb +51 -52
- data/lib/nexpose/global_blackout.rb +6 -7
- data/lib/nexpose/global_settings.rb +2 -3
- data/lib/nexpose/group.rb +25 -19
- data/lib/nexpose/json_serializer.rb +4 -14
- data/lib/nexpose/maint.rb +8 -9
- data/lib/nexpose/manage.rb +2 -2
- data/lib/nexpose/multi_tenant_user.rb +42 -42
- data/lib/nexpose/password_policy.rb +14 -14
- data/lib/nexpose/pool.rb +6 -5
- data/lib/nexpose/report.rb +30 -34
- data/lib/nexpose/report_template.rb +17 -18
- data/lib/nexpose/role.rb +64 -55
- data/lib/nexpose/scan.rb +77 -60
- data/lib/nexpose/scan_template.rb +17 -17
- data/lib/nexpose/scheduled_backup.rb +8 -8
- data/lib/nexpose/scheduled_maintenance.rb +9 -9
- data/lib/nexpose/shared_credential.rb +30 -33
- data/lib/nexpose/shared_secret.rb +5 -5
- data/lib/nexpose/silo.rb +68 -66
- data/lib/nexpose/silo_profile.rb +47 -50
- data/lib/nexpose/site.rb +101 -123
- data/lib/nexpose/site_credentials.rb +15 -17
- data/lib/nexpose/tag.rb +73 -80
- data/lib/nexpose/ticket.rb +45 -42
- data/lib/nexpose/user.rb +45 -45
- data/lib/nexpose/util.rb +1 -1
- data/lib/nexpose/version.rb +1 -1
- data/lib/nexpose/vuln.rb +45 -43
- data/lib/nexpose/vuln_def.rb +7 -7
- data/lib/nexpose/vuln_exception.rb +35 -36
- data/lib/nexpose/wait.rb +32 -28
- data/lib/nexpose/web_credentials.rb +34 -36
- metadata +2 -2
data/lib/nexpose/vuln_def.rb
CHANGED
@@ -8,7 +8,7 @@ module Nexpose
|
|
8
8
|
# @return [Array[VulnerabilityDefinition]] Collection of vulnerability definitions.
|
9
9
|
#
|
10
10
|
def all_vulns
|
11
|
-
uri
|
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
|
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
|
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
|
65
|
+
uri = '/api/2.0/vulnerability_definitions'
|
66
66
|
params = { title: title, all_words: all_words }
|
67
|
-
resp
|
68
|
-
json
|
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
|
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
|
-
|
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
|
81
|
+
options = { 'exception-id' => id }
|
84
82
|
options['reason'] = reason if reason
|
85
|
-
xml
|
86
|
-
comment_xml
|
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
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
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
|
-
|
130
|
-
|
131
|
-
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
173
|
-
alias
|
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
|
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
|
417
|
-
REJECTED
|
418
|
-
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
|
423
|
+
FALSE_POSITIVE = 'False Positive'
|
425
424
|
COMPENSATING_CONTROL = 'Compensating Control'
|
426
|
-
ACCEPTABLE_USE
|
427
|
-
ACCEPTABLE_RISK
|
428
|
-
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
|
435
|
-
ALL_INSTANCES_ON_A_SPECIFIC_ASSET
|
436
|
-
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
|
data/lib/nexpose/wait.rb
CHANGED
@@ -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
|
7
|
-
@ready
|
8
|
-
@retry_count
|
9
|
-
@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
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
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
|
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
|
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
|
60
|
-
@baseURL
|
56
|
+
@headers = {}
|
57
|
+
@name = name
|
58
|
+
@baseURL = baseURL
|
61
59
|
@soft403Pattern = soft403Pattern
|
62
|
-
@service
|
63
|
-
@enabled
|
64
|
-
@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
|
-
|
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
|
120
|
-
@value
|
121
|
-
@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
|
158
|
-
@action
|
159
|
-
@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
|
206
|
-
@baseURL
|
207
|
-
@loginURL
|
203
|
+
@name = name
|
204
|
+
@baseURL = baseURL
|
205
|
+
@loginURL = loginURL
|
208
206
|
@soft403Pattern = soft403Pattern
|
209
|
-
@service
|
210
|
-
@enabled
|
211
|
-
@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
|