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
@@ -7,20 +7,20 @@ module Nexpose
|
|
7
7
|
CLUSTER = 'CLUSTER'
|
8
8
|
|
9
9
|
# Valid Operators: IS, IS_NOT
|
10
|
-
DATACENTER ='DATACENTER'
|
10
|
+
DATACENTER = 'DATACENTER'
|
11
11
|
|
12
12
|
# Valid Operators: CONTAINS, NOT_CONTAINS
|
13
|
-
GUEST_OS_FAMILY = 'GUEST_OS_FAMILY'
|
13
|
+
GUEST_OS_FAMILY = 'GUEST_OS_FAMILY' # Also AWS Filter
|
14
14
|
|
15
15
|
# Valid Operators: IN, NOT_IN
|
16
|
-
IP_ADDRESS_RANGE = 'IP_ADDRESS'
|
16
|
+
IP_ADDRESS_RANGE = 'IP_ADDRESS' # Also AWS Filter
|
17
17
|
|
18
18
|
# Valid Operators: IN, NOT_IN
|
19
19
|
# Valid Values (See Value::PowerState): ON, OFF, SUSPENDED
|
20
20
|
POWER_STATE = 'POWER_STATE'
|
21
21
|
|
22
22
|
# Valid Operators: CONTAINS, NOT_CONTAINS
|
23
|
-
RESOURCE_POOL_PATH ='RESOURCE_POOL_PATH'
|
23
|
+
RESOURCE_POOL_PATH = 'RESOURCE_POOL_PATH'
|
24
24
|
|
25
25
|
# Valid Operators: IS, IS_NOT, CONTAINS, NOT_CONTAINS, STARTS_WITH
|
26
26
|
VIRTUAL_MACHINE_NAME = 'VM'
|
@@ -45,7 +45,7 @@ module Nexpose
|
|
45
45
|
INSTANCE_TYPE = 'INSTANCE_TYPE'
|
46
46
|
|
47
47
|
# Valid Operators: IN, NOT_IN
|
48
|
-
REGION ='REGION'
|
48
|
+
REGION = 'REGION'
|
49
49
|
|
50
50
|
###### Mobile or Active sync Filters ######
|
51
51
|
# Valid Operators: CONTAINS, NOT_CONTAINS
|
@@ -54,13 +54,12 @@ module Nexpose
|
|
54
54
|
# Valid Operators: IS, IS_NOT, CONTAINS, NOT_CONTAINS, STARTS_WITH
|
55
55
|
USER = 'DEVICE_USER_DISPLAY_NAME'
|
56
56
|
|
57
|
-
|
58
57
|
end
|
59
58
|
|
60
59
|
module Value
|
61
60
|
module PowerState
|
62
|
-
ON
|
63
|
-
OFF
|
61
|
+
ON = 'poweredOn'
|
62
|
+
OFF = 'poweredOff'
|
64
63
|
SUSPENDED = 'suspended'
|
65
64
|
end
|
66
65
|
end
|
data/lib/nexpose/engine.rb
CHANGED
@@ -10,8 +10,7 @@ module Nexpose
|
|
10
10
|
# @return [Boolean] true if engine successfully deleted.
|
11
11
|
#
|
12
12
|
def delete_engine(engine_id, scope = 'silo')
|
13
|
-
xml = make_xml('EngineDeleteRequest',
|
14
|
-
{'engine-id' => engine_id, 'scope' => scope})
|
13
|
+
xml = make_xml('EngineDeleteRequest', { 'engine-id' => engine_id, 'scope' => scope })
|
15
14
|
response = execute(xml, '1.2')
|
16
15
|
response.success
|
17
16
|
end
|
@@ -27,9 +26,9 @@ module Nexpose
|
|
27
26
|
# @return [Boolean] true if the connection is successfully reversed.
|
28
27
|
#
|
29
28
|
def reverse_engine_connection(engine_id)
|
30
|
-
uri
|
29
|
+
uri = "/api/2.1/engine/#{engine_id}/reverseConnection"
|
31
30
|
response = AJAX.put(self, uri)
|
32
|
-
response.eql?(
|
31
|
+
response.eql?('true')
|
33
32
|
end
|
34
33
|
|
35
34
|
# Kicks off an update on a single engine.
|
@@ -54,8 +53,8 @@ module Nexpose
|
|
54
53
|
# each active scan on the engine.
|
55
54
|
#
|
56
55
|
def engine_activity(engine_id)
|
57
|
-
xml = make_xml('EngineActivityRequest', {'engine-id' => engine_id})
|
58
|
-
r
|
56
|
+
xml = make_xml('EngineActivityRequest', { 'engine-id' => engine_id })
|
57
|
+
r = execute(xml)
|
59
58
|
arr = []
|
60
59
|
if r.success
|
61
60
|
r.res.elements.each('//ScanSummary') do |scan_event|
|
@@ -72,7 +71,7 @@ module Nexpose
|
|
72
71
|
#
|
73
72
|
def list_engines
|
74
73
|
response = execute(make_xml('EngineListingRequest'))
|
75
|
-
arr
|
74
|
+
arr = []
|
76
75
|
if response.success
|
77
76
|
response.res.elements.each('//EngineSummary') do |engine|
|
78
77
|
arr << EngineSummary.new(engine.attributes['id'].to_i,
|
@@ -86,7 +85,7 @@ module Nexpose
|
|
86
85
|
arr
|
87
86
|
end
|
88
87
|
|
89
|
-
|
88
|
+
alias engines list_engines
|
90
89
|
end
|
91
90
|
|
92
91
|
# Object representing the current details of a scan engine attached to the
|
@@ -110,12 +109,12 @@ module Nexpose
|
|
110
109
|
attr_reader :scope
|
111
110
|
|
112
111
|
def initialize(id, name, address, port, status, scope = 'silo')
|
113
|
-
@id
|
114
|
-
@name
|
112
|
+
@id = id
|
113
|
+
@name = name
|
115
114
|
@address = address
|
116
|
-
@port
|
117
|
-
@status
|
118
|
-
@scope
|
115
|
+
@port = port
|
116
|
+
@status = status
|
117
|
+
@scope = scope
|
119
118
|
end
|
120
119
|
end
|
121
120
|
|
@@ -143,13 +142,13 @@ module Nexpose
|
|
143
142
|
attr_accessor :sites
|
144
143
|
|
145
144
|
def initialize(address, name = nil, port = 40814)
|
146
|
-
@id
|
145
|
+
@id = -1
|
147
146
|
@address = address
|
148
|
-
@name
|
149
|
-
@name
|
150
|
-
@port
|
151
|
-
@scope
|
152
|
-
@sites
|
147
|
+
@name = name
|
148
|
+
@name ||= address
|
149
|
+
@port = port
|
150
|
+
@scope = 'silo'
|
151
|
+
@sites = []
|
153
152
|
end
|
154
153
|
|
155
154
|
def self.load(connection, id)
|
@@ -163,12 +162,14 @@ module Nexpose
|
|
163
162
|
engine = Engine.new(config.attributes['address'],
|
164
163
|
config.attributes['name'],
|
165
164
|
config.attributes['port'])
|
166
|
-
engine.id
|
167
|
-
engine.scope
|
165
|
+
engine.id = config.attributes['id']
|
166
|
+
engine.scope = config.attributes['scope'] if config.attributes['scope']
|
168
167
|
engine.priority = config.attributes['priority'] if config.attributes['priority']
|
168
|
+
|
169
169
|
config.elements.each('Site') do |site|
|
170
170
|
engine.sites << SiteSummary.new(site.attributes['id'], site.attributes['name'])
|
171
171
|
end
|
172
|
+
|
172
173
|
return engine
|
173
174
|
end
|
174
175
|
end
|
data/lib/nexpose/error.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
module Nexpose
|
2
|
+
|
2
3
|
class APIError < ::RuntimeError
|
3
4
|
attr_accessor :req, :reason
|
4
5
|
attr_reader :code
|
5
6
|
|
6
7
|
def initialize(req, reason = '', code = 400)
|
7
|
-
@req
|
8
|
+
@req = req
|
8
9
|
@reason = reason
|
9
|
-
@code
|
10
|
+
@code = code
|
10
11
|
end
|
11
12
|
|
12
13
|
def to_s
|
@@ -16,15 +17,16 @@ module Nexpose
|
|
16
17
|
|
17
18
|
class AuthenticationFailed < APIError
|
18
19
|
def initialize(req)
|
19
|
-
@req
|
20
|
-
@reason =
|
20
|
+
@req = req
|
21
|
+
@reason = 'Login Failed'
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
24
25
|
class PermissionError < APIError
|
25
26
|
def initialize(req)
|
26
|
-
@req
|
27
|
+
@req = req
|
27
28
|
@reason = 'User does not have permission to perform this action.'
|
28
29
|
end
|
29
30
|
end
|
31
|
+
|
30
32
|
end
|
data/lib/nexpose/external.rb
CHANGED
@@ -27,10 +27,10 @@ module Nexpose
|
|
27
27
|
# @return [Array[ImportResult]] collection of import results.
|
28
28
|
#
|
29
29
|
def import_assets_from_json(site_id, json)
|
30
|
-
uri
|
30
|
+
uri = "/api/2.1/sites/#{site_id}/assets"
|
31
31
|
# Wait up to 5 minutes for a response.
|
32
32
|
resp = AJAX.post(self, uri, json, AJAX::CONTENT_TYPE::JSON, 300)
|
33
|
-
arr
|
33
|
+
arr = JSON.parse(resp, symbolize_names: true)
|
34
34
|
arr.map { |e| External::ImportResult.new.object_from_hash(self, e) }
|
35
35
|
end
|
36
36
|
end
|
@@ -87,15 +87,15 @@ module Nexpose
|
|
87
87
|
attr_accessor :vulnerabilities
|
88
88
|
|
89
89
|
def initialize
|
90
|
-
@aliases
|
91
|
-
@software
|
92
|
-
@services
|
93
|
-
@attributes
|
94
|
-
@users
|
95
|
-
@groups
|
96
|
-
@files
|
90
|
+
@aliases = []
|
91
|
+
@software = []
|
92
|
+
@services = []
|
93
|
+
@attributes = []
|
94
|
+
@users = []
|
95
|
+
@groups = []
|
96
|
+
@files = []
|
97
97
|
@unique_identifiers = []
|
98
|
-
@vulnerabilities
|
98
|
+
@vulnerabilities = []
|
99
99
|
end
|
100
100
|
|
101
101
|
def to_json
|
@@ -123,10 +123,10 @@ module Nexpose
|
|
123
123
|
|
124
124
|
# Valid host types for an asset.
|
125
125
|
module HostType
|
126
|
-
GUEST
|
126
|
+
GUEST = 'GUEST'
|
127
127
|
HYPERVISOR = 'HYPERVISOR'
|
128
|
-
PHYSICAL
|
129
|
-
MOBILE
|
128
|
+
PHYSICAL = 'PHYSICAL'
|
129
|
+
MOBILE = 'MOBILE'
|
130
130
|
end
|
131
131
|
end
|
132
132
|
|
@@ -143,7 +143,9 @@ module Nexpose
|
|
143
143
|
attr_accessor :vulnerabilities
|
144
144
|
|
145
145
|
def initialize(port, protocol = Protocol::RAW, name = nil)
|
146
|
-
@port
|
146
|
+
@port = port
|
147
|
+
@protocol = protocol
|
148
|
+
@name = name
|
147
149
|
@vulnerabilities = []
|
148
150
|
end
|
149
151
|
|
@@ -169,7 +171,10 @@ module Nexpose
|
|
169
171
|
attr_accessor :proof
|
170
172
|
|
171
173
|
def initialize(vuln_id, status = Status::EXPLOITED, proof = nil, key = nil)
|
172
|
-
@vuln_id
|
174
|
+
@vuln_id = vuln_id
|
175
|
+
@status = status
|
176
|
+
@proof = proof
|
177
|
+
@key = key
|
173
178
|
end
|
174
179
|
|
175
180
|
def to_h
|
@@ -185,7 +190,7 @@ module Nexpose
|
|
185
190
|
EXPLOITED = 'vulnerable-exploited'
|
186
191
|
# Vulnerable because the service or software version is associated with
|
187
192
|
# a known vulnerability.
|
188
|
-
VERSION
|
193
|
+
VERSION = 'vulnerable-version'
|
189
194
|
# A potential vulnerability.
|
190
195
|
POTENTIAL = 'potential'
|
191
196
|
end
|
data/lib/nexpose/filter.rb
CHANGED
@@ -14,7 +14,7 @@ module Nexpose
|
|
14
14
|
#
|
15
15
|
def filter(field, operator, value = '')
|
16
16
|
criterion = Criterion.new(field, operator, value)
|
17
|
-
criteria
|
17
|
+
criteria = Criteria.new(criterion)
|
18
18
|
search(criteria)
|
19
19
|
end
|
20
20
|
|
@@ -34,9 +34,7 @@ module Nexpose
|
|
34
34
|
# @return [Array[FilteredAsset]] List of matching assets.
|
35
35
|
#
|
36
36
|
def search(criteria)
|
37
|
-
results = DataTable._get_json_table(self,
|
38
|
-
'/data/asset/filterAssets',
|
39
|
-
criteria._to_payload)
|
37
|
+
results = DataTable._get_json_table(self, '/data/asset/filterAssets', criteria._to_payload)
|
40
38
|
results.map { |a| FilteredAsset.new(a) }
|
41
39
|
end
|
42
40
|
end
|
@@ -173,32 +171,32 @@ module Nexpose
|
|
173
171
|
# List of acceptable operators. Not all fields accept all operators.
|
174
172
|
#
|
175
173
|
module Operator
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
174
|
+
ARE = 'ARE'
|
175
|
+
BETWEEN = 'BETWEEN'
|
176
|
+
CONTAINS = 'CONTAINS'
|
177
|
+
DO_NOT_INCLUDE = 'DO_NOT_INCLUDE'
|
178
|
+
EARLIER_THAN = 'EARLIER_THAN'
|
179
|
+
ENDS_WITH = 'ENDS_WITH'
|
180
|
+
GREATER_THAN = 'GREATER_THAN'
|
181
|
+
IN = 'IN'
|
182
|
+
INCLUDE = 'INCLUDE'
|
183
|
+
IN_RANGE = 'IN_RANGE'
|
184
|
+
IS = 'IS'
|
185
|
+
IS_APPLIED = 'IS_APPLIED'
|
186
|
+
IS_EMPTY = 'IS_EMPTY'
|
187
|
+
IS_NOT = 'IS_NOT'
|
188
|
+
IS_NOT_APPLIED = 'IS_NOT_APPLIED'
|
189
|
+
IS_NOT_EMPTY = 'IS_NOT_EMPTY'
|
190
|
+
LESS_THAN = 'LESS_THAN'
|
191
|
+
LIKE = 'LIKE'
|
192
|
+
NOT_CONTAINS = 'NOT_CONTAINS'
|
193
|
+
NOT_IN = 'NOT_IN'
|
194
|
+
NOT_IN_RANGE = 'NOT_IN_RANGE'
|
195
|
+
NOT_LIKE = 'NOT_LIKE'
|
196
|
+
ON_OR_AFTER = 'ON_OR_AFTER'
|
197
|
+
ON_OR_BEFORE = 'ON_OR_BEFORE'
|
198
|
+
STARTS_WITH = 'STARTS_WITH'
|
191
199
|
WITHIN_THE_LAST = 'WITHIN_THE_LAST'
|
192
|
-
GREATER_THAN = 'GREATER_THAN'
|
193
|
-
LESS_THAN = 'LESS_THAN'
|
194
|
-
IS_EMPTY = 'IS_EMPTY'
|
195
|
-
IS_NOT_EMPTY = 'IS_NOT_EMPTY'
|
196
|
-
INCLUDE = 'INCLUDE'
|
197
|
-
DO_NOT_INCLUDE = 'DO_NOT_INCLUDE'
|
198
|
-
IS_APPLIED = 'IS_APPLIED'
|
199
|
-
IS_NOT_APPLIED = 'IS_NOT_APPLIED'
|
200
|
-
LIKE = 'LIKE'
|
201
|
-
NOT_LIKE = 'NOT_LIKE'
|
202
200
|
end
|
203
201
|
|
204
202
|
# Specialized values used by certain search fields
|
@@ -206,36 +204,36 @@ module Nexpose
|
|
206
204
|
module Value
|
207
205
|
# Constants for filtering on access complexity.
|
208
206
|
module AccessComplexity
|
209
|
-
LOW
|
207
|
+
LOW = 'L'
|
210
208
|
MEDIUM = 'M'
|
211
|
-
HIGH
|
209
|
+
HIGH = 'H'
|
212
210
|
end
|
213
211
|
|
214
212
|
# Constants for filtering on access vector.
|
215
213
|
module AccessVector
|
216
|
-
LOCAL
|
214
|
+
LOCAL = 'L'
|
217
215
|
ADJACENT = 'A'
|
218
|
-
NETWORK
|
216
|
+
NETWORK = 'N'
|
219
217
|
end
|
220
218
|
|
221
219
|
# Constants for filtering on whether authentication is required.
|
222
220
|
module AuthenticationRequired
|
223
|
-
NONE
|
224
|
-
SINGLE
|
221
|
+
NONE = 'N'
|
222
|
+
SINGLE = 'S'
|
225
223
|
MULTIPLE = 'M'
|
226
224
|
end
|
227
225
|
|
228
226
|
# Constants for filtering on CVSS impact.
|
229
227
|
module CVSSImpact
|
230
|
-
NONE
|
231
|
-
PARTIAL
|
228
|
+
NONE = 'N'
|
229
|
+
PARTIAL = 'P'
|
232
230
|
COMPLETE = 'C'
|
233
231
|
end
|
234
232
|
|
235
233
|
# Constants for filtering on host type.
|
236
234
|
module HostType
|
237
|
-
UNKNOWN
|
238
|
-
VIRTUAL
|
235
|
+
UNKNOWN = '0'
|
236
|
+
VIRTUAL = '1'
|
239
237
|
HYPERVISOR = '2'
|
240
238
|
BARE_METAL = '3'
|
241
239
|
end
|
@@ -263,15 +261,15 @@ module Nexpose
|
|
263
261
|
# Constants for filtering on vulnerability validations.
|
264
262
|
module ValidatedVulnerability
|
265
263
|
NOT_PRESENT = 1
|
266
|
-
PRESENT
|
264
|
+
PRESENT = 0
|
267
265
|
end
|
268
266
|
|
269
267
|
# Constants for filtering on vulnerability exposure.
|
270
268
|
module VulnerabilityExposure
|
271
|
-
MALWARE
|
269
|
+
MALWARE = 'type:"malware_type", name:"malwarekit"'
|
272
270
|
# TODO: A problem in Nexpose causes these values to not be constant.
|
273
271
|
METASPLOIT = 'type:"exploit_source_type", name:"2"'
|
274
|
-
DATABASE
|
272
|
+
DATABASE = 'type:"exploit_source_type", name:"1"'
|
275
273
|
end
|
276
274
|
end
|
277
275
|
end
|
@@ -288,7 +286,8 @@ module Nexpose
|
|
288
286
|
attr_accessor :value
|
289
287
|
|
290
288
|
def initialize(field, operator, value = '')
|
291
|
-
@field
|
289
|
+
@field = field.upcase
|
290
|
+
@operator = operator.upcase
|
292
291
|
if value.is_a? Array
|
293
292
|
@value = value.map(&:to_s)
|
294
293
|
else
|
@@ -384,17 +383,17 @@ module Nexpose
|
|
384
383
|
attr_reader :last_scan
|
385
384
|
|
386
385
|
def initialize(json)
|
387
|
-
@id
|
388
|
-
@ip
|
389
|
-
@name
|
390
|
-
@os
|
386
|
+
@id = json['assetID']
|
387
|
+
@ip = json['assetIP']
|
388
|
+
@name = json['assetName']
|
389
|
+
@os = json['assetOSName']
|
391
390
|
@exploit_count = json['exploitCount'].to_i
|
392
391
|
@malware_count = json['malwareCount'].to_i
|
393
|
-
@vuln_count
|
394
|
-
@risk_score
|
395
|
-
@site_ids
|
396
|
-
@site_id
|
397
|
-
@last_scan
|
392
|
+
@vuln_count = json['vulnCount'].to_i
|
393
|
+
@risk_score = json['riskScore'].to_f
|
394
|
+
@site_ids = json['sitePermissions'].map { |site| site['siteID'] }
|
395
|
+
@site_id = @site_ids.first
|
396
|
+
@last_scan = Time.at(json['lastScanDate'].to_i / 1000)
|
398
397
|
end
|
399
398
|
end
|
400
399
|
end
|
@@ -18,8 +18,7 @@ module Nexpose
|
|
18
18
|
|
19
19
|
def to_h
|
20
20
|
{
|
21
|
-
|
22
|
-
(@blackout || []).map { |blackout| blackout.to_h }
|
21
|
+
blackouts: (@blackout || []).map(&:to_h)
|
23
22
|
}
|
24
23
|
end
|
25
24
|
|
@@ -32,11 +31,11 @@ module Nexpose
|
|
32
31
|
end
|
33
32
|
|
34
33
|
def self.load(nsc)
|
35
|
-
uri
|
36
|
-
resp
|
37
|
-
hash
|
38
|
-
blackout
|
39
|
-
blackout.blackout = (hash[:blackouts] || []).map { |
|
34
|
+
uri = '/api/2.1/silo_blackout/'
|
35
|
+
resp = AJAX.get(nsc, uri, AJAX::CONTENT_TYPE::JSON)
|
36
|
+
hash = JSON.parse(resp, symbolize_names: true)
|
37
|
+
blackout = self.json_initializer(hash).deserialize(hash)
|
38
|
+
blackout.blackout = (hash[:blackouts] || []).map { |bout| Nexpose::Blackout.from_hash(bout) }
|
40
39
|
blackout
|
41
40
|
end
|
42
41
|
end
|