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
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
|
|