nexpose 7.0.0 → 7.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -19,9 +19,8 @@ module Nexpose
|
|
19
19
|
# Private constructor. See #load method for retrieving a settings object.
|
20
20
|
#
|
21
21
|
def initialize(xml)
|
22
|
-
@xml
|
23
|
-
|
24
|
-
@asset_linking = parse_asset_linking_from_xml(xml)
|
22
|
+
@xml = xml
|
23
|
+
@asset_linking = parse_asset_linking_from_xml(xml)
|
25
24
|
@asset_exclusions = HostOrIP.parse(xml)
|
26
25
|
@control_scanning = parse_control_scanning_from_xml(xml)
|
27
26
|
end
|
data/lib/nexpose/group.rb
CHANGED
@@ -10,11 +10,11 @@ module Nexpose
|
|
10
10
|
# @return [Boolean] Whether group deletion succeeded.
|
11
11
|
#
|
12
12
|
def delete_asset_group(id)
|
13
|
-
r = execute(make_xml('AssetGroupDeleteRequest', {'group-id' => id}))
|
13
|
+
r = execute(make_xml('AssetGroupDeleteRequest', { 'group-id' => id }))
|
14
14
|
r.success
|
15
15
|
end
|
16
16
|
|
17
|
-
|
17
|
+
alias delete_group delete_asset_group
|
18
18
|
|
19
19
|
# Retrieve an array of all asset groups the user is authorized to view or
|
20
20
|
# manage.
|
@@ -23,7 +23,6 @@ module Nexpose
|
|
23
23
|
#
|
24
24
|
def list_asset_groups
|
25
25
|
r = execute(make_xml('AssetGroupListingRequest'))
|
26
|
-
|
27
26
|
groups = []
|
28
27
|
if r.success
|
29
28
|
r.res.elements.each('AssetGroupListingResponse/AssetGroupSummary') do |group|
|
@@ -37,8 +36,8 @@ module Nexpose
|
|
37
36
|
groups
|
38
37
|
end
|
39
38
|
|
40
|
-
|
41
|
-
|
39
|
+
alias groups list_asset_groups
|
40
|
+
alias asset_groups list_asset_groups
|
42
41
|
end
|
43
42
|
|
44
43
|
# Summary value object for asset group information.
|
@@ -47,7 +46,11 @@ module Nexpose
|
|
47
46
|
attr_reader :id, :name, :description, :risk_score, :dynamic
|
48
47
|
|
49
48
|
def initialize(id, name, desc, risk, dynamic)
|
50
|
-
@id
|
49
|
+
@id = id
|
50
|
+
@name = name
|
51
|
+
@description = desc
|
52
|
+
@risk_score = risk
|
53
|
+
@dynamic = dynamic
|
51
54
|
end
|
52
55
|
|
53
56
|
def dynamic?
|
@@ -68,17 +71,20 @@ module Nexpose
|
|
68
71
|
class AssetGroup < AssetGroupSummary
|
69
72
|
include Sanitize
|
70
73
|
|
71
|
-
attr_accessor :name, :description, :id
|
74
|
+
attr_accessor :name, :description, :id, :tags
|
72
75
|
|
73
76
|
# Array[Device] of devices associated with this asset group.
|
74
77
|
attr_accessor :assets
|
75
|
-
alias
|
76
|
-
alias
|
78
|
+
alias devices assets
|
79
|
+
alias devices= assets=
|
77
80
|
|
78
81
|
def initialize(name, desc, id = -1, risk = 0.0)
|
79
|
-
@name
|
80
|
-
@
|
81
|
-
@
|
82
|
+
@name = name
|
83
|
+
@description = desc
|
84
|
+
@id = id
|
85
|
+
@risk_score = risk
|
86
|
+
@assets = []
|
87
|
+
@tags = []
|
82
88
|
end
|
83
89
|
|
84
90
|
def save(connection)
|
@@ -86,7 +92,7 @@ module Nexpose
|
|
86
92
|
xml << to_xml
|
87
93
|
xml << '</AssetGroupSaveRequest>'
|
88
94
|
res = connection.execute(xml)
|
89
|
-
@id = res.attributes['group-id'].to_i if res.success
|
95
|
+
@id = res.attributes['group-id'].to_i if res.success && @id < 1
|
90
96
|
end
|
91
97
|
|
92
98
|
# Generate an XML representation of this group configuration
|
@@ -95,8 +101,8 @@ module Nexpose
|
|
95
101
|
#
|
96
102
|
def as_xml
|
97
103
|
xml = REXML::Element.new('AssetGroup')
|
98
|
-
xml.attributes['id']
|
99
|
-
xml.attributes['name']
|
104
|
+
xml.attributes['id'] = @id
|
105
|
+
xml.attributes['name'] = @name
|
100
106
|
xml.attributes['description'] = @description
|
101
107
|
|
102
108
|
if @description && !@description.empty?
|
@@ -106,7 +112,7 @@ module Nexpose
|
|
106
112
|
end
|
107
113
|
|
108
114
|
elem = REXML::Element.new('Devices')
|
109
|
-
@assets.each { |a| elem.add_element('device', {'id' => a.id}) }
|
115
|
+
@assets.each { |a| elem.add_element('device', { 'id' => a.id }) }
|
110
116
|
xml.add_element(elem)
|
111
117
|
|
112
118
|
unless tags.empty?
|
@@ -134,8 +140,8 @@ module Nexpose
|
|
134
140
|
# @return [Hash] Hash of site ID to Scan launch information for each scan.
|
135
141
|
#
|
136
142
|
def rescan_assets(connection)
|
137
|
-
|
138
|
-
|
143
|
+
scans = {}
|
144
|
+
sites_ids = @assets.map(&:site_id).uniq
|
139
145
|
sites_ids.each do |site_id|
|
140
146
|
to_scan = @assets.select { |d| d.site_id == site_id }
|
141
147
|
scans[site_id] = connection.scan_devices(to_scan)
|
@@ -153,7 +159,7 @@ module Nexpose
|
|
153
159
|
#
|
154
160
|
def self.load(connection, id)
|
155
161
|
xml = %(<AssetGroupConfigRequest session-id="#{connection.session_id}" group-id="#{id}"/>)
|
156
|
-
r
|
162
|
+
r = APIRequest.execute(connection.url, xml)
|
157
163
|
parse(r.res)
|
158
164
|
end
|
159
165
|
|
@@ -6,13 +6,11 @@ module Nexpose
|
|
6
6
|
data.each do |key, value|
|
7
7
|
if respond_to?(key)
|
8
8
|
property = value
|
9
|
-
|
10
9
|
if value.respond_to? :each
|
11
10
|
obj = resolve_type(key)
|
12
|
-
|
13
11
|
unless obj.nil?
|
14
12
|
if value.is_a?(Array)
|
15
|
-
property = value.map { |dv|
|
13
|
+
property = value.map { |dv| ((dv.respond_to? :each) ? create_object(obj, dv).deserialize(dv) : dv) }
|
16
14
|
else
|
17
15
|
property = create_object(obj, value).deserialize(value)
|
18
16
|
end
|
@@ -20,20 +18,15 @@ module Nexpose
|
|
20
18
|
elsif value.is_a?(String) && value.match(/^\d{8}T\d{6}\.\d{3}/)
|
21
19
|
property = ISO8601.to_time(value)
|
22
20
|
end
|
23
|
-
|
24
21
|
instance_variable_set("@#{key}", property)
|
25
22
|
end
|
26
23
|
end
|
27
|
-
|
28
24
|
self
|
29
25
|
end
|
30
26
|
|
31
|
-
def serialize
|
27
|
+
def serialize
|
32
28
|
hash = to_hash(Hash.new)
|
33
|
-
|
34
|
-
unless hash.nil?
|
35
|
-
JSON.generate(hash)
|
36
|
-
end
|
29
|
+
JSON.generate(hash) unless hash.nil?
|
37
30
|
end
|
38
31
|
|
39
32
|
def to_hash(hash)
|
@@ -41,7 +34,6 @@ module Nexpose
|
|
41
34
|
value = self.instance_variable_get(m)
|
42
35
|
hash[m.to_s.delete('@')] = do_hash(value)
|
43
36
|
end
|
44
|
-
|
45
37
|
hash
|
46
38
|
end
|
47
39
|
|
@@ -51,12 +43,10 @@ module Nexpose
|
|
51
43
|
if obj.is_a?(Array)
|
52
44
|
obj = obj.map do |el|
|
53
45
|
do_hash(el)
|
54
|
-
|
55
46
|
end
|
56
47
|
elsif obj.class.included_modules.include? JsonSerializer
|
57
48
|
obj = obj.to_hash(Hash.new)
|
58
49
|
end
|
59
|
-
|
60
50
|
obj
|
61
51
|
end
|
62
52
|
|
@@ -89,4 +79,4 @@ module Nexpose
|
|
89
79
|
class_name
|
90
80
|
end
|
91
81
|
end
|
92
|
-
end
|
82
|
+
end
|
data/lib/nexpose/maint.rb
CHANGED
@@ -46,11 +46,10 @@ module Nexpose
|
|
46
46
|
#
|
47
47
|
def db_maintenance(clean_up = false, compress = false, reindex = false)
|
48
48
|
return unless compress || clean_up || reindex
|
49
|
-
parameters = { 'cmd' => 'startMaintenance',
|
50
|
-
|
51
|
-
parameters['cleanup'] = 1 if clean_up
|
49
|
+
parameters = { 'cmd' => 'startMaintenance', 'targetTask' => 'dbMaintenance' }
|
50
|
+
parameters['cleanup'] = 1 if clean_up
|
52
51
|
parameters['compress'] = 1 if compress
|
53
|
-
parameters['reindex']
|
52
|
+
parameters['reindex'] = 1 if reindex
|
54
53
|
xml = AJAX.form_post(self, '/admin/global/maintenance/maintCmd.txml', parameters)
|
55
54
|
if !!(xml =~ /succeded="true"/)
|
56
55
|
_maintenance_restart
|
@@ -85,12 +84,12 @@ module Nexpose
|
|
85
84
|
attr_reader :size
|
86
85
|
|
87
86
|
def initialize(name, date, description, version, independent, size)
|
88
|
-
@name
|
89
|
-
@date
|
90
|
-
@description
|
91
|
-
@version
|
87
|
+
@name = name
|
88
|
+
@date = date
|
89
|
+
@description = description
|
90
|
+
@version = version
|
92
91
|
@platform_independent = independent
|
93
|
-
@size
|
92
|
+
@size = size
|
94
93
|
end
|
95
94
|
|
96
95
|
# Restore this backup to the Nexpose console.
|
data/lib/nexpose/manage.rb
CHANGED
@@ -47,9 +47,9 @@ module Nexpose
|
|
47
47
|
# Includes Product, Content, and Java versions.
|
48
48
|
#
|
49
49
|
def engine_versions
|
50
|
-
info
|
50
|
+
info = console_command('version engines')
|
51
51
|
versions = []
|
52
|
-
engines
|
52
|
+
engines = info.sub('VERSION INFORMATION\n', '').split(/\n\n/)
|
53
53
|
engines.each do |eng|
|
54
54
|
engdata = {}
|
55
55
|
eng.split(/\n/).each do |kv|
|
@@ -17,14 +17,14 @@ module Nexpose
|
|
17
17
|
end
|
18
18
|
arr
|
19
19
|
end
|
20
|
-
|
20
|
+
alias silo_users list_silo_users
|
21
21
|
|
22
22
|
# Delete the specified silo user
|
23
23
|
#
|
24
24
|
# @return Whether or not the delete request succeeded.
|
25
25
|
#
|
26
26
|
def delete_silo_user(user_id)
|
27
|
-
r = execute(make_xml('MultiTenantUserDeleteRequest', {'user-id' => user_id}), '1.2')
|
27
|
+
r = execute(make_xml('MultiTenantUserDeleteRequest', { 'user-id' => user_id }), '1.2')
|
28
28
|
r.success
|
29
29
|
end
|
30
30
|
end
|
@@ -42,21 +42,21 @@ module Nexpose
|
|
42
42
|
attr_reader :locked
|
43
43
|
|
44
44
|
def initialize(&block)
|
45
|
-
instance_eval
|
45
|
+
instance_eval(&block) if block_given?
|
46
46
|
end
|
47
47
|
|
48
48
|
def self.parse(xml)
|
49
49
|
new do
|
50
|
-
@id
|
51
|
-
@full_name
|
52
|
-
@user_name
|
53
|
-
@email
|
54
|
-
@superuser
|
55
|
-
@enabled
|
50
|
+
@id = xml.attributes['id'].to_i
|
51
|
+
@full_name = xml.attributes['full-name']
|
52
|
+
@user_name = xml.attributes['user-name']
|
53
|
+
@email = xml.attributes['email']
|
54
|
+
@superuser = xml.attributes['superuser'].to_s.chomp.eql?('true')
|
55
|
+
@enabled = xml.attributes['enabled'].to_s.chomp.eql?('true')
|
56
56
|
@auth_module = xml.attributes['auth-module']
|
57
57
|
@auth_source = xml.attributes['auth-source']
|
58
|
-
@silo_count
|
59
|
-
@locked
|
58
|
+
@silo_count = xml.attributes['silo-count'].to_i
|
59
|
+
@locked = xml.attributes['locked'].to_s.chomp.eql?('true')
|
60
60
|
end
|
61
61
|
end
|
62
62
|
end
|
@@ -73,13 +73,13 @@ module Nexpose
|
|
73
73
|
attr_accessor :silo_access
|
74
74
|
|
75
75
|
def initialize(&block)
|
76
|
-
instance_eval
|
76
|
+
instance_eval(&block) if block_given?
|
77
77
|
|
78
78
|
@silo_access = Array(@silo_access)
|
79
79
|
end
|
80
80
|
|
81
81
|
def save(connection)
|
82
|
-
if
|
82
|
+
if @id
|
83
83
|
update(connection)
|
84
84
|
else
|
85
85
|
create(connection)
|
@@ -112,14 +112,14 @@ module Nexpose
|
|
112
112
|
|
113
113
|
def as_xml
|
114
114
|
xml = REXML::Element.new('MultiTenantUserConfig')
|
115
|
-
xml.add_attributes({'id'
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
115
|
+
xml.add_attributes({ 'id' => @id,
|
116
|
+
'full-name' => @full_name,
|
117
|
+
'user-name' => @user_name,
|
118
|
+
'authsrcid' => @auth_source_id,
|
119
|
+
'email' => @email,
|
120
|
+
'password' => @password,
|
121
|
+
'superuser' => @superuser,
|
122
|
+
'enabled' => @enabled })
|
123
123
|
siloaccesses = xml.add_element('SiloAccesses')
|
124
124
|
@silo_access.each { |silo_access| siloaccesses.add_element(silo_access.as_xml) }
|
125
125
|
xml
|
@@ -135,20 +135,20 @@ module Nexpose
|
|
135
135
|
|
136
136
|
def self.parse(xml)
|
137
137
|
new do |user|
|
138
|
-
user.id
|
139
|
-
user.full_name
|
140
|
-
user.user_name
|
141
|
-
user.email
|
142
|
-
user.superuser
|
143
|
-
user.enabled
|
138
|
+
user.id = xml.attributes['id'].to_i
|
139
|
+
user.full_name = xml.attributes['full-name']
|
140
|
+
user.user_name = xml.attributes['user-name']
|
141
|
+
user.email = xml.attributes['email']
|
142
|
+
user.superuser = xml.attributes['superuser'].to_s.chomp.eql?('true')
|
143
|
+
user.enabled = xml.attributes['enabled'].to_s.chomp.eql?('true')
|
144
144
|
user.auth_source_id = xml.attributes['authsrcid'].to_i
|
145
|
-
user.silo_access
|
145
|
+
user.silo_access = []
|
146
146
|
xml.elements.each('SiloAccesses/SiloAccess') { |access| user.silo_access << SiloAccess.parse(access) }
|
147
147
|
end
|
148
148
|
end
|
149
149
|
|
150
150
|
def self.load(connection, user_id)
|
151
|
-
r = connection.execute(connection.make_xml('MultiTenantUserConfigRequest', {'user-id' => user_id}), '1.2')
|
151
|
+
r = connection.execute(connection.make_xml('MultiTenantUserConfigRequest', { 'user-id' => user_id }), '1.2')
|
152
152
|
|
153
153
|
if r.success
|
154
154
|
r.res.elements.each('MultiTenantUserConfigResponse/MultiTenantUserConfig') do |config|
|
@@ -169,30 +169,30 @@ module Nexpose
|
|
169
169
|
attr_accessor :groups
|
170
170
|
|
171
171
|
def initialize(&block)
|
172
|
-
instance_eval
|
172
|
+
instance_eval(&block) if block_given?
|
173
173
|
@sites = Array(@sites)
|
174
174
|
@groups = Array(@groups)
|
175
175
|
end
|
176
176
|
|
177
177
|
def as_xml
|
178
178
|
xml = REXML::Element.new('SiloAccess')
|
179
|
-
xml.add_attributes({'all-groups'
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
179
|
+
xml.add_attributes({ 'all-groups' => @all_groups,
|
180
|
+
'all-sites' => @all_sites,
|
181
|
+
'role-name' => @role_name,
|
182
|
+
'silo-id' => @silo_id,
|
183
|
+
'default-silo' => @default })
|
184
184
|
|
185
185
|
unless @groups.empty?
|
186
186
|
groups = xml.add_element('AllowedGroups')
|
187
187
|
@groups.each do |group|
|
188
|
-
groups.add_element('AllowedGroup', {'id' => group})
|
188
|
+
groups.add_element('AllowedGroup', { 'id' => group })
|
189
189
|
end
|
190
190
|
end
|
191
191
|
|
192
192
|
unless @sites.empty?
|
193
193
|
sites = xml.add_element('AllowedSites')
|
194
194
|
@sites.each do |site|
|
195
|
-
sites.add_element('AllowedSite', {'id' => site})
|
195
|
+
sites.add_element('AllowedSite', { 'id' => site })
|
196
196
|
end
|
197
197
|
end
|
198
198
|
|
@@ -206,11 +206,11 @@ module Nexpose
|
|
206
206
|
def self.parse(xml)
|
207
207
|
new do |access|
|
208
208
|
access.all_groups = xml.attributes['all-groups'].to_s.chomp.eql?('true')
|
209
|
-
access.all_sites
|
210
|
-
access.role_name
|
211
|
-
access.silo_id
|
212
|
-
access.default
|
213
|
-
access.sites
|
209
|
+
access.all_sites = xml.attributes['all-sites'].to_s.chomp.eql?('true')
|
210
|
+
access.role_name = xml.attributes['role-name']
|
211
|
+
access.silo_id = xml.attributes['silo-id']
|
212
|
+
access.default = xml.attributes['default-silo'].to_s.chomp.eql?('true')
|
213
|
+
access.sites = []
|
214
214
|
xml.elements.each('AllowedSites/AllowedSite') { |site| access.sites << site.attributes['id'].to_i }
|
215
215
|
access.groups = []
|
216
216
|
xml.elements.each('AllowedGroups/AllowedGroup') { |group| access.groups << group.attributes['id'].to_i }
|
@@ -11,12 +11,12 @@ module Nexpose
|
|
11
11
|
attr_accessor :expiration_days
|
12
12
|
|
13
13
|
def initialize(policy_name:, min_length:, max_length:, special_chars:, capitals:, digits:, expiration_days: 0)
|
14
|
-
@policy_name
|
15
|
-
@min_length
|
16
|
-
@max_length
|
17
|
-
@special_chars
|
18
|
-
@capitals
|
19
|
-
@digits
|
14
|
+
@policy_name = policy_name.to_s
|
15
|
+
@min_length = min_length.to_i
|
16
|
+
@max_length = max_length.to_i
|
17
|
+
@special_chars = special_chars.to_i
|
18
|
+
@capitals = capitals.to_i
|
19
|
+
@digits = digits.to_i
|
20
20
|
@expiration_days = expiration_days.to_i
|
21
21
|
end
|
22
22
|
|
@@ -32,13 +32,13 @@ module Nexpose
|
|
32
32
|
|
33
33
|
def to_h
|
34
34
|
{
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
35
|
+
policyName: @policy_name,
|
36
|
+
minLength: @min_length,
|
37
|
+
maxLength: @max_length,
|
38
|
+
specialChars: @special_chars,
|
39
|
+
capitals: @capitals,
|
40
|
+
digits: @digits,
|
41
|
+
expirationDays: @expiration_days
|
42
42
|
}
|
43
43
|
end
|
44
44
|
|
@@ -52,7 +52,7 @@ module Nexpose
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def self.load(nsc)
|
55
|
-
uri
|
55
|
+
uri = '/api/2.1/password_policy/'
|
56
56
|
resp = AJAX.get(nsc, uri, AJAX::CONTENT_TYPE::JSON)
|
57
57
|
hash = JSON.parse(resp, symbolize_names: true)
|
58
58
|
self.from_hash(hash)
|