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/ticket.rb
CHANGED
@@ -6,7 +6,7 @@ module Nexpose
|
|
6
6
|
def list_tickets
|
7
7
|
# TODO: Should take in filters as arguments.
|
8
8
|
xml = make_xml('TicketListingRequest')
|
9
|
-
r
|
9
|
+
r = execute(xml, '1.2')
|
10
10
|
tickets = []
|
11
11
|
if r.success
|
12
12
|
r.res.elements.each('TicketListingResponse/TicketSummary') do |summary|
|
@@ -16,7 +16,7 @@ module Nexpose
|
|
16
16
|
tickets
|
17
17
|
end
|
18
18
|
|
19
|
-
|
19
|
+
alias tickets list_tickets
|
20
20
|
|
21
21
|
# Deletes a Nexpose ticket.
|
22
22
|
#
|
@@ -57,8 +57,8 @@ module Nexpose
|
|
57
57
|
|
58
58
|
# The asset the ticket is created for.
|
59
59
|
attr_accessor :asset_id
|
60
|
-
alias
|
61
|
-
alias
|
60
|
+
alias device_id asset_id
|
61
|
+
alias device_id= asset_id=
|
62
62
|
|
63
63
|
# The login name of person to whom the ticket is assigned.
|
64
64
|
# The user must have view asset privilege on the asset specified in the asset-id attribute.
|
@@ -78,43 +78,43 @@ module Nexpose
|
|
78
78
|
attr_accessor :state
|
79
79
|
|
80
80
|
def initialize(name, id)
|
81
|
-
@id
|
81
|
+
@id = id
|
82
|
+
@name = name
|
82
83
|
end
|
83
84
|
|
84
85
|
def self.parse(xml)
|
85
|
-
ticket
|
86
|
-
|
87
|
-
ticket.
|
88
|
-
|
89
|
-
|
90
|
-
ticket.
|
91
|
-
ticket.
|
92
|
-
ticket.created_on = DateTime.parse(xml.attributes['created-on']).to_time
|
86
|
+
ticket = new(xml.attributes['name'], xml.attributes['id'].to_i)
|
87
|
+
ticket.asset_id = xml.attributes['device-id'].to_i
|
88
|
+
ticket.assigned_to = xml.attributes['assigned-to']
|
89
|
+
lookup = Ticket::Priority.constants.reduce({}) { |a, e| a[Ticket::Priority.const_get(e)] = e; a }
|
90
|
+
ticket.priority = lookup[xml.attributes['priority']]
|
91
|
+
ticket.author = xml.attributes['author']
|
92
|
+
ticket.created_on = DateTime.parse(xml.attributes['created-on']).to_time
|
93
93
|
ticket.created_on -= ticket.created_on.gmt_offset
|
94
|
-
lookup
|
95
|
-
ticket.state
|
94
|
+
lookup = Ticket::State.constants.reduce({}) { |a, e| a[Ticket::State.const_get(e)] = e; a }
|
95
|
+
ticket.state = lookup[xml.attributes['state']]
|
96
96
|
ticket
|
97
97
|
end
|
98
98
|
|
99
99
|
module State
|
100
|
-
OPEN
|
101
|
-
ASSIGNED
|
102
|
-
MODIFIED
|
103
|
-
FIXED
|
104
|
-
PARTIAL
|
105
|
-
REJECTED_FIX
|
106
|
-
PRIORITIZED
|
100
|
+
OPEN = 'O'
|
101
|
+
ASSIGNED = 'A'
|
102
|
+
MODIFIED = 'M'
|
103
|
+
FIXED = 'X'
|
104
|
+
PARTIAL = 'P'
|
105
|
+
REJECTED_FIX = 'R'
|
106
|
+
PRIORITIZED = 'Z'
|
107
107
|
NOT_REPRODUCIBLE = 'F'
|
108
|
-
NOT_ISSUE
|
109
|
-
CLOSED
|
110
|
-
UNKNOWN
|
108
|
+
NOT_ISSUE = 'I'
|
109
|
+
CLOSED = 'C'
|
110
|
+
UNKNOWN = 'U'
|
111
111
|
end
|
112
112
|
|
113
113
|
module Priority
|
114
|
-
LOW
|
114
|
+
LOW = 'low'
|
115
115
|
MODERATE = 'moderate'
|
116
|
-
NORMAL
|
117
|
-
HIGH
|
116
|
+
NORMAL = 'normal'
|
117
|
+
HIGH = 'high'
|
118
118
|
CRITICAL = 'critical'
|
119
119
|
end
|
120
120
|
end
|
@@ -131,11 +131,12 @@ module Nexpose
|
|
131
131
|
attr_accessor :history
|
132
132
|
|
133
133
|
def initialize(name, id = nil)
|
134
|
-
@id
|
135
|
-
@
|
134
|
+
@id = id
|
135
|
+
@name = name
|
136
|
+
@priority = Priority::NORMAL
|
136
137
|
@vulnerabilities = []
|
137
|
-
@comments
|
138
|
-
@history
|
138
|
+
@comments = []
|
139
|
+
@history = []
|
139
140
|
end
|
140
141
|
|
141
142
|
# Save this ticket to a Nexpose console.
|
@@ -213,7 +214,7 @@ module Nexpose
|
|
213
214
|
ticket.history << Event.parse(entry)
|
214
215
|
end
|
215
216
|
|
216
|
-
ticket.comments = ticket.history.select { |h| h.description == 'Added comment' }.map
|
217
|
+
ticket.comments = ticket.history.select { |h| h.description == 'Added comment' }.map(&:comment)
|
217
218
|
|
218
219
|
ticket
|
219
220
|
end
|
@@ -232,22 +233,24 @@ module Nexpose
|
|
232
233
|
attr_accessor :comment
|
233
234
|
|
234
235
|
def initialize(state, author, created)
|
235
|
-
@state
|
236
|
+
@state = state
|
237
|
+
@author = author
|
238
|
+
@created = created
|
236
239
|
end
|
237
240
|
|
238
241
|
def self.parse(xml)
|
239
|
-
author
|
240
|
-
created_on
|
242
|
+
author = xml.attributes['author']
|
243
|
+
created_on = DateTime.parse(xml.attributes['created-on']).to_time
|
241
244
|
created_on -= created_on.gmt_offset
|
242
245
|
|
243
|
-
event
|
244
|
-
lookup
|
245
|
-
state
|
246
|
-
desc
|
246
|
+
event = REXML::XPath.first(xml, 'Event')
|
247
|
+
lookup = Ticket::State.constants.reduce({}) { |a, e| a[Ticket::State.const_get(e)] = e; a }
|
248
|
+
state = lookup[event.attributes['state']]
|
249
|
+
desc = event.text
|
247
250
|
|
248
|
-
event
|
251
|
+
event = new(state, author, created_on)
|
249
252
|
|
250
|
-
comment
|
253
|
+
comment = REXML::XPath.first(xml, 'Comment')
|
251
254
|
event.comment = comment.text if comment
|
252
255
|
|
253
256
|
event.description = desc if desc
|
data/lib/nexpose/user.rb
CHANGED
@@ -18,7 +18,7 @@ module Nexpose
|
|
18
18
|
arr
|
19
19
|
end
|
20
20
|
|
21
|
-
|
21
|
+
alias users list_users
|
22
22
|
|
23
23
|
# Retrieve the User ID based upon the user's login name.
|
24
24
|
#
|
@@ -47,16 +47,16 @@ module Nexpose
|
|
47
47
|
attr_reader :is_admin, :is_disabled, :is_locked, :site_count, :group_count
|
48
48
|
|
49
49
|
def initialize(id, auth_source, auth_module, name, full_name, email, is_admin, is_disabled, is_locked, site_count, group_count)
|
50
|
-
@id
|
50
|
+
@id = id
|
51
51
|
@auth_source = auth_source
|
52
52
|
@auth_module = auth_module
|
53
|
-
@name
|
54
|
-
@full_name
|
55
|
-
@email
|
56
|
-
@is_admin
|
53
|
+
@name = name
|
54
|
+
@full_name = full_name
|
55
|
+
@email = email
|
56
|
+
@is_admin = is_admin
|
57
57
|
@is_disabled = is_disabled
|
58
|
-
@is_locked
|
59
|
-
@site_count
|
58
|
+
@is_locked = is_locked
|
59
|
+
@site_count = site_count
|
60
60
|
@group_count = group_count
|
61
61
|
end
|
62
62
|
|
@@ -97,41 +97,41 @@ module Nexpose
|
|
97
97
|
attr_accessor :all_sites, :all_groups
|
98
98
|
|
99
99
|
def initialize(name, full_name, password, role_name = 'user', id = -1, enabled = 1, email = nil, all_sites = false, all_groups = false, token = nil)
|
100
|
-
@name
|
101
|
-
@password
|
102
|
-
@token
|
103
|
-
@role_name
|
104
|
-
@authsrcid
|
105
|
-
@id
|
106
|
-
@enabled
|
107
|
-
@full_name
|
108
|
-
@email
|
109
|
-
@all_sites
|
100
|
+
@name = name
|
101
|
+
@password = password
|
102
|
+
@token = token
|
103
|
+
@role_name = role_name
|
104
|
+
@authsrcid = 'global-admin'.eql?(@role_name) ? '1' : '2'
|
105
|
+
@id = id
|
106
|
+
@enabled = enabled
|
107
|
+
@full_name = full_name
|
108
|
+
@email = email
|
109
|
+
@all_sites = all_sites || role_name == 'global-admin'
|
110
110
|
@all_groups = all_groups || role_name == 'global-admin'
|
111
|
-
@sites
|
112
|
-
@groups
|
111
|
+
@sites = []
|
112
|
+
@groups = []
|
113
113
|
end
|
114
114
|
|
115
115
|
def to_xml
|
116
116
|
xml = '<UserConfig'
|
117
|
-
xml << %
|
118
|
-
xml << %
|
119
|
-
xml << %
|
120
|
-
xml << %
|
121
|
-
xml << %
|
122
|
-
xml << %
|
123
|
-
xml << %
|
124
|
-
xml << %
|
125
|
-
xml << %
|
117
|
+
xml << %( id="#{@id}" )
|
118
|
+
xml << %( authsrcid="#{@authsrcid}" )
|
119
|
+
xml << %( name="#{replace_entities(@name)}" )
|
120
|
+
xml << %( fullname="#{replace_entities(@full_name)}" )
|
121
|
+
xml << %( role-name="#{replace_entities(@role_name)}" )
|
122
|
+
xml << %( password="#{replace_entities(@password)}" ) if @password
|
123
|
+
xml << %( token="#{replace_entities(@token)}" ) if @token
|
124
|
+
xml << %( email="#{replace_entities(@email)}" ) if @email
|
125
|
+
xml << %( enabled="#{@enabled}" )
|
126
126
|
# These two fields are keying off role_name to work around a defect.
|
127
|
-
xml << %
|
128
|
-
xml << %
|
127
|
+
xml << %( allGroups="#{@all_groups || @role_name == 'global-admin'}" )
|
128
|
+
xml << %( allSites="#{@all_sites || @role_name == 'global-admin'}" )
|
129
129
|
xml << '>'
|
130
130
|
@sites.each do |site|
|
131
|
-
xml << %
|
131
|
+
xml << %( <site id="#{site}" /> )
|
132
132
|
end
|
133
133
|
@groups.each do |group|
|
134
|
-
xml << %
|
134
|
+
xml << %( <group id="#{group}" /> )
|
135
135
|
end
|
136
136
|
xml << '</UserConfig>'
|
137
137
|
end
|
@@ -155,22 +155,22 @@ module Nexpose
|
|
155
155
|
# Issue a UserConfigRequest to load an existing UserConfig from Nexpose.
|
156
156
|
def self.load(connection, user_id)
|
157
157
|
xml = '<UserConfigRequest session-id="' + connection.session_id + '"'
|
158
|
-
xml << %
|
158
|
+
xml << %( id="#{user_id}" )
|
159
159
|
xml << ' />'
|
160
160
|
r = connection.execute(xml, '1.1')
|
161
161
|
if r.success
|
162
162
|
r.res.elements.each('UserConfigResponse/UserConfig') do |config|
|
163
|
-
id
|
164
|
-
role_name
|
165
|
-
#authsrcid
|
166
|
-
name
|
167
|
-
fullname
|
168
|
-
|
169
|
-
email
|
170
|
-
password
|
171
|
-
token
|
172
|
-
enabled
|
173
|
-
all_sites
|
163
|
+
id = config.attributes['id']
|
164
|
+
role_name = config.attributes['role-name']
|
165
|
+
# authsrcid = config.attributes['authsrcid']
|
166
|
+
name = config.attributes['name']
|
167
|
+
fullname = config.attributes['fullname']
|
168
|
+
|
169
|
+
email = config.attributes['email']
|
170
|
+
password = config.attributes['password']
|
171
|
+
token = config.attributes['token']
|
172
|
+
enabled = config.attributes['enabled'].to_i
|
173
|
+
all_sites = config.attributes['allSites'] == 'true' ? true : false
|
174
174
|
all_groups = config.attributes['allGroups'] == 'true' ? true : false
|
175
175
|
# Not trying to load sites and groups.
|
176
176
|
# Looks like API currently doesn't return that info to load.
|
data/lib/nexpose/util.rb
CHANGED
@@ -30,7 +30,7 @@ module Nexpose
|
|
30
30
|
# '1' if the call succeeded.
|
31
31
|
#
|
32
32
|
def self.success?(xml_string)
|
33
|
-
xml
|
33
|
+
xml = ::REXML::Document.new(xml_string.to_s)
|
34
34
|
success = ::REXML::XPath.first(xml, '//@success')
|
35
35
|
!success.nil? && success.value.to_i == 1
|
36
36
|
end
|
data/lib/nexpose/version.rb
CHANGED
data/lib/nexpose/vuln.rb
CHANGED
@@ -20,15 +20,15 @@ module Nexpose
|
|
20
20
|
vulns << XML::VulnerabilitySummary.parse(vuln)
|
21
21
|
else
|
22
22
|
vulns << XML::Vulnerability.new(vuln.attributes['id'],
|
23
|
-
|
24
|
-
|
23
|
+
vuln.attributes['title'],
|
24
|
+
vuln.attributes['severity'].to_i)
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
28
28
|
vulns
|
29
29
|
end
|
30
30
|
|
31
|
-
|
31
|
+
alias vulns list_vulns
|
32
32
|
|
33
33
|
# Retrieve a list of the different vulnerability check categories.
|
34
34
|
#
|
@@ -39,7 +39,7 @@ module Nexpose
|
|
39
39
|
data.map { |c| c['Category'] }
|
40
40
|
end
|
41
41
|
|
42
|
-
|
42
|
+
alias vuln_categories list_vuln_categories
|
43
43
|
|
44
44
|
# Retrieve a list of the different vulnerability check types.
|
45
45
|
#
|
@@ -49,7 +49,7 @@ module Nexpose
|
|
49
49
|
data = DataTable._get_dyn_table(self, '/data/vulnerability/checktypes/dyntable.xml?tableID=VulnCheckCategorySynopsis')
|
50
50
|
data.map { |c| c['Category'] }
|
51
51
|
end
|
52
|
-
|
52
|
+
alias list_vuln_types vuln_types
|
53
53
|
|
54
54
|
# Retrieve details for a vulnerability.
|
55
55
|
#
|
@@ -111,7 +111,9 @@ module Nexpose
|
|
111
111
|
attr_reader :severity
|
112
112
|
|
113
113
|
def initialize(id, title, severity)
|
114
|
-
@id
|
114
|
+
@id = id
|
115
|
+
@title = title
|
116
|
+
@severity = severity.to_i
|
115
117
|
end
|
116
118
|
end
|
117
119
|
|
@@ -126,10 +128,10 @@ module Nexpose
|
|
126
128
|
attr_reader :check_type
|
127
129
|
|
128
130
|
def initialize(json)
|
129
|
-
@id
|
130
|
-
@check_id
|
131
|
-
@title
|
132
|
-
@severity
|
131
|
+
@id = json['Vuln ID']
|
132
|
+
@check_id = json['Vuln Check ID']
|
133
|
+
@title = json['Vulnerability']
|
134
|
+
@severity = json['Severity'].to_i
|
133
135
|
@check_type = json['Check Type']
|
134
136
|
@categories = json['Category'].split(/, */)
|
135
137
|
end
|
@@ -165,15 +167,15 @@ module Nexpose
|
|
165
167
|
xml.attributes['severity'].to_i)
|
166
168
|
|
167
169
|
vuln.pci_severity = xml.attributes['pciSeverity'].to_i
|
168
|
-
vuln.safe
|
169
|
-
vuln.added
|
170
|
-
vuln.modified
|
171
|
-
vuln.credentials
|
170
|
+
vuln.safe = xml.attributes['safe'] == 'true' # or xml.attributes['safe'] == '1'
|
171
|
+
vuln.added = Date.parse(xml.attributes['added'])
|
172
|
+
vuln.modified = Date.parse(xml.attributes['modified'])
|
173
|
+
vuln.credentials = xml.attributes['requiresCredentials'] == 'true'
|
172
174
|
|
173
175
|
# These three fields are optional in the XSD.
|
174
|
-
vuln.published
|
175
|
-
vuln.cvss_vector
|
176
|
-
vuln.cvss_score
|
176
|
+
vuln.published = Date.parse(xml.attributes['published']) if xml.attributes['published']
|
177
|
+
vuln.cvss_vector = xml.attributes['cvssVector'] if xml.attributes['cvssVector']
|
178
|
+
vuln.cvss_score = xml.attributes['cvssScore'].to_f if xml.attributes['cvssScore']
|
177
179
|
vuln
|
178
180
|
end
|
179
181
|
|
@@ -194,16 +196,16 @@ module Nexpose
|
|
194
196
|
attr_accessor :solution
|
195
197
|
|
196
198
|
def initialize(id, title, severity)
|
197
|
-
@id
|
199
|
+
@id = id
|
200
|
+
@title = title
|
201
|
+
@severity = severity
|
198
202
|
@references = []
|
199
203
|
end
|
200
204
|
|
201
205
|
def self.parse(xml)
|
202
|
-
vuln
|
203
|
-
|
206
|
+
vuln = parse_attributes(xml)
|
204
207
|
vuln.description = REXML::XPath.first(xml, 'description').text
|
205
|
-
vuln.solution
|
206
|
-
|
208
|
+
vuln.solution = REXML::XPath.first(xml, 'solution').text
|
207
209
|
xml.elements.each('references/reference') do |ref|
|
208
210
|
vuln.references << Reference.new(ref.attributes['source'], ref.text)
|
209
211
|
end
|
@@ -218,7 +220,7 @@ module Nexpose
|
|
218
220
|
attr_reader :reference
|
219
221
|
|
220
222
|
def initialize(source, reference)
|
221
|
-
@source
|
223
|
+
@source = source
|
222
224
|
@reference = reference
|
223
225
|
end
|
224
226
|
end
|
@@ -251,18 +253,18 @@ module Nexpose
|
|
251
253
|
attr_reader :verified
|
252
254
|
|
253
255
|
def initialize(json)
|
254
|
-
@id
|
255
|
-
@console_id
|
256
|
-
@title
|
256
|
+
@id = json['nexVulnID']
|
257
|
+
@console_id = json['vulnID']
|
258
|
+
@title = json['title']
|
257
259
|
@cvss_vector = json['cvssBase']
|
258
|
-
@cvss_score
|
259
|
-
@risk
|
260
|
-
@published
|
261
|
-
@severity
|
262
|
-
@instances
|
263
|
-
@exploit
|
264
|
-
@malware
|
265
|
-
@verified
|
260
|
+
@cvss_score = json['cvssScore']
|
261
|
+
@risk = json['riskScore']
|
262
|
+
@published = Time.at(json['publishedDate'] / 1000)
|
263
|
+
@severity = json['severity']
|
264
|
+
@instances = json['vulnInstanceCount']
|
265
|
+
@exploit = json['mainExploit']
|
266
|
+
@malware = json['malwareCount']
|
267
|
+
@verified = DateTime.iso8601(json['verifiedDate'].slice(0, 15)).to_time if json['verifiedDate']
|
266
268
|
end
|
267
269
|
end
|
268
270
|
|
@@ -272,16 +274,16 @@ module Nexpose
|
|
272
274
|
#
|
273
275
|
class VulnSynopsis < VulnFinding
|
274
276
|
def initialize(hash)
|
275
|
-
@id
|
276
|
-
@title
|
277
|
+
@id = hash['Vuln ID'].to_i
|
278
|
+
@title = hash['Vulnerability']
|
277
279
|
@cvss_vector = hash['CVSS Base Vector']
|
278
|
-
@cvss_score
|
279
|
-
@risk
|
280
|
-
@published
|
281
|
-
@severity
|
282
|
-
@instances
|
283
|
-
@exploit
|
284
|
-
@malware
|
280
|
+
@cvss_score = hash['CVSS Score'].to_f
|
281
|
+
@risk = hash['Risk'].to_f
|
282
|
+
@published = Time.at(hash['Published On'].to_i / 1000)
|
283
|
+
@severity = hash['Severity'].to_i
|
284
|
+
@instances = hash['Instances'].to_i
|
285
|
+
@exploit = hash['ExploitSource']
|
286
|
+
@malware = hash['MalwareSource'] == 'true'
|
285
287
|
end
|
286
288
|
end
|
287
289
|
|