nexpose 0.8.17 → 0.8.18
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/lib/nexpose.rb +2 -0
- data/lib/nexpose/api.rb +77 -0
- data/lib/nexpose/device.rb +2 -2
- data/lib/nexpose/filter.rb +5 -5
- data/lib/nexpose/util.rb +1 -1
- data/lib/nexpose/version.rb +1 -1
- data/lib/nexpose/vuln.rb +113 -128
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 24b07c5aa9671577da0a19a249babd2c0fce5ff6
|
4
|
+
data.tar.gz: 30f364953ab62ff807f82deffb848b2ceef80636
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fca1a54493ded6d695c2668c53c3ced413c7c82b181367bf3af666cf9cab10ad7209c1a7e6a67f059e6ba7b03f03ba5a783d9aa7ed0706754e7413e847e89d20
|
7
|
+
data.tar.gz: 9f30a2887a5b7f7064b1bf8c5dd7482e7762cb0b7b25e5d91a22dadab2e6577590bc374f76323372394bda48db48526a4663058ae7a356a8148bcec10f1e6764
|
data/lib/nexpose.rb
CHANGED
@@ -48,6 +48,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
48
48
|
#
|
49
49
|
|
50
50
|
require 'date'
|
51
|
+
require 'time'
|
51
52
|
require 'rexml/document'
|
52
53
|
require 'nokogiri'
|
53
54
|
require 'net/https'
|
@@ -61,6 +62,7 @@ require 'nexpose/error'
|
|
61
62
|
require 'nexpose/util'
|
62
63
|
require 'nexpose/alert'
|
63
64
|
require 'nexpose/ajax'
|
65
|
+
require 'nexpose/api'
|
64
66
|
require 'nexpose/api_request'
|
65
67
|
require 'nexpose/common'
|
66
68
|
require 'nexpose/console'
|
data/lib/nexpose/api.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
module Nexpose
|
2
|
+
# Base class for all API 2.0 objects which are derived from JSON
|
3
|
+
# representations.
|
4
|
+
#
|
5
|
+
# This class is not intended to be used by customers, but to extend
|
6
|
+
# functionality in the gem itself.
|
7
|
+
#
|
8
|
+
# To use this class, do the following:
|
9
|
+
# * Subclass APIObject
|
10
|
+
# * Do NOT provide a constructor method, or it must take no arguments.
|
11
|
+
# * Clearly document all attributes which the customer can expect to see.
|
12
|
+
# * Clearly document those attributes which are lazily loaded.
|
13
|
+
# * If applicable, implement a load method which calls new.object_from_hash
|
14
|
+
#
|
15
|
+
class APIObject
|
16
|
+
# Populate object methods and attributes from a JSON-derived hash.
|
17
|
+
#
|
18
|
+
# @param [Nexpose::Connection] nsc Active connection to a console.
|
19
|
+
# @param [Hash] hash Result of running JSON#parse with the
|
20
|
+
# symbolize_names parameter to a 2.0 API response.
|
21
|
+
# Pass hash[:resources] if the response is pageable.
|
22
|
+
#
|
23
|
+
def object_from_hash(nsc, hash)
|
24
|
+
hash.each do |k, v|
|
25
|
+
next if k == :url # Do not store self-referential URL.
|
26
|
+
# Store resource URLs separately and create lazy accessors.
|
27
|
+
if v.is_a?(Hash) && v.key?(:url)
|
28
|
+
instance_variable_set("@#{k}_url", v[:url].gsub(/.*\/api/, '/api'))
|
29
|
+
self.class.send(:define_method, k, proc { |conn = nsc| load_resource(conn, k, instance_variable_get("@#{k}_url")) })
|
30
|
+
else
|
31
|
+
# Convert timestamps.
|
32
|
+
if v.is_a?(String) && v.match(/^\d{8}T\d{6}\.\d{3}/)
|
33
|
+
instance_variable_set("@#{k}", ISO8601.to_time(v))
|
34
|
+
else
|
35
|
+
instance_variable_set("@#{k}", v)
|
36
|
+
end
|
37
|
+
self.class.send(:define_method, k, proc { instance_variable_get("@#{k}") })
|
38
|
+
end
|
39
|
+
end
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# Load a resource from the security console. Once loaded, the value is
|
46
|
+
# cached so that it need not be loaded again.
|
47
|
+
#
|
48
|
+
# @param [Connection] nsc Active connection to the console.
|
49
|
+
# @param [Symbol] k Original key name, used to identify the class to load.
|
50
|
+
# @param [String] url Truncated URL to use to retrieve the resource.
|
51
|
+
# @return [Array[?]] Collection of "k" marshalled object.
|
52
|
+
#
|
53
|
+
def load_resource(nsc, k, url)
|
54
|
+
obj = class_from_string(k)
|
55
|
+
resp = AJAX.get(nsc, url, AJAX::CONTENT_TYPE::JSON)
|
56
|
+
hash = JSON.parse(resp, symbolize_names: true)
|
57
|
+
resources = hash[:resources].map { |e| obj.method(:new).call.object_from_hash(nsc, e) }
|
58
|
+
instance_variable_set("@#{k}", resources)
|
59
|
+
self.class.send(:define_method, k, proc { instance_variable_get("@#{k}") })
|
60
|
+
resources
|
61
|
+
end
|
62
|
+
|
63
|
+
# Get the class referred to by a field name.
|
64
|
+
#
|
65
|
+
# For example, this method will translate a field name like "malware_kits"
|
66
|
+
# into to corresponding MalwareKit class.
|
67
|
+
#
|
68
|
+
# @param [String] field Snake-case name of a field.
|
69
|
+
# @return [Class] Class associated with the provided field.
|
70
|
+
#
|
71
|
+
def class_from_string(field)
|
72
|
+
str = field.to_s.split('_').map(&:capitalize!).join
|
73
|
+
str.chop! if str.end_with?('s')
|
74
|
+
Object.const_get('Nexpose').const_get(str)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/nexpose/device.rb
CHANGED
@@ -65,14 +65,14 @@ module Nexpose
|
|
65
65
|
# Get a list of all assets currently associated with a group.
|
66
66
|
#
|
67
67
|
# @param [Fixnum] dev_id Unique identifier of a device (asset).
|
68
|
-
# @return [Array[
|
68
|
+
# @return [Array[FilteredAsset]] List of group assets.
|
69
69
|
#
|
70
70
|
def group_assets(group_id)
|
71
71
|
payload = { 'sort' => 'assetName',
|
72
72
|
'table-id' => 'group-assets',
|
73
73
|
'groupID' => group_id }
|
74
74
|
results = DataTable._get_json_table(self, '/data/asset/group', payload)
|
75
|
-
results.map { |a|
|
75
|
+
results.map { |a| FilteredAsset.new(a) }
|
76
76
|
end
|
77
77
|
|
78
78
|
# List the vulnerability findings for a given device ID.
|
data/lib/nexpose/filter.rb
CHANGED
@@ -11,7 +11,7 @@ module Nexpose
|
|
11
11
|
# @param [String] field Constant from Search::Field
|
12
12
|
# @param [String] operator Constant from Search::Operator
|
13
13
|
# @param [String] value Search term or constant from Search::Value
|
14
|
-
# @return [Array[
|
14
|
+
# @return [Array[FilteredAsset]] List of matching assets.
|
15
15
|
#
|
16
16
|
def filter(field, operator, value = '')
|
17
17
|
criterion = Criterion.new(field, operator, value)
|
@@ -32,17 +32,17 @@ module Nexpose
|
|
32
32
|
# results = nsc.search(criteria)
|
33
33
|
#
|
34
34
|
# @param [Criteria] criteria Criteria search object.
|
35
|
-
# @return [Array[
|
35
|
+
# @return [Array[FilteredAsset]] List of matching assets.
|
36
36
|
#
|
37
37
|
def search(criteria)
|
38
38
|
results = DataTable._get_json_table(self,
|
39
39
|
'/data/asset/filterAssets',
|
40
40
|
criteria._to_payload)
|
41
|
-
results.map { |a|
|
41
|
+
results.map { |a| FilteredAsset.new(a) }
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
-
#
|
45
|
+
# Constants for performing Asset Filter searches and generating Dynamic Asset
|
46
46
|
# Groups.
|
47
47
|
#
|
48
48
|
module Search
|
@@ -349,7 +349,7 @@ module Nexpose
|
|
349
349
|
|
350
350
|
# Asset data as returned by an Asset Filter search.
|
351
351
|
#
|
352
|
-
class
|
352
|
+
class FilteredAsset
|
353
353
|
|
354
354
|
# Unique identifier of this asset. Also known as device ID.
|
355
355
|
attr_reader :id
|
data/lib/nexpose/util.rb
CHANGED
@@ -104,7 +104,7 @@ module Nexpose
|
|
104
104
|
# @return [Time] Time, if it can be converted.
|
105
105
|
#
|
106
106
|
def to_time(time_string)
|
107
|
-
Time.strptime(time_string.to_s, '%Y%m%dT%H%M%S.%
|
107
|
+
Time.strptime(time_string.to_s, '%Y%m%dT%H%M%S.%L%Z')
|
108
108
|
end
|
109
109
|
|
110
110
|
# Convert a time object into a UTC ISO 8601 basic date-time format.
|
data/lib/nexpose/version.rb
CHANGED
data/lib/nexpose/vuln.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
module Nexpose
|
2
|
-
|
3
2
|
class Connection
|
4
3
|
include XMLUtils
|
5
4
|
|
@@ -18,9 +17,9 @@ module Nexpose
|
|
18
17
|
if response.success
|
19
18
|
response.res.elements.each('VulnerabilityListingResponse/VulnerabilitySummary') do |vuln|
|
20
19
|
if full
|
21
|
-
vulns << VulnerabilitySummary.parse(vuln)
|
20
|
+
vulns << XML::VulnerabilitySummary.parse(vuln)
|
22
21
|
else
|
23
|
-
vulns << Vulnerability.new(vuln.attributes['id'],
|
22
|
+
vulns << XML::Vulnerability.new(vuln.attributes['id'],
|
24
23
|
vuln.attributes['title'],
|
25
24
|
vuln.attributes['severity'].to_i)
|
26
25
|
end
|
@@ -63,7 +62,7 @@ module Nexpose
|
|
63
62
|
response = execute(xml, '1.2')
|
64
63
|
if response.success
|
65
64
|
response.res.elements.each('VulnerabilityDetailsResponse/Vulnerability') do |vuln|
|
66
|
-
return VulnerabilityDetail.parse(vuln)
|
65
|
+
return XML::VulnerabilityDetail.parse(vuln)
|
67
66
|
end
|
68
67
|
end
|
69
68
|
end
|
@@ -79,7 +78,7 @@ module Nexpose
|
|
79
78
|
uri = "/data/vulnerability/vulnerabilities/dyntable.xml?tableID=VulnCheckSynopsis&phrase=#{URI.encode(search_term)}&allWords=#{all_words}"
|
80
79
|
data = DataTable._get_dyn_table(self, uri)
|
81
80
|
data.map do |vuln|
|
82
|
-
VulnCheck.new(vuln)
|
81
|
+
XML::VulnCheck.new(vuln)
|
83
82
|
end
|
84
83
|
end
|
85
84
|
|
@@ -99,142 +98,130 @@ module Nexpose
|
|
99
98
|
end
|
100
99
|
end
|
101
100
|
|
102
|
-
#
|
101
|
+
# Object definitions which are derived from XML values.
|
103
102
|
#
|
104
|
-
|
105
|
-
|
106
|
-
#
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
103
|
+
module XML
|
104
|
+
# Basic vulnerability information. Only includes ID, title, and severity.
|
105
|
+
#
|
106
|
+
class Vulnerability
|
107
|
+
# The unique ID string for this vulnerability
|
108
|
+
attr_reader :id
|
109
|
+
# The title of this vulnerability
|
110
|
+
attr_reader :title
|
111
|
+
# How critical the vulnerability is on a scale of 1 to 10.
|
112
|
+
attr_reader :severity
|
113
|
+
|
114
|
+
def initialize(id, title, severity)
|
115
|
+
@id, @title, @severity = id, title, severity.to_i
|
116
|
+
end
|
117
117
|
end
|
118
|
-
end
|
119
|
-
|
120
|
-
# Vulnerability Check information.
|
121
|
-
#
|
122
|
-
class VulnCheck < Vulnerability
|
123
118
|
|
124
|
-
|
125
|
-
#
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
119
|
+
# Vulnerability Check information.
|
120
|
+
#
|
121
|
+
class VulnCheck < Vulnerability
|
122
|
+
attr_reader :check_id
|
123
|
+
# @return [Array[String]] Categories that this check is a member of.
|
124
|
+
# Note that this is note the same as the categories from #list_vuln_categories.
|
125
|
+
attr_reader :categories
|
126
|
+
# @return [String] Check type. @see #list_vuln_types
|
127
|
+
attr_reader :check_type
|
128
|
+
|
129
|
+
def initialize(json)
|
130
|
+
@id = json['Vuln ID']
|
131
|
+
@check_id = json['Vuln Check ID']
|
132
|
+
@title = json['Vulnerability']
|
133
|
+
@severity = json['Severity'].to_i
|
134
|
+
@check_type = json['Check Type']
|
135
|
+
@categories = json['Category'].split(/, */)
|
136
|
+
end
|
138
137
|
end
|
139
|
-
end
|
140
|
-
|
141
|
-
# Summary of a vulnerability.
|
142
|
-
#
|
143
|
-
class VulnerabilitySummary < Vulnerability
|
144
|
-
|
145
|
-
# PCI severity value for the vulnerability on a scale of 1 to 5.
|
146
|
-
attr_accessor :pci_severity
|
147
|
-
|
148
|
-
# Whether all checks for the vulnerability are safe.
|
149
|
-
# Unsafe checks may cause denial of service or otherwise disrupt system performance.
|
150
|
-
attr_accessor :safe
|
151
|
-
|
152
|
-
# A vulnerability is considered "credentialed" when all of its checks
|
153
|
-
# require credentials or if the check depends on previous authentication
|
154
|
-
# during a scan.
|
155
|
-
attr_accessor :credentials
|
156
|
-
|
157
|
-
# When this vulnerability was first included in the application.
|
158
|
-
attr_accessor :added
|
159
|
-
|
160
|
-
# The last date the vulnerability was modified.
|
161
|
-
attr_accessor :modified
|
162
|
-
|
163
|
-
# The date when the information about the vulnerability was first released.
|
164
|
-
attr_accessor :published
|
165
138
|
|
166
|
-
#
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
#
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
139
|
+
# Summary of a vulnerability.
|
140
|
+
#
|
141
|
+
class VulnerabilitySummary < Vulnerability
|
142
|
+
# PCI severity value for the vulnerability on a scale of 1 to 5.
|
143
|
+
attr_accessor :pci_severity
|
144
|
+
# Whether all checks for the vulnerability are safe.
|
145
|
+
# Unsafe checks may cause denial of service or otherwise disrupt system performance.
|
146
|
+
attr_accessor :safe
|
147
|
+
# A vulnerability is considered "credentialed" when all of its checks
|
148
|
+
# require credentials or if the check depends on previous authentication
|
149
|
+
# during a scan.
|
150
|
+
attr_accessor :credentials
|
151
|
+
# When this vulnerability was first included in the application.
|
152
|
+
attr_accessor :added
|
153
|
+
# The last date the vulnerability was modified.
|
154
|
+
attr_accessor :modified
|
155
|
+
# The date when the information about the vulnerability was first released.
|
156
|
+
attr_accessor :published
|
157
|
+
# How the vulnerability is exploited according to PCI standards.
|
158
|
+
attr_accessor :cvss_vector
|
159
|
+
# The computation of the Common Vulnerability Scoring System indicating
|
160
|
+
# compliance with PCI standards on a scale from 0 to 10.0.
|
161
|
+
attr_accessor :cvss_score
|
162
|
+
|
163
|
+
def self.parse_attributes(xml)
|
164
|
+
vuln = new(xml.attributes['id'],
|
165
|
+
xml.attributes['title'],
|
166
|
+
xml.attributes['severity'].to_i)
|
167
|
+
|
168
|
+
vuln.pci_severity = xml.attributes['pciSeverity'].to_i
|
169
|
+
vuln.safe = xml.attributes['safe'] == 'true' # or xml.attributes['safe'] == '1'
|
170
|
+
vuln.added = Date.parse(xml.attributes['added'])
|
171
|
+
vuln.modified = Date.parse(xml.attributes['modified'])
|
172
|
+
vuln.credentials = xml.attributes['requiresCredentials'] == 'true'
|
173
|
+
|
174
|
+
# These three fields are optional in the XSD.
|
175
|
+
vuln.published = Date.parse(xml.attributes['published']) if xml.attributes['published']
|
176
|
+
vuln.cvss_vector = xml.attributes['cvssVector'] if xml.attributes['cvssVector']
|
177
|
+
vuln.cvss_score = xml.attributes['cvssScore'].to_f if xml.attributes['cvssScore']
|
178
|
+
vuln
|
179
|
+
end
|
190
180
|
|
191
|
-
|
192
|
-
|
181
|
+
def self.parse(xml)
|
182
|
+
parse_attributes(xml)
|
183
|
+
end
|
193
184
|
end
|
194
|
-
end
|
195
|
-
|
196
|
-
# Details for a vulnerability.
|
197
|
-
#
|
198
|
-
class VulnerabilityDetail < VulnerabilitySummary
|
199
|
-
|
200
|
-
# The HTML Description of this vulnerability.
|
201
|
-
attr_accessor :description
|
202
|
-
|
203
|
-
# External References for this vulnerability.
|
204
|
-
# Array containing (Reference)
|
205
|
-
attr_accessor :references
|
206
185
|
|
207
|
-
#
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
186
|
+
# Details for a vulnerability.
|
187
|
+
#
|
188
|
+
class VulnerabilityDetail < VulnerabilitySummary
|
189
|
+
# The HTML Description of this vulnerability.
|
190
|
+
attr_accessor :description
|
191
|
+
# External References for this vulnerability.
|
192
|
+
# Array containing (Reference)
|
193
|
+
attr_accessor :references
|
194
|
+
# The HTML Solution for this vulnerability.
|
195
|
+
attr_accessor :solution
|
196
|
+
|
197
|
+
def initialize(id, title, severity)
|
198
|
+
@id, @title, @severity = id, title, severity
|
199
|
+
@references = []
|
200
|
+
end
|
214
201
|
|
215
|
-
|
216
|
-
|
202
|
+
def self.parse(xml)
|
203
|
+
vuln = parse_attributes(xml)
|
217
204
|
|
218
|
-
|
219
|
-
|
205
|
+
vuln.description = REXML::XPath.first(xml, 'description').text
|
206
|
+
vuln.solution = REXML::XPath.first(xml, 'solution').text
|
220
207
|
|
221
|
-
|
222
|
-
|
208
|
+
xml.elements.each('references/reference') do |ref|
|
209
|
+
vuln.references << Reference.new(ref.attributes['source'], ref.text)
|
210
|
+
end
|
211
|
+
vuln
|
223
212
|
end
|
224
|
-
vuln
|
225
213
|
end
|
226
|
-
end
|
227
214
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
attr_reader :reference
|
215
|
+
# Reference information for a Vulnerability.
|
216
|
+
#
|
217
|
+
class Reference
|
218
|
+
attr_reader :source
|
219
|
+
attr_reader :reference
|
234
220
|
|
235
|
-
|
236
|
-
|
237
|
-
|
221
|
+
def initialize(source, reference)
|
222
|
+
@source = source
|
223
|
+
@reference = reference
|
224
|
+
end
|
238
225
|
end
|
239
226
|
end
|
240
227
|
|
@@ -243,7 +230,6 @@ module Nexpose
|
|
243
230
|
# cross-referenced to the String ID to be used elsewhere.
|
244
231
|
#
|
245
232
|
class VulnFinding
|
246
|
-
|
247
233
|
# Unique identifier of the vulnerability.
|
248
234
|
attr_reader :id
|
249
235
|
# Unique, console-specific identifier of the vulnerability.
|
@@ -286,7 +272,6 @@ module Nexpose
|
|
286
272
|
# cross-referenced to the String ID to be used elsewhere.
|
287
273
|
#
|
288
274
|
class VulnSynopsis < VulnFinding
|
289
|
-
|
290
275
|
def initialize(hash)
|
291
276
|
@id = hash['Vuln ID'].to_i
|
292
277
|
@title = hash['Vulnerability']
|
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.18
|
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-12-
|
14
|
+
date: 2014-12-15 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: rex
|
@@ -87,6 +87,7 @@ files:
|
|
87
87
|
- lib/nexpose.rb
|
88
88
|
- lib/nexpose/ajax.rb
|
89
89
|
- lib/nexpose/alert.rb
|
90
|
+
- lib/nexpose/api.rb
|
90
91
|
- lib/nexpose/api_request.rb
|
91
92
|
- lib/nexpose/common.rb
|
92
93
|
- lib/nexpose/connection.rb
|