nexpose 0.8.9 → 0.8.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/nexpose/ajax.rb +31 -0
- data/lib/nexpose/device.rb +65 -1
- data/lib/nexpose/discovery.rb +38 -6
- data/lib/nexpose/discovery/filter.rb +9 -0
- data/lib/nexpose/scan.rb +44 -2
- data/lib/nexpose/site.rb +17 -9
- metadata +2 -3
- data/lib/nexpose/settings.xml +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 81928c1e3298ba80b864e0d14499f58a9a41eae2
|
4
|
+
data.tar.gz: dcbe6c5b9df95b254fa98f3eb5ec2041492d389a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aa9189178c6c2d497635cc871473f78277abe75374bcf32ac883303463b079b73bc5bdacdac0d8292fcfbd0d326f1d6754235fe2b3ab8d19dcbb260c651cb09e
|
7
|
+
data.tar.gz: 7fc4dc7d5a376c7a6e1a0f440e48b532cb73acb1c4470973528fb228be3d8ccbfb60d1d6acd55ce9c1ee00d3aed91dea79b691b484cfddd3bd0374bef0a5690e
|
data/lib/nexpose/ajax.rb
CHANGED
@@ -119,6 +119,15 @@ module Nexpose
|
|
119
119
|
uri
|
120
120
|
end
|
121
121
|
|
122
|
+
def preserving_preference(nsc, pref)
|
123
|
+
begin
|
124
|
+
orig = _get_rows(nsc, pref)
|
125
|
+
yield
|
126
|
+
ensure
|
127
|
+
_set_rows(nsc, pref, orig)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
122
131
|
###
|
123
132
|
# Internal helper methods
|
124
133
|
|
@@ -154,5 +163,27 @@ module Nexpose
|
|
154
163
|
raise Nexpose::APIError.new(response, "#{req_type} request to #{request.path} failed. #{request.body}")
|
155
164
|
end
|
156
165
|
end
|
166
|
+
|
167
|
+
def _get_rows(nsc, pref)
|
168
|
+
uri = '/ajax/user_pref_get.txml'
|
169
|
+
resp = get(nsc, uri, CONTENT_TYPE::XML, 'name' => "#{pref}.rows")
|
170
|
+
xml = REXML::Document.new(resp)
|
171
|
+
if val = REXML::XPath.first(xml, 'GetUserPref/userPref')
|
172
|
+
val.text.to_i
|
173
|
+
else
|
174
|
+
10
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def _set_rows(nsc, pref, value)
|
179
|
+
uri = '/ajax/user_pref_set.txml'
|
180
|
+
params = { 'name' => "#{pref}.rows",
|
181
|
+
'value' => value }
|
182
|
+
resp = get(nsc, uri, CONTENT_TYPE::XML, params)
|
183
|
+
xml = REXML::Document.new(resp)
|
184
|
+
if attr = REXML::XPath.first(xml, 'SetUserPref/@success')
|
185
|
+
attr.value == '1'
|
186
|
+
end
|
187
|
+
end
|
157
188
|
end
|
158
189
|
end
|
data/lib/nexpose/device.rb
CHANGED
@@ -93,6 +93,22 @@ module Nexpose
|
|
93
93
|
alias_method :asset_vulns, :list_device_vulns
|
94
94
|
alias_method :device_vulns, :list_device_vulns
|
95
95
|
|
96
|
+
# Retrieve a list of assets which completed in a given scan. If called
|
97
|
+
# during a scan, this method returns currently completed assets. A
|
98
|
+
# "completed" asset can be in one of three states: completed successfully,
|
99
|
+
# failed due to an error, or stopped by a user.
|
100
|
+
#
|
101
|
+
# @param [Fixnum] scan_id Unique identifier of a scan.
|
102
|
+
# @return [Array[CompletedAsset]] List of completed assets.
|
103
|
+
#
|
104
|
+
def completed_assets(scan_id)
|
105
|
+
uri = "/data/asset/scan/#{scan_id}/complete-assets"
|
106
|
+
AJAX.preserving_preference(self, 'scan-complete-assets') do
|
107
|
+
data = DataTable._get_json_table(self, uri, {}, 500, nil, false)
|
108
|
+
data.map(&CompletedAsset.method(:parse_json))
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
96
112
|
def delete_device(device_id)
|
97
113
|
r = execute(make_xml('DeviceDeleteRequest', { 'device-id' => device_id }))
|
98
114
|
r.success
|
@@ -104,7 +120,6 @@ module Nexpose
|
|
104
120
|
# Object that represents a single device in a Nexpose security console.
|
105
121
|
#
|
106
122
|
class Device
|
107
|
-
|
108
123
|
# A unique device ID (assigned automatically by the Nexpose console).
|
109
124
|
attr_reader :id
|
110
125
|
# IP Address or Hostname of this device.
|
@@ -124,4 +139,53 @@ module Nexpose
|
|
124
139
|
@risk_score = risk_score.to_f
|
125
140
|
end
|
126
141
|
end
|
142
|
+
|
143
|
+
# Summary object of a completed asset for a scan.
|
144
|
+
#
|
145
|
+
class CompletedAsset
|
146
|
+
# Unique identifier of an asset.
|
147
|
+
attr_reader :id
|
148
|
+
# IP address of the asset.
|
149
|
+
attr_reader :ip
|
150
|
+
# Host name of the asset, if discovered.
|
151
|
+
attr_reader :host_name
|
152
|
+
# Operating system fingerprint of the asset.
|
153
|
+
attr_reader :os
|
154
|
+
# Number of vulnerabilities discovered on the asset.
|
155
|
+
attr_reader :vulns
|
156
|
+
# Status of the asset on scan completion.
|
157
|
+
# One of :completed, :error, or :stopped.
|
158
|
+
attr_reader :status
|
159
|
+
# Time it took to scan the asset, in milliseconds.
|
160
|
+
attr_reader :duration
|
161
|
+
|
162
|
+
# Internal constructor to be called by #parse_json.
|
163
|
+
def initialize(&block)
|
164
|
+
instance_eval(&block) if block_given?
|
165
|
+
end
|
166
|
+
|
167
|
+
# Convenience method for assessing "ip" as "ip_address".
|
168
|
+
def ip_address
|
169
|
+
ip
|
170
|
+
end
|
171
|
+
|
172
|
+
# Convenience method for assessing "os" as "operating_system".
|
173
|
+
def operating_system
|
174
|
+
os
|
175
|
+
end
|
176
|
+
|
177
|
+
# Internal method for converting a JSON representation into a CompletedScan
|
178
|
+
# object.
|
179
|
+
def self.parse_json(json)
|
180
|
+
new do
|
181
|
+
@id = json['id'].to_i
|
182
|
+
@ip = json['ipAddress']
|
183
|
+
@host_name = json['hostName']
|
184
|
+
@os = json['operatingSystem']
|
185
|
+
@vulns = json['vulnerabilityCount']
|
186
|
+
@status = json['scanStatusTranslation'].downcase.to_sym
|
187
|
+
@duration = json['duration']
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
127
191
|
end
|
data/lib/nexpose/discovery.rb
CHANGED
@@ -33,6 +33,14 @@ module Nexpose
|
|
33
33
|
module Protocol
|
34
34
|
HTTP = 'HTTP'
|
35
35
|
HTTPS = 'HTTPS'
|
36
|
+
LDAP = 'LDAP'
|
37
|
+
LDAPS = 'LDAPS'
|
38
|
+
end
|
39
|
+
|
40
|
+
module Type
|
41
|
+
VSPHERE = 'VSPHERE'
|
42
|
+
AWS = 'AWS'
|
43
|
+
ACTIVESYNC = 'ACTIVESYNC'
|
36
44
|
end
|
37
45
|
|
38
46
|
# A unique identifier for this connection.
|
@@ -41,6 +49,9 @@ module Nexpose
|
|
41
49
|
# A unique name for this connection.
|
42
50
|
attr_accessor :name
|
43
51
|
|
52
|
+
# Type of discovery connection
|
53
|
+
attr_accessor :type
|
54
|
+
|
44
55
|
# The IP address or fully qualified domain name of the server.
|
45
56
|
attr_accessor :address
|
46
57
|
|
@@ -70,6 +81,7 @@ module Nexpose
|
|
70
81
|
#
|
71
82
|
def initialize(name, address, user, password = nil)
|
72
83
|
@name, @address, @user, @password = name, address, user, password
|
84
|
+
@type = nil # for backwards compatibilitly, at some point should set this to Type::VSPHERE
|
73
85
|
@id = -1
|
74
86
|
@port = 443
|
75
87
|
@protocol = Protocol::HTTPS
|
@@ -131,12 +143,13 @@ module Nexpose
|
|
131
143
|
|
132
144
|
def as_xml
|
133
145
|
xml = REXML::Element.new('DiscoveryConnection')
|
134
|
-
xml.
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
146
|
+
xml.attributes['name'] = @name
|
147
|
+
xml.attributes['address'] = @address
|
148
|
+
xml.attributes['port'] = @port
|
149
|
+
xml.attributes['protocol'] = @protocol
|
150
|
+
xml.attributes['user-name'] = @user
|
151
|
+
xml.attributes['password'] = @password
|
152
|
+
xml.attributes['type'] = @type if @type
|
140
153
|
xml
|
141
154
|
end
|
142
155
|
|
@@ -188,4 +201,23 @@ module Nexpose
|
|
188
201
|
end
|
189
202
|
end
|
190
203
|
end
|
204
|
+
|
205
|
+
class MobileDiscoveryConnection < DiscoveryConnection
|
206
|
+
# Create a new Mobile discovery connection.
|
207
|
+
#
|
208
|
+
# @param [String] name Name to assign to this connection.
|
209
|
+
# @param [DiscoveryConnection::Protocol] protocol The protocol to use for discovery - LDAPS or LDAP
|
210
|
+
# @param [String] address IP or fully qualified domain name of the
|
211
|
+
# connection server.
|
212
|
+
# @param [String] user User name for credentials on this connection.
|
213
|
+
# @param [String] password Password for credentials on this connection.
|
214
|
+
#
|
215
|
+
def initialize(name, protocol, address, user, password = nil)
|
216
|
+
@name, @protocol, @address, @user, @password = name, protocol, address, user, password
|
217
|
+
@type = Type::ACTIVESYNC
|
218
|
+
@id = -1
|
219
|
+
@port = 443 #port not used for mobile connection
|
220
|
+
end
|
221
|
+
|
222
|
+
end
|
191
223
|
end
|
@@ -2,6 +2,7 @@ module Nexpose
|
|
2
2
|
module Search
|
3
3
|
module Field
|
4
4
|
|
5
|
+
###### vSphere Filters ######
|
5
6
|
# Valid Operators: IS, IS_NOT, CONTAINS, NOT_CONTAINS, STARTS_WITH
|
6
7
|
CLUSTER = 'CLUSTER'
|
7
8
|
|
@@ -23,6 +24,14 @@ module Nexpose
|
|
23
24
|
|
24
25
|
# Valid Operators: IS, IS_NOT, CONTAINS, NOT_CONTAINS, STARTS_WITH
|
25
26
|
VIRTUAL_MACHINE_NAME = 'VM'
|
27
|
+
|
28
|
+
###### Mobile Filters ######
|
29
|
+
# Valid Operators: CONTAINS, NOT_CONTAINS
|
30
|
+
OPERATING_SYSTEM = 'DEVICE_OPERATING_SYSTEM'
|
31
|
+
|
32
|
+
# Valid Operators: IS, IS_NOT, CONTAINS, NOT_CONTAINS, STARTS_WITH
|
33
|
+
USER = 'DEVICE_USER_DISPLAY_NAME'
|
34
|
+
|
26
35
|
end
|
27
36
|
|
28
37
|
module Value
|
data/lib/nexpose/scan.rb
CHANGED
@@ -520,7 +520,6 @@ module Nexpose
|
|
520
520
|
# and vuln-potential.
|
521
521
|
#
|
522
522
|
class Status
|
523
|
-
|
524
523
|
attr_reader :severities, :count
|
525
524
|
|
526
525
|
def initialize(severity = nil, count = 0)
|
@@ -547,7 +546,6 @@ module Nexpose
|
|
547
546
|
# Struct class for tracking scan launch information.
|
548
547
|
#
|
549
548
|
class Scan
|
550
|
-
|
551
549
|
# The scan ID when a scan is successfully launched.
|
552
550
|
attr_reader :id
|
553
551
|
# The engine the scan was dispatched to.
|
@@ -578,4 +576,48 @@ module Nexpose
|
|
578
576
|
UNKNOWN = 'unknown'
|
579
577
|
end
|
580
578
|
end
|
579
|
+
|
580
|
+
# Summary object of a completed scan for a site.
|
581
|
+
#
|
582
|
+
class CompletedScan
|
583
|
+
# Unique identifier of a scan.
|
584
|
+
attr_reader :id
|
585
|
+
# Start time of the scan.
|
586
|
+
attr_reader :start_time
|
587
|
+
# Completion time of the scan.
|
588
|
+
attr_reader :end_time
|
589
|
+
# Elapsed time of the scan in milliseconds.
|
590
|
+
attr_reader :duration
|
591
|
+
# Number of vulnerabilities discovered in the scan.
|
592
|
+
attr_reader :vulns
|
593
|
+
# Number of live assets discovered in the scan.
|
594
|
+
attr_reader :assets
|
595
|
+
# Cumulative risk score for all assets in the scan.
|
596
|
+
attr_reader :risk_score
|
597
|
+
# Scan type. One of :scheduled or :manual
|
598
|
+
attr_reader :type
|
599
|
+
# Name of the engine where the scan was run. Not the unique ID.
|
600
|
+
attr_reader :engine_name
|
601
|
+
|
602
|
+
# Internal constructor to be called by #parse_json.
|
603
|
+
def initialize(&block)
|
604
|
+
instance_eval(&block) if block_given?
|
605
|
+
end
|
606
|
+
|
607
|
+
# Internal method for converting a JSON representation into a CompletedScan
|
608
|
+
# object.
|
609
|
+
def self.parse_json(json)
|
610
|
+
new do
|
611
|
+
@id = json['scanID']
|
612
|
+
@start_time = Time.at(json['startTime'] / 1000)
|
613
|
+
@end_time = Time.at(json['endTime'] / 1000)
|
614
|
+
@duration = json['duration']
|
615
|
+
@vulns = json['vulnerabilityCount']
|
616
|
+
@assets = json['liveHosts']
|
617
|
+
@risk_score = json['riskScore']
|
618
|
+
@type = json['startedByCD'] == 'S' ? :scheduled : :manual
|
619
|
+
@engine_name = json['scanEngineName']
|
620
|
+
end
|
621
|
+
end
|
622
|
+
end
|
581
623
|
end
|
data/lib/nexpose/site.rb
CHANGED
@@ -29,7 +29,7 @@ module Nexpose
|
|
29
29
|
# @return Whether or not the delete request succeeded.
|
30
30
|
#
|
31
31
|
def delete_site(site_id)
|
32
|
-
r = execute(make_xml('SiteDeleteRequest', {'site-id' => site_id}))
|
32
|
+
r = execute(make_xml('SiteDeleteRequest', { 'site-id' => site_id }))
|
33
33
|
r.success
|
34
34
|
end
|
35
35
|
|
@@ -40,7 +40,7 @@ module Nexpose
|
|
40
40
|
# each scan run to date on the site provided.
|
41
41
|
#
|
42
42
|
def site_scan_history(site_id)
|
43
|
-
r = execute(make_xml('SiteScanHistoryRequest', {'site-id' => site_id}))
|
43
|
+
r = execute(make_xml('SiteScanHistoryRequest', { 'site-id' => site_id }))
|
44
44
|
scans = []
|
45
45
|
if r.success
|
46
46
|
r.res.elements.each('SiteScanHistoryResponse/ScanSummary') do |scan_event|
|
@@ -59,7 +59,18 @@ module Nexpose
|
|
59
59
|
# @return [ScanSummary] details of the last completed scan for a site.
|
60
60
|
#
|
61
61
|
def last_scan(site_id)
|
62
|
-
site_scan_history(site_id).select
|
62
|
+
site_scan_history(site_id).select(&:end_time).max_by(&:end_time)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Retrieve a history of the completed scans for a given site.
|
66
|
+
#
|
67
|
+
# @param [FixNum] site_id Site ID to find scans for.
|
68
|
+
# @return [CompletedScan] details of the completed scans for the site.
|
69
|
+
#
|
70
|
+
def completed_scans(site_id)
|
71
|
+
table = { 'table-id' => 'site-completed-scans' }
|
72
|
+
data = DataTable._get_json_table(self, "/data/scan/site/#{site_id}", table)
|
73
|
+
data.map(&CompletedScan.method(:parse_json))
|
63
74
|
end
|
64
75
|
end
|
65
76
|
|
@@ -178,7 +189,7 @@ module Nexpose
|
|
178
189
|
#
|
179
190
|
# @param [String] hostname FQDN or DNS-resolvable host name of an asset.
|
180
191
|
def remove_host(hostname)
|
181
|
-
@assets = assets.reject { |asset| asset == HostName.new(hostname) }
|
192
|
+
@assets = assets.reject { |asset| asset == HostName.new(hostname) }
|
182
193
|
end
|
183
194
|
|
184
195
|
# Adds an asset to this site by IP address.
|
@@ -228,8 +239,8 @@ module Nexpose
|
|
228
239
|
#
|
229
240
|
def remove_asset(asset)
|
230
241
|
begin
|
231
|
-
# If the asset registers as a valid IP,
|
232
|
-
|
242
|
+
# If the asset registers as a valid IP, remove as IP.
|
243
|
+
IPAddr.new(asset)
|
233
244
|
remove_ip(asset)
|
234
245
|
rescue ArgumentError => e
|
235
246
|
if e.message == 'invalid address'
|
@@ -531,7 +542,6 @@ module Nexpose
|
|
531
542
|
# Object that represents the summary of a Nexpose Site.
|
532
543
|
#
|
533
544
|
class SiteSummary
|
534
|
-
|
535
545
|
# The Site ID.
|
536
546
|
attr_reader :id
|
537
547
|
# The Site Name.
|
@@ -557,7 +567,6 @@ module Nexpose
|
|
557
567
|
# Object that represents a hostname to be added to a site.
|
558
568
|
#
|
559
569
|
class HostName
|
560
|
-
|
561
570
|
# Named host (usually DNS or Netbios name).
|
562
571
|
attr_accessor :host
|
563
572
|
|
@@ -595,7 +604,6 @@ module Nexpose
|
|
595
604
|
# If to is nil then the from field will be used to specify a single IP Address only.
|
596
605
|
#
|
597
606
|
class IPRange
|
598
|
-
|
599
607
|
# Start of range *Required
|
600
608
|
attr_accessor :from
|
601
609
|
# End of range *Optional (If nil then IPRange is a single IP Address)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nexpose
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- HD Moore
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2014-10-
|
14
|
+
date: 2014-10-29 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: rex
|
@@ -96,7 +96,6 @@ files:
|
|
96
96
|
- lib/nexpose/role.rb
|
97
97
|
- lib/nexpose/scan.rb
|
98
98
|
- lib/nexpose/scan_template.rb
|
99
|
-
- lib/nexpose/settings.xml
|
100
99
|
- lib/nexpose/shared_cred.rb
|
101
100
|
- lib/nexpose/silo.rb
|
102
101
|
- lib/nexpose/silo_profile.rb
|
data/lib/nexpose/settings.xml
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
<GlobalSettings>
|
2
|
-
<riskModel id="real_risk" recalculation_duration="do_not_recalculate"></riskModel>
|
3
|
-
<RiskConfig includeRiskModifiers="false">
|
4
|
-
<RiskModifiers veryHigh="2" high="1.5" medium="1" low="0.75" veryLow="0.5"></RiskModifiers>
|
5
|
-
</RiskConfig>
|
6
|
-
<ControlsScan>
|
7
|
-
<enableControlsScan enabled="0"></enableControlsScan>
|
8
|
-
</ControlsScan>
|
9
|
-
<ExcludedHosts>
|
10
|
-
<range from="10.4.18.219"></range>
|
11
|
-
<range from="10.4.25.117"></range>
|
12
|
-
</ExcludedHosts>
|
13
|
-
</GlobalSettings>
|
14
|
-
|