nexpose 0.9.8 → 1.0.0
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/Gemfile.lock +1 -1
- data/lib/nexpose.rb +8 -4
- data/lib/nexpose/ajax.rb +29 -4
- data/lib/nexpose/alert.rb +160 -177
- data/lib/nexpose/api.rb +18 -0
- data/lib/nexpose/common.rb +144 -10
- data/lib/nexpose/credential.rb +185 -1
- data/lib/nexpose/discovery.rb +141 -16
- data/lib/nexpose/discovery/filter.rb +26 -3
- data/lib/nexpose/engine.rb +16 -0
- data/lib/nexpose/json_serializer.rb +92 -0
- data/lib/nexpose/scan.rb +131 -23
- data/lib/nexpose/scan_template.rb +1 -1
- data/lib/nexpose/shared_secret.rb +31 -0
- data/lib/nexpose/site.rb +339 -317
- data/lib/nexpose/site_credentials.rb +178 -0
- data/lib/nexpose/tag.rb +42 -1
- data/lib/nexpose/util.rb +11 -16
- data/lib/nexpose/version.rb +1 -1
- data/lib/nexpose/wait.rb +103 -0
- data/lib/nexpose/web_credentials.rb +252 -0
- metadata +18 -8
- data/lib/nexpose/site_credential.rb +0 -323
data/lib/nexpose/api.rb
CHANGED
@@ -82,4 +82,22 @@ module Nexpose
|
|
82
82
|
Object.const_get('Nexpose').const_get(str)
|
83
83
|
end
|
84
84
|
end
|
85
|
+
|
86
|
+
module TypedAccessor
|
87
|
+
def typed_accessor(name, type)
|
88
|
+
|
89
|
+
# here we dynamically define accessor methods
|
90
|
+
define_method(name) do
|
91
|
+
instance_variable_get("@#{name}")
|
92
|
+
end
|
93
|
+
|
94
|
+
define_method("#{name}=") do |value|
|
95
|
+
instance_variable_set("@#{name}", value)
|
96
|
+
end
|
97
|
+
|
98
|
+
define_method("#{name}_type") do
|
99
|
+
type
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
85
103
|
end
|
data/lib/nexpose/common.rb
CHANGED
@@ -91,8 +91,44 @@ module Nexpose
|
|
91
91
|
end
|
92
92
|
end
|
93
93
|
|
94
|
+
# Configuration structure for ad-hoc schedules
|
95
|
+
class AdHocSchedule < APIObject
|
96
|
+
# Start time in ISO8601 format
|
97
|
+
attr_accessor :start
|
98
|
+
|
99
|
+
# The template to use to scan the assets
|
100
|
+
attr_accessor :scan_template_id
|
101
|
+
|
102
|
+
# The amount of time, in minutes, to allow execution before stopping.
|
103
|
+
attr_accessor :max_duration
|
104
|
+
|
105
|
+
def initialize(start, scan_template_id, max_duration = nil)
|
106
|
+
@start = start
|
107
|
+
@scan_template_id = scan_template_id
|
108
|
+
@max_duration = max_duration if max_duration
|
109
|
+
end
|
110
|
+
|
111
|
+
def as_xml
|
112
|
+
xml = REXML::Element.new('AdHocSchedule')
|
113
|
+
xml.attributes['start'] = @start
|
114
|
+
xml.attributes['maxDuration'] = @max_duration if @max_duration
|
115
|
+
xml.attributes['template'] = @scan_template_id
|
116
|
+
xml
|
117
|
+
end
|
118
|
+
|
119
|
+
def from_hash(hash)
|
120
|
+
schedule = AdHocSchedule.new(hash[:start], hash[:scan_template_id])
|
121
|
+
schedule.max_duration = hash[:max_duration] if hash[:max_duration]
|
122
|
+
schedule
|
123
|
+
end
|
124
|
+
|
125
|
+
def to_xml
|
126
|
+
as_xml.to_s
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
94
130
|
# Configuration structure for schedules.
|
95
|
-
class Schedule
|
131
|
+
class Schedule < APIObject
|
96
132
|
# Whether or not this schedule is enabled.
|
97
133
|
attr_accessor :enabled
|
98
134
|
# Valid schedule types: daily, hourly, monthly-date, monthly-day, weekly.
|
@@ -107,8 +143,8 @@ module Nexpose
|
|
107
143
|
# The date after which the schedule is disabled, in ISO 8601 format.
|
108
144
|
attr_accessor :not_valid_after
|
109
145
|
|
146
|
+
# TODO: Remove this unused attribute
|
110
147
|
attr_accessor :incremental
|
111
|
-
attr_accessor :repeater_type
|
112
148
|
|
113
149
|
# Extended attributes added with the new scheduler implementation
|
114
150
|
attr_accessor :is_extended
|
@@ -120,23 +156,89 @@ module Nexpose
|
|
120
156
|
attr_accessor :start_month
|
121
157
|
attr_accessor :timezone
|
122
158
|
attr_accessor :next_run_time
|
123
|
-
attr_accessor :template
|
124
159
|
|
125
|
-
|
160
|
+
# scan-schedule attributes
|
161
|
+
attr_accessor :repeater_type
|
162
|
+
attr_accessor :scan_template_id
|
163
|
+
|
164
|
+
# @param [Time] start
|
165
|
+
def initialize(type, interval, start, enabled = true, scan_template_id = nil)
|
126
166
|
@type = type
|
127
167
|
@interval = interval
|
128
168
|
@start = start
|
129
169
|
@enabled = enabled
|
170
|
+
@scan_template_id = scan_template_id
|
130
171
|
end
|
131
172
|
|
132
173
|
def self.from_hash(hash)
|
133
|
-
|
134
|
-
hash
|
135
|
-
|
174
|
+
start = nil
|
175
|
+
start = Nexpose::ISO8601.to_time(hash[:start_date]) if hash[:start_date]
|
176
|
+
repeat_scan_hash = hash[:repeat_scan]
|
177
|
+
if repeat_scan_hash.nil?
|
178
|
+
schedule = new('daily', 0, start)
|
179
|
+
else
|
180
|
+
schedule = new(repeat_scan_hash[:type], repeat_scan_hash[:interval], start)
|
181
|
+
end
|
182
|
+
schedule.enabled = hash[:enabled].nil? ? true : hash[:enabled]
|
183
|
+
schedule.scan_template_id = hash[:scan_template_id]
|
184
|
+
schedule.start = Nexpose::ISO8601.to_time(hash[:start_date]) if hash[:start_date]
|
185
|
+
schedule.max_duration = hash[:maximum_scan_duration] if hash[:maximum_scan_duration]
|
186
|
+
schedule.not_valid_after = Nexpose::ISO8601.to_time(hash[:not_valid_after_date]) if hash[:not_valid_after_date]
|
187
|
+
schedule.timezone = hash[:time_zone] if hash[:time_zone]
|
188
|
+
schedule.next_run_time = hash[:next_run_time] if hash[:next_run_time]
|
189
|
+
|
190
|
+
unless repeat_scan_hash.nil?
|
191
|
+
schedule.type = repeat_scan_hash[:type]
|
192
|
+
schedule.interval = repeat_scan_hash[:interval]
|
193
|
+
schedule.repeater_type = 'restart' if repeat_scan_hash[:on_repeat] == 'restart-scan'
|
194
|
+
schedule.repeater_type = 'continue' if repeat_scan_hash[:on_repeat] == 'resume-scan'
|
195
|
+
|
196
|
+
schedule.is_extended = repeat_scan_hash[:is_extended] if repeat_scan_hash[:is_extended]
|
197
|
+
schedule.hour = repeat_scan_hash[:hour] if repeat_scan_hash[:hour]
|
198
|
+
schedule.minute = repeat_scan_hash[:minute] if repeat_scan_hash[:minute]
|
199
|
+
schedule.date = repeat_scan_hash[:date] if repeat_scan_hash[:date]
|
200
|
+
schedule.day = repeat_scan_hash[:day] if repeat_scan_hash[:day]
|
201
|
+
schedule.occurrence = repeat_scan_hash[:occurrence] if repeat_scan_hash[:occurrence]
|
202
|
+
schedule.start_month = repeat_scan_hash[:start_month] if repeat_scan_hash[:start_month]
|
136
203
|
end
|
204
|
+
|
137
205
|
schedule
|
138
206
|
end
|
139
207
|
|
208
|
+
def to_h
|
209
|
+
schedule_hash = {
|
210
|
+
enabled: @enabled,
|
211
|
+
scan_template_id: @scan_template_id,
|
212
|
+
maximum_scan_duration: @max_duration
|
213
|
+
}
|
214
|
+
schedule_hash[:start_date] = Nexpose::ISO8601.to_string(@start) if @start
|
215
|
+
schedule_hash[:not_valid_after_date] = Nexpose::ISO8601.to_string(@not_valid_after) if @not_valid_after
|
216
|
+
schedule_hash[:time_zone] = @timezone if @timezone
|
217
|
+
|
218
|
+
unless (@type.nil? || @interval == 0) && !@is_extended
|
219
|
+
repeat_scan_hash = {
|
220
|
+
type: @type,
|
221
|
+
interval: @interval
|
222
|
+
}
|
223
|
+
repeat_scan_hash[:on_repeat] = 'restart-scan' if @repeater_type == 'restart'
|
224
|
+
repeat_scan_hash[:on_repeat] = 'resume-scan' if @repeater_type == 'continue'
|
225
|
+
|
226
|
+
if @is_extended
|
227
|
+
repeat_scan_hash[:is_extended] = @is_extended
|
228
|
+
repeat_scan_hash[:hour] = @hour if @hour
|
229
|
+
repeat_scan_hash[:minute] = @minute if @minute
|
230
|
+
repeat_scan_hash[:date] = @date if @date
|
231
|
+
repeat_scan_hash[:day] = @day if @day
|
232
|
+
repeat_scan_hash[:occurrence] = @occurrence if @occurrence
|
233
|
+
repeat_scan_hash[:start_month] = @start_month if @start_month
|
234
|
+
end
|
235
|
+
|
236
|
+
schedule_hash[:repeat_scan] = repeat_scan_hash
|
237
|
+
end
|
238
|
+
|
239
|
+
schedule_hash
|
240
|
+
end
|
241
|
+
|
140
242
|
def as_xml
|
141
243
|
xml = REXML::Element.new('Schedule')
|
142
244
|
xml.attributes['enabled'] = @enabled ? 1 : 0
|
@@ -155,7 +257,7 @@ module Nexpose
|
|
155
257
|
xml.attributes['occurrence'] = @occurrence if @occurrence
|
156
258
|
xml.attributes['start_month'] = @start_month if @start_month
|
157
259
|
xml.attributes['timezone'] = @timezone if @timezone
|
158
|
-
xml.attributes['template'] = @
|
260
|
+
xml.attributes['template'] = @scan_template_id if @scan_template_id
|
159
261
|
xml
|
160
262
|
end
|
161
263
|
|
@@ -183,7 +285,7 @@ module Nexpose
|
|
183
285
|
schedule.start_month = xml.attributes['start_month'] if xml.attributes['start_month']
|
184
286
|
schedule.timezone = xml.attributes['timezone'] if xml.attributes['timezone']
|
185
287
|
schedule.next_run_time = xml.attributes['next_run_time'] if xml.attributes['next_run_time']
|
186
|
-
schedule.
|
288
|
+
schedule.scan_template_id = xml.attributes['template'] if xml.attributes['template']
|
187
289
|
schedule
|
188
290
|
end
|
189
291
|
|
@@ -200,7 +302,7 @@ module Nexpose
|
|
200
302
|
end
|
201
303
|
|
202
304
|
# Organization configuration, as used in Site and Silo.
|
203
|
-
class Organization
|
305
|
+
class Organization < APIObject
|
204
306
|
attr_accessor :name
|
205
307
|
attr_accessor :url
|
206
308
|
attr_accessor :primary_contact
|
@@ -217,6 +319,38 @@ module Nexpose
|
|
217
319
|
instance_eval(&block) if block_given?
|
218
320
|
end
|
219
321
|
|
322
|
+
def to_h
|
323
|
+
{ name: name,
|
324
|
+
url: url,
|
325
|
+
primary_contact: primary_contact,
|
326
|
+
job_title: job_title,
|
327
|
+
email: email,
|
328
|
+
telephone: telephone,
|
329
|
+
address: address,
|
330
|
+
state: state,
|
331
|
+
city: city,
|
332
|
+
zip: zip,
|
333
|
+
country: country
|
334
|
+
}
|
335
|
+
end
|
336
|
+
|
337
|
+
# Create organization object from hash
|
338
|
+
def self.create(hash)
|
339
|
+
new do |org|
|
340
|
+
org.name = hash[:name]
|
341
|
+
org.url = hash[:url]
|
342
|
+
org.primary_contact = hash[:primary_contact]
|
343
|
+
org.job_title = hash[:job_title]
|
344
|
+
org.email = hash[:email]
|
345
|
+
org.telephone = hash[:telephone]
|
346
|
+
org.address = hash[:address]
|
347
|
+
org.state = hash[:state]
|
348
|
+
org.city = hash[:city]
|
349
|
+
org.zip = hash[:zip]
|
350
|
+
org.country = hash[:country]
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
220
354
|
def self.parse(xml)
|
221
355
|
new do |org|
|
222
356
|
org.name = xml.attributes['name']
|
data/lib/nexpose/credential.rb
CHANGED
@@ -2,7 +2,7 @@ module Nexpose
|
|
2
2
|
|
3
3
|
# Contains the shared methods for the SiteCredential and SharedCredential Objects.
|
4
4
|
# See Nexpose::SiteCredential or Nexpose::SharedCredential for additional info.
|
5
|
-
class Credential
|
5
|
+
class Credential < APIObject
|
6
6
|
|
7
7
|
# Mapping of Common Ports.
|
8
8
|
DEFAULT_PORTS = { 'cvs' => 2401,
|
@@ -27,6 +27,13 @@ module Nexpose
|
|
27
27
|
'db2' => 50000 }
|
28
28
|
|
29
29
|
|
30
|
+
# Credential scope
|
31
|
+
module Scope
|
32
|
+
ALL_SITES_ENABLED_DEFAULT = 'A'
|
33
|
+
ALL_SITES_DISABLED_DEFAULT = 'G'
|
34
|
+
SITE_SPECIFIC = 'S'
|
35
|
+
end
|
36
|
+
|
30
37
|
# Credential Service/Type Options.
|
31
38
|
module Service
|
32
39
|
CVS = 'cvs' # Concurrent Versioning System (CVS)
|
@@ -61,7 +68,23 @@ module Nexpose
|
|
61
68
|
PBRUN = 'PBRUN'
|
62
69
|
end
|
63
70
|
|
71
|
+
#Authentication type for SNMP version 3
|
72
|
+
module AuthenticationType
|
73
|
+
NOAUTH = 'noauth' # No authentication protocol
|
74
|
+
SHA = 'sha' # SHA authentication protocol
|
75
|
+
MD5 = 'md5' # MD5 authentication protocol
|
76
|
+
end
|
64
77
|
|
78
|
+
# PrivacyType for snmp version 3
|
79
|
+
module PrivacyType
|
80
|
+
NOPRIV = 'nopriv' # No privacy protocol
|
81
|
+
DES = 'des' # DES privacy protocol
|
82
|
+
AES128 = 'aes128' # AES128 privacy protocol
|
83
|
+
AES192 = 'aes192' # AES192 privacy protocol
|
84
|
+
AES192WITH3DESKEYEXTENSION = 'aes192with3deskeyextension' # AES192 with 3 DES key extension privacy protocol
|
85
|
+
AES256 = 'aes256' # AES256 privacy protocol
|
86
|
+
AES265WITH3DESKEYEXTENSION = 'aes265with3deskeyextension' # AES256 with 3 DES key extension privacy protocol
|
87
|
+
end
|
65
88
|
# Test this credential against a target where the credentials should apply.
|
66
89
|
# Only works for a newly created credential. Loading an existing credential
|
67
90
|
# will likely fail.
|
@@ -102,6 +125,167 @@ module Nexpose
|
|
102
125
|
siteid: siteid }
|
103
126
|
end
|
104
127
|
|
128
|
+
# sets the Concurrent Versioning System (CVS) service
|
129
|
+
def set_cvs_service(username = nil, password = nil)
|
130
|
+
@user_name = username
|
131
|
+
@password = password
|
132
|
+
@service = Service::CVS
|
133
|
+
end
|
134
|
+
|
135
|
+
# sets the DB2 service
|
136
|
+
def set_db2_service(database = nil, username = nil, password = nil)
|
137
|
+
@database = database
|
138
|
+
@user_name = username
|
139
|
+
@password = password
|
140
|
+
@service = Service::DB2
|
141
|
+
end
|
142
|
+
|
143
|
+
# sets the File Transfer Protocol (FTP) service
|
144
|
+
def set_ftp_service(username = nil, password = nil)
|
145
|
+
@user_name = username
|
146
|
+
@password = password
|
147
|
+
@service = Service::FTP
|
148
|
+
end
|
149
|
+
|
150
|
+
# sets the IBM AS/400 service.
|
151
|
+
def set_as400_service(domain = nil, username = nil, password = nil)
|
152
|
+
@domain = domain
|
153
|
+
@user_name = username
|
154
|
+
@password = password
|
155
|
+
@service = Service::AS400
|
156
|
+
end
|
157
|
+
|
158
|
+
# sets the Lotus Notes/Domino service.
|
159
|
+
def set_notes_service(password = nil)
|
160
|
+
@notes_id_password = password
|
161
|
+
@service = Service::NOTES
|
162
|
+
end
|
163
|
+
|
164
|
+
# sets the Microsoft SQL Server service.
|
165
|
+
def set_tds_service(database = nil, domain = nil, username = nil, password = nil)
|
166
|
+
@database = database
|
167
|
+
@domain = domain
|
168
|
+
@use_windows_auth = domain.nil?
|
169
|
+
@user_name = username
|
170
|
+
@password = password
|
171
|
+
@service = Service::TDS
|
172
|
+
end
|
173
|
+
|
174
|
+
# sets the Microsoft Windows/Samba (SMB/CIFS) service.
|
175
|
+
def set_cifs_service(domain = nil, username = nil, password = nil)
|
176
|
+
@domain = domain
|
177
|
+
@user_name = username
|
178
|
+
@password = password
|
179
|
+
@service = Service::CIFS
|
180
|
+
end
|
181
|
+
|
182
|
+
# sets the Microsoft Windows/Samba LM/NTLM Hash (SMB/CIFS) service.
|
183
|
+
def set_cifshash_service(domain = nil, username = nil, password = nil)
|
184
|
+
@domain = domain
|
185
|
+
@user_name = username
|
186
|
+
@password = password
|
187
|
+
@service = Service::CIFSHASH
|
188
|
+
end
|
189
|
+
|
190
|
+
# sets the MySQL Server service.
|
191
|
+
def set_mysql_service(database = nil, username = nil, password = nil)
|
192
|
+
@database = database
|
193
|
+
@user_name = username
|
194
|
+
@password = password
|
195
|
+
@service = Service::MYSQL
|
196
|
+
end
|
197
|
+
|
198
|
+
# sets the Oracle service.
|
199
|
+
def set_oracle_service(sid = nil, username = nil, password = nil)
|
200
|
+
@database = sid
|
201
|
+
@user_name = username
|
202
|
+
@password = password
|
203
|
+
@service = Service::ORACLE
|
204
|
+
end
|
205
|
+
|
206
|
+
# sets the Post Office Protocol (POP) service.
|
207
|
+
def set_pop_service(username = nil, password = nil)
|
208
|
+
@user_name = username
|
209
|
+
@password = password
|
210
|
+
@service = Service::POP
|
211
|
+
end
|
212
|
+
|
213
|
+
# sets the PostgreSQL service.
|
214
|
+
def set_postgresql_service(database = nil, username = nil, password = nil)
|
215
|
+
@database = database
|
216
|
+
@user_name = username
|
217
|
+
@password = password
|
218
|
+
@service = Service::POSTGRESQL
|
219
|
+
end
|
220
|
+
|
221
|
+
# sets the Remote Execution service.
|
222
|
+
def set_remote_execution_service(username = nil, password = nil)
|
223
|
+
@user_name = username
|
224
|
+
@password = password
|
225
|
+
@service = Service::REMOTE_EXECUTION
|
226
|
+
end
|
227
|
+
|
228
|
+
# sets the Secure Shell (SSH) service.
|
229
|
+
def set_ssh_service(username = nil, password = nil, elevation_type = nil, elevation_user = nil, elevation_password = nil)
|
230
|
+
@user_name = username
|
231
|
+
@password = password
|
232
|
+
@permission_elevation_type = elevation_type || ElevationType::NONE
|
233
|
+
@permission_elevation_user = elevation_user
|
234
|
+
@permission_elevation_password = elevation_password
|
235
|
+
@service = Service::SSH
|
236
|
+
end
|
237
|
+
|
238
|
+
# sets the Secure Shell (SSH) Public Key service.
|
239
|
+
def set_ssh_key_service(username, pemkey, password = nil, elevation_type = nil, elevation_user = nil, elevation_password = nil)
|
240
|
+
@user_name = username
|
241
|
+
@password = password
|
242
|
+
@pem_format_private_key = pemkey
|
243
|
+
@permission_elevation_type = elevation_type || ElevationType::NONE
|
244
|
+
@permission_elevation_user = elevation_user
|
245
|
+
@permission_elevation_password = elevation_password
|
246
|
+
@service = Service::SSH_KEY
|
247
|
+
end
|
248
|
+
|
249
|
+
# sets the Simple Network Management Protocol v1/v2c service.
|
250
|
+
def set_snmp_service(community_name = nil)
|
251
|
+
@community_name = community_name
|
252
|
+
@service = Service::SNMP
|
253
|
+
end
|
254
|
+
|
255
|
+
# sets the Simple Network Management Protocol v3 service.
|
256
|
+
def set_snmpv3_service(authentication_type = AuthenticationType::NOAUTH, username = nil, password = nil, privacy_type = PrivacyType::NOPRIV, privacy_password = nil)
|
257
|
+
@authentication_type = authentication_type
|
258
|
+
@user_name = username
|
259
|
+
@password = password
|
260
|
+
@privacy_type = privacy_type
|
261
|
+
@privacy_password = privacy_password
|
262
|
+
@service = Service::SNMPV3
|
263
|
+
end
|
264
|
+
|
265
|
+
# sets the Sybase SQL Server service.
|
266
|
+
def set_sybase_service(database = nil, domain = nil, username = nil, password = nil)
|
267
|
+
@database = database
|
268
|
+
@domain = domain
|
269
|
+
@use_windows_auth = domain.nil?
|
270
|
+
@user_name = username
|
271
|
+
@password = password
|
272
|
+
@service = Service::SYBASE
|
273
|
+
end
|
274
|
+
|
275
|
+
# sets the Telnet service.
|
276
|
+
def set_telnet_service(username = nil, password = nil)
|
277
|
+
@user_name = username
|
278
|
+
@password = password
|
279
|
+
@service = Service::TELNET
|
280
|
+
end
|
281
|
+
|
282
|
+
# sets the Web Site HTTP Authentication service.
|
283
|
+
def set_http_service(domain = nil, username = nil, password = nil)
|
284
|
+
@domain = domain
|
285
|
+
@user_name = username
|
286
|
+
@password = password
|
287
|
+
@service = Service::HTTP
|
288
|
+
end
|
105
289
|
end
|
106
290
|
|
107
291
|
|