nexpose 2.3.0 → 3.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/lib/nexpose/ajax.rb +4 -2
- data/lib/nexpose/device.rb +61 -0
- data/lib/nexpose/report.rb +1 -1
- data/lib/nexpose/rexlite/README.md +5 -0
- data/lib/nexpose/rexlite/mime/encoding.rb +17 -0
- data/lib/nexpose/rexlite/mime/header.rb +76 -0
- data/lib/nexpose/rexlite/mime/message.rb +161 -0
- data/lib/nexpose/rexlite/mime/part.rb +50 -0
- data/lib/nexpose/rexlite/mime.rb +10 -0
- data/lib/nexpose/scan.rb +77 -6
- data/lib/nexpose/scan_template.rb +0 -18
- data/lib/nexpose/site.rb +0 -30
- data/lib/nexpose/version.rb +1 -1
- data/lib/nexpose.rb +1 -9
- data/nexpose.gemspec +0 -2
- metadata +8 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 655abc9506b7d35e2b0d09097a40fe2cd1be8957
|
4
|
+
data.tar.gz: 47dd93d3c38ab7d6834ec2f905ce41bf8644ddb4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da349fdd82a17c6df04d4321b3ace9d10dab2fc49d3d2dfe614de2dbf4caa22f40e50f8a7102a199d81acff5a4e6409fae7fea59d1fdf0883567c75127e5e9d9
|
7
|
+
data.tar.gz: b879628ea4cc24feb49f35d5ed99d42c4f3e515dbd2eae6cedb21024bd2723de4236288db913c8daeb486f0d21c74a7b77527237e96c93ae13a595979426e528
|
data/lib/nexpose/ajax.rb
CHANGED
@@ -187,8 +187,10 @@ module Nexpose
|
|
187
187
|
# is 2.1 or greater otherwise use the request body
|
188
188
|
def get_error_message(request, response)
|
189
189
|
version = get_request_api_version(request)
|
190
|
-
|
191
|
-
|
190
|
+
data_request = request.path.include? '/data/'
|
191
|
+
data_request ? response_is_text = (response.content_type.include? 'text/plain') : nil
|
192
|
+
return_response = (version >= 2.1 || (data_request && response_is_text))
|
193
|
+
(return_response && response.body) ? "response body: #{response.body}" : "request body: #{request.body}"
|
192
194
|
end
|
193
195
|
|
194
196
|
# Execute a block of code while presenving the preferences for any
|
data/lib/nexpose/device.rb
CHANGED
@@ -130,6 +130,21 @@ module Nexpose
|
|
130
130
|
end
|
131
131
|
|
132
132
|
alias_method :delete_asset, :delete_device
|
133
|
+
|
134
|
+
# Retrieve the scan history for an asset.
|
135
|
+
# Note: This is not optimized for querying many assets.
|
136
|
+
#
|
137
|
+
# @param [Fixnum] asset_id Unique identifer of an asset.
|
138
|
+
# @return [Array[AssetScan]] A list of scans for the asset.
|
139
|
+
#
|
140
|
+
def asset_scan_history(asset_id)
|
141
|
+
uri = "/data/assets/#{asset_id}/scans"
|
142
|
+
AJAX.preserving_preference(self, 'asset-scan-history') do
|
143
|
+
data = DataTable._get_json_table(self, uri, {}, 500, nil, true)
|
144
|
+
data.each { |a| a['assetID'] = asset_id.to_s }
|
145
|
+
data.map(&AssetScan.method(:parse_json))
|
146
|
+
end
|
147
|
+
end
|
133
148
|
end
|
134
149
|
|
135
150
|
# Object that represents a single device in a Nexpose security console.
|
@@ -208,4 +223,50 @@ module Nexpose
|
|
208
223
|
#
|
209
224
|
class IncompleteAsset < CompletedAsset
|
210
225
|
end
|
226
|
+
|
227
|
+
|
228
|
+
# Summary object of a scan for a particular asset.
|
229
|
+
#
|
230
|
+
class AssetScan
|
231
|
+
# Unique identifier of an asset.
|
232
|
+
attr_reader :asset_id
|
233
|
+
# IP address of the asset.
|
234
|
+
attr_reader :ip
|
235
|
+
# Host name of the asset, if discovered.
|
236
|
+
attr_reader :host_name
|
237
|
+
# Site name where the scan originated.
|
238
|
+
attr_reader :site_name
|
239
|
+
# Unique identifier for the site where the scan originated.
|
240
|
+
attr_reader :site_id
|
241
|
+
# Unique identifier for the scan.
|
242
|
+
attr_reader :scan_id
|
243
|
+
# Time when the asset finished scanning.
|
244
|
+
attr_reader :end_time
|
245
|
+
# Number of vulnerabilities discovered on the asset.
|
246
|
+
attr_reader :vulns
|
247
|
+
# Operating system fingerprint of the asset.
|
248
|
+
attr_reader :os
|
249
|
+
# Name of the scan engine used for the scan.
|
250
|
+
attr_reader :engine_name
|
251
|
+
|
252
|
+
# Internal constructor to be called by #parse_json.
|
253
|
+
def initialize(&block)
|
254
|
+
instance_eval(&block) if block_given?
|
255
|
+
end
|
256
|
+
|
257
|
+
def self.parse_json(json)
|
258
|
+
new do
|
259
|
+
@asset_id = json['assetID'].to_i
|
260
|
+
@scan_id = json['scanID'].to_i
|
261
|
+
@site_id = json['siteID'].to_i
|
262
|
+
@ip = json['ipAddress']
|
263
|
+
@host_name = json['hostname']
|
264
|
+
@os = json['operatingSystem']
|
265
|
+
@vulns = json['vulnCount']
|
266
|
+
@end_time = Time.at(json['completed'].to_i / 1000)
|
267
|
+
@site_name = json['siteName']
|
268
|
+
@engine_name = json['scanEngineName']
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
211
272
|
end
|
data/lib/nexpose/report.rb
CHANGED
@@ -249,7 +249,7 @@ module Nexpose
|
|
249
249
|
content_type_response = content_type_response[0, last_semi_colon_index]
|
250
250
|
|
251
251
|
data = 'Content-Type: ' + content_type_response + "\r\n\r\n" + response.raw_response_data
|
252
|
-
doc =
|
252
|
+
doc = Rexlite::MIME::Message.new(data)
|
253
253
|
doc.parts.each do |part|
|
254
254
|
if /.*base64.*/ =~ part.header.to_s
|
255
255
|
if @format =~ /(?:ht|x)ml/
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
module Rexlite
|
3
|
+
module MIME
|
4
|
+
# Set of helpers methods to deal with SMTP encoding related topics.
|
5
|
+
module Encoding
|
6
|
+
|
7
|
+
# Enforces CRLF on the input data
|
8
|
+
#
|
9
|
+
# @param data [String] The data to CRLF enforce.
|
10
|
+
# @return [String] CRLF enforced data.
|
11
|
+
def force_crlf(data)
|
12
|
+
data.gsub("\r", '').gsub("\n", "\r\n")
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
module Rexlite
|
3
|
+
module MIME
|
4
|
+
class Header
|
5
|
+
|
6
|
+
attr_accessor :headers
|
7
|
+
|
8
|
+
def initialize(data='')
|
9
|
+
self.headers = []
|
10
|
+
parse(data)
|
11
|
+
end
|
12
|
+
|
13
|
+
def parse(data)
|
14
|
+
prev = nil
|
15
|
+
data.gsub("\r", '').split("\n").each do |line|
|
16
|
+
|
17
|
+
# Handle header folding
|
18
|
+
if (line =~ /^\s+/)
|
19
|
+
# Ignore if there is no previous header
|
20
|
+
next if not prev
|
21
|
+
next if not self.headers[prev]
|
22
|
+
self.headers[prev][1] << line.strip
|
23
|
+
next
|
24
|
+
end
|
25
|
+
|
26
|
+
var, val = line.split(':', 2)
|
27
|
+
next if val.nil?
|
28
|
+
|
29
|
+
self.headers << [ var.to_s.strip, val.to_s.strip ]
|
30
|
+
prev = self.headers.length - 1
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
self.headers.map{ |pair| "#{pair[0]}: #{pair[1]}\r\n" }.join
|
36
|
+
end
|
37
|
+
|
38
|
+
def find(idx)
|
39
|
+
if (idx.class == ::Fixnum)
|
40
|
+
return self.headers[idx]
|
41
|
+
else
|
42
|
+
self.headers.each do |pair|
|
43
|
+
if (pair[0] == idx.to_s)
|
44
|
+
return pair
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
|
51
|
+
def set(var, val)
|
52
|
+
hdr = self.find(var) || self.add(var, '')
|
53
|
+
hdr[1] = val
|
54
|
+
end
|
55
|
+
|
56
|
+
def add(var, val)
|
57
|
+
self.headers << [var, val]
|
58
|
+
self.headers[-1]
|
59
|
+
end
|
60
|
+
|
61
|
+
def remove(idx)
|
62
|
+
if (idx.class == ::Fixnum)
|
63
|
+
self.headers.delete_at(idx)
|
64
|
+
else
|
65
|
+
self.headers.each_index do |i|
|
66
|
+
pair = self.headers[i]
|
67
|
+
if (pair[0] == idx.to_s)
|
68
|
+
self.headers.delete_at(i)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
module Rexlite
|
3
|
+
module MIME
|
4
|
+
class Message
|
5
|
+
|
6
|
+
require 'nexpose/rexlite/mime/header'
|
7
|
+
require 'nexpose/rexlite/mime/part'
|
8
|
+
require 'nexpose/rexlite/mime/encoding'
|
9
|
+
|
10
|
+
include Rexlite::MIME::Encoding
|
11
|
+
|
12
|
+
attr_accessor :header, :parts, :bound, :content
|
13
|
+
|
14
|
+
|
15
|
+
def initialize(data=nil)
|
16
|
+
self.header = Rexlite::MIME::Header.new
|
17
|
+
self.parts = []
|
18
|
+
self.bound = "_Part_#{rand(1024)}_#{rand(0xffffffff)}_#{rand(0xffffffff)}"
|
19
|
+
self.content = ''
|
20
|
+
if data
|
21
|
+
head,body = data.split(/\r?\n\r?\n/, 2)
|
22
|
+
|
23
|
+
self.header.parse(head)
|
24
|
+
ctype = self.header.find('Content-Type')
|
25
|
+
|
26
|
+
if ctype && ctype[1] && ctype[1] =~ /multipart\/mixed;\s*boundary="?([A-Za-z0-9'\(\)\+\_,\-\.\/:=\?^\s]+)"?/
|
27
|
+
self.bound = $1
|
28
|
+
chunks = body.to_s.split(/--#{self.bound}(--)?\r?\n/)
|
29
|
+
self.content = chunks.shift.to_s.gsub(/\s+$/, '')
|
30
|
+
self.content << "\r\n" unless self.content.empty?
|
31
|
+
|
32
|
+
chunks.each do |chunk|
|
33
|
+
break if chunk == "--"
|
34
|
+
head,body = chunk.split(/\r?\n\r?\n/, 2)
|
35
|
+
part = Rexlite::MIME::Part.new
|
36
|
+
part.header.parse(head)
|
37
|
+
part.content = body.gsub(/\s+$/, '')
|
38
|
+
self.parts << part
|
39
|
+
end
|
40
|
+
else
|
41
|
+
self.content = body.to_s.gsub(/\s+$/, '') + "\r\n"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def to
|
47
|
+
(self.header.find('To') || [nil, nil])[1]
|
48
|
+
end
|
49
|
+
|
50
|
+
def to=(val)
|
51
|
+
self.header.set("To", val)
|
52
|
+
end
|
53
|
+
|
54
|
+
def from=(val)
|
55
|
+
self.header.set("From", val)
|
56
|
+
end
|
57
|
+
|
58
|
+
def from
|
59
|
+
(self.header.find('From') || [nil, nil])[1]
|
60
|
+
end
|
61
|
+
|
62
|
+
def subject=(val)
|
63
|
+
self.header.set("Subject", val)
|
64
|
+
end
|
65
|
+
|
66
|
+
def subject
|
67
|
+
(self.header.find('Subject') || [nil, nil])[1]
|
68
|
+
end
|
69
|
+
|
70
|
+
def mime_defaults
|
71
|
+
self.header.set("MIME-Version", "1.0")
|
72
|
+
self.header.set("Content-Type", "multipart/mixed; boundary=\"#{self.bound}\"")
|
73
|
+
self.header.set("Subject", '') # placeholder
|
74
|
+
self.header.set("Date", Time.now.strftime("%a,%e %b %Y %H:%M:%S %z"))
|
75
|
+
self.header.set("Message-ID",
|
76
|
+
"<"+
|
77
|
+
rand_text_alphanumeric(rand(20)+40)+
|
78
|
+
"@"+
|
79
|
+
rand_text_alpha(rand(20)+3)+
|
80
|
+
">"
|
81
|
+
)
|
82
|
+
self.header.set("From", '') # placeholder
|
83
|
+
self.header.set("To", '') # placeholder
|
84
|
+
end
|
85
|
+
|
86
|
+
def rand_text_alphanumeric(len, bad='')
|
87
|
+
foo = []
|
88
|
+
foo += ('A' .. 'Z').to_a
|
89
|
+
foo += ('a' .. 'z').to_a
|
90
|
+
foo += ('0' .. '9').to_a
|
91
|
+
rand_base(len, bad, *foo )
|
92
|
+
end
|
93
|
+
|
94
|
+
def rand_text_alpha(len, bad='')
|
95
|
+
foo = []
|
96
|
+
foo += ('A' .. 'Z').to_a
|
97
|
+
foo += ('a' .. 'z').to_a
|
98
|
+
rand_base(len, bad, *foo )
|
99
|
+
end
|
100
|
+
|
101
|
+
def add_part(data='', content_type='text/plain', transfer_encoding="8bit", content_disposition=nil)
|
102
|
+
part = Rexlite::MIME::Part.new
|
103
|
+
|
104
|
+
if content_disposition
|
105
|
+
part.header.set("Content-Disposition", content_disposition)
|
106
|
+
end
|
107
|
+
|
108
|
+
part.header.set("Content-Type", content_type) if content_type
|
109
|
+
|
110
|
+
if transfer_encoding
|
111
|
+
part.header.set("Content-Transfer-Encoding", transfer_encoding)
|
112
|
+
end
|
113
|
+
|
114
|
+
part.content = data
|
115
|
+
self.parts << part
|
116
|
+
part
|
117
|
+
end
|
118
|
+
|
119
|
+
def add_part_attachment(data, name)
|
120
|
+
self.add_part(
|
121
|
+
encode_base64(data, "\r\n"),
|
122
|
+
"application/octet-stream; name=\"#{name}\"",
|
123
|
+
"base64",
|
124
|
+
"attachment; filename=\"#{name}\""
|
125
|
+
)
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
def add_part_inline_attachment(data, name)
|
130
|
+
self.add_part(
|
131
|
+
encode_base64(data, "\r\n"),
|
132
|
+
"application/octet-stream; name=\"#{name}\"",
|
133
|
+
"base64",
|
134
|
+
"inline; filename=\"#{name}\""
|
135
|
+
)
|
136
|
+
end
|
137
|
+
|
138
|
+
def encode_base64(str, delim='')
|
139
|
+
[str.to_s].pack("m").gsub(/\s+/, delim)
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
def to_s
|
144
|
+
header_string = self.header.to_s
|
145
|
+
|
146
|
+
msg = header_string.empty? ? '' : force_crlf(self.header.to_s + "\r\n")
|
147
|
+
msg << force_crlf(self.content + "\r\n") unless self.content.empty?
|
148
|
+
|
149
|
+
self.parts.each do |part|
|
150
|
+
msg << force_crlf("--" + self.bound + "\r\n")
|
151
|
+
msg << part.to_s
|
152
|
+
end
|
153
|
+
|
154
|
+
msg << force_crlf("--" + self.bound + "--\r\n") if self.parts.length > 0
|
155
|
+
|
156
|
+
msg
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
module Rexlite
|
3
|
+
module MIME
|
4
|
+
class Part
|
5
|
+
|
6
|
+
require 'nexpose/rexlite/mime/header'
|
7
|
+
require 'nexpose/rexlite/mime/encoding'
|
8
|
+
|
9
|
+
include Rexlite::MIME::Encoding
|
10
|
+
|
11
|
+
attr_accessor :header, :content
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
self.header = Rexlite::MIME::Header.new
|
15
|
+
self.content = ''
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
self.header.to_s + "\r\n" + content_encoded + "\r\n"
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the part content with any necessary encoding or transformation
|
23
|
+
# applied.
|
24
|
+
#
|
25
|
+
# @return [String] Content with encoding or transformations applied.
|
26
|
+
def content_encoded
|
27
|
+
binary_content? ? content : force_crlf(content)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Answers if the part content is binary.
|
31
|
+
#
|
32
|
+
# @return [Boolean] true if the part content is binary, false otherwise.
|
33
|
+
def binary_content?
|
34
|
+
transfer_encoding && transfer_encoding == 'binary'
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns the Content-Transfer-Encoding of the part.
|
38
|
+
#
|
39
|
+
# @return [nil] if the part hasn't Content-Transfer-Encoding.
|
40
|
+
# @return [String] The Content-Transfer-Encoding or the part.
|
41
|
+
def transfer_encoding
|
42
|
+
h = header.find('Content-Transfer-Encoding')
|
43
|
+
return nil if h.nil?
|
44
|
+
|
45
|
+
h[1]
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/nexpose/scan.rb
CHANGED
@@ -204,6 +204,26 @@ module Nexpose
|
|
204
204
|
Scan.parse(response.res) if response.success
|
205
205
|
end
|
206
206
|
|
207
|
+
# Initiate an ad-hoc scan on a subset of site assets with
|
208
|
+
# a specific scan template and scan engine, which may differ
|
209
|
+
# from the site's defined scan template and scan engine.
|
210
|
+
#
|
211
|
+
# @param [Fixnum] site_id Site ID to scan.
|
212
|
+
# @param [Array[String]] assets Hostnames and/or IP addresses to scan.
|
213
|
+
# @param [String] scan_template The scan template ID.
|
214
|
+
# @param [Fixnum] scan_engine The scan engine ID.
|
215
|
+
# @return [Fixnum] Scan ID.
|
216
|
+
#
|
217
|
+
def scan_assets_with_template_and_engine(site_id, assets, scan_template, scan_engine)
|
218
|
+
uri = "/data/site/#{site_id}/scan"
|
219
|
+
assets.size > 1 ? addresses = assets.join(',') : addresses = assets.first
|
220
|
+
params = { 'addressList' => addresses,
|
221
|
+
'template' => scan_template,
|
222
|
+
'scanEngine' => scan_engine }
|
223
|
+
scan_id = AJAX.form_post(self, uri, params)
|
224
|
+
scan_id.to_i
|
225
|
+
end
|
226
|
+
|
207
227
|
# Utility method for appending a HostName or IPRange object into an
|
208
228
|
# XML object, in preparation for ad hoc scanning.
|
209
229
|
#
|
@@ -357,6 +377,29 @@ module Nexpose
|
|
357
377
|
end
|
358
378
|
end
|
359
379
|
|
380
|
+
# Get paused scans. Provide a site ID to get paused scans for a site.
|
381
|
+
# With no site ID, all paused scans are returned.
|
382
|
+
#
|
383
|
+
# @param [Fixnum] site_id Site ID to retrieve paused scans for.
|
384
|
+
# @param [Fixnum] limit The maximum number of records to return from this call.
|
385
|
+
# @return [Array[ActiveScan]] List of paused scans.
|
386
|
+
#
|
387
|
+
def paused_scans(site_id = nil, limit = nil)
|
388
|
+
if site_id
|
389
|
+
uri = "/data/scan/site/#{site_id}?status=active"
|
390
|
+
rows = AJAX.row_pref_of(limit)
|
391
|
+
params = { 'sort' => 'endTime', 'dir' => 'DESC', 'startIndex' => 0 }
|
392
|
+
AJAX.preserving_preference(self, 'site-active-scans') do
|
393
|
+
data = DataTable._get_json_table(self, uri, params, rows, limit).select { |scan| scan['paused'] }
|
394
|
+
data.map(&ActiveScan.method(:parse_json))
|
395
|
+
end
|
396
|
+
else
|
397
|
+
uri = '/data/site/scans/dyntable.xml?printDocType=0&tableID=siteScansTable&activeOnly=true'
|
398
|
+
data = DataTable._get_dyn_table(self, uri).select { |scan| (scan['Status'].include? 'Paused') }
|
399
|
+
data.map(&ActiveScan.method(:parse_dyntable))
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
360
403
|
# Export the data associated with a single scan, and optionally store it in
|
361
404
|
# a zip-compressed file under the provided name.
|
362
405
|
#
|
@@ -385,11 +428,7 @@ module Nexpose
|
|
385
428
|
end
|
386
429
|
end
|
387
430
|
|
388
|
-
# Import scan data into a site.
|
389
|
-
#
|
390
|
-
# This code currently depends on a gem not in the gemspec. In order to use
|
391
|
-
# this method, you will need to add the following line to your script:
|
392
|
-
# require 'rest-client'
|
431
|
+
# Import scan data into a site.
|
393
432
|
#
|
394
433
|
# This method is designed to work with export_scan to migrate scan data
|
395
434
|
# from one console to another. This method will import the data as if run
|
@@ -404,7 +443,7 @@ module Nexpose
|
|
404
443
|
# @return [String] An empty string on success.
|
405
444
|
#
|
406
445
|
def import_scan(site_id, zip_file)
|
407
|
-
data =
|
446
|
+
data = Rexlite::MIME::Message.new
|
408
447
|
data.add_part(site_id.to_s, nil, nil, 'form-data; name="siteid"')
|
409
448
|
data.add_part(session_id, nil, nil, 'form-data; name="nexposeCCSessionID"')
|
410
449
|
::File.open(zip_file, 'rb') do |scan|
|
@@ -768,4 +807,36 @@ module Nexpose
|
|
768
807
|
end
|
769
808
|
end
|
770
809
|
end
|
810
|
+
|
811
|
+
class ActiveScan < CompletedScan
|
812
|
+
def self.parse_dyntable(json)
|
813
|
+
new do
|
814
|
+
@id = json['Scan ID']
|
815
|
+
@site_id = json['Site ID']
|
816
|
+
@status = CompletedScan._parse_status(json['Status Code'])
|
817
|
+
@start_time = Time.at(json['Started'].to_i / 1000)
|
818
|
+
@end_time = Time.at(json['Progress'].to_i / 1000)
|
819
|
+
@duration = json['Elapsed'].to_i
|
820
|
+
@vulns = json['Vulnerabilities Discovered'].to_i
|
821
|
+
@assets = json['Devices Discovered'].to_i
|
822
|
+
@risk_score = json['riskScore']
|
823
|
+
@type = json['Scan Type'] == 'Manual' ? :manual : :scheduled
|
824
|
+
@engine_name = json['Scan Engine']
|
825
|
+
end
|
826
|
+
end
|
827
|
+
|
828
|
+
# Internal method to parsing status codes.
|
829
|
+
def self._parse_status(code)
|
830
|
+
case code
|
831
|
+
when 'U'
|
832
|
+
:running
|
833
|
+
when 'P'
|
834
|
+
:paused
|
835
|
+
when 'I'
|
836
|
+
:integrating
|
837
|
+
else
|
838
|
+
:unknown
|
839
|
+
end
|
840
|
+
end
|
841
|
+
end
|
771
842
|
end
|
@@ -321,12 +321,6 @@ module Nexpose
|
|
321
321
|
checks ? checks.elements.to_a('VulnCategory').map { |c| c.attributes['name'] } : []
|
322
322
|
end
|
323
323
|
|
324
|
-
# @deprecated Use {#enabled_checks_by_category} instead
|
325
|
-
def checks_by_category
|
326
|
-
warn "[DEPRECATED] Use #{self.class}#enabled_checks_by_category instead of #{self.class}##{__method__}"
|
327
|
-
enabled_checks_by_category
|
328
|
-
end
|
329
|
-
|
330
324
|
# Get a list of the check categories enabled for this scan template.
|
331
325
|
#
|
332
326
|
# @return [Array[String]] List of enabled categories.
|
@@ -370,12 +364,6 @@ module Nexpose
|
|
370
364
|
checks ? checks.elements.to_a('CheckType').map { |c| c.attributes['name'] } : []
|
371
365
|
end
|
372
366
|
|
373
|
-
# @deprecated Use {#enabled_checks_by_type} instead
|
374
|
-
def checks_by_type
|
375
|
-
warn "[DEPRECATED] Use #{self.class}#enabled_checks_by_type instead of #{self.class}##{__method__}"
|
376
|
-
enabled_checks_by_type
|
377
|
-
end
|
378
|
-
|
379
367
|
# Get a list of the check types enabled for this scan template.
|
380
368
|
#
|
381
369
|
# @return [Array[String]] List of enabled check types.
|
@@ -430,12 +418,6 @@ module Nexpose
|
|
430
418
|
checks.elements.delete("Enabled/#{elem}[@name='#{check}']")
|
431
419
|
end
|
432
420
|
|
433
|
-
# @deprecated Use {#enabled_vuln_checks} instead
|
434
|
-
def vuln_checks
|
435
|
-
warn "[DEPRECATED] Use #{self.class}#enabled_vuln_checks instead of #{self.class}##{__method__}"
|
436
|
-
enabled_vuln_checks
|
437
|
-
end
|
438
|
-
|
439
421
|
# Get a list of the individual vuln checks enabled for this scan template.
|
440
422
|
#
|
441
423
|
# @return [Array[String]] List of enabled vulnerability checks.
|
data/lib/nexpose/site.rb
CHANGED
@@ -250,12 +250,6 @@ module Nexpose
|
|
250
250
|
end
|
251
251
|
end
|
252
252
|
|
253
|
-
# @deprecated Use {#include_ip_range} instead.
|
254
|
-
def add_ip_range(from, to)
|
255
|
-
warn "[DEPRECATED] Use #{self.class}#include_ip_range instead of #{self.class}#add_ip_range."
|
256
|
-
include_ip_range(from, to)
|
257
|
-
end
|
258
|
-
|
259
253
|
# Remove assets to this site by IP address range.
|
260
254
|
#
|
261
255
|
# @param [String] from Beginning IP address of a range.
|
@@ -274,12 +268,6 @@ module Nexpose
|
|
274
268
|
end
|
275
269
|
end
|
276
270
|
|
277
|
-
# @deprecated Use {#remove_included_ip_range} instead.
|
278
|
-
def remove_ip_range(from, to)
|
279
|
-
warn "[DEPRECATED] Use #{self.class}#remove_included_ip_range instead of #{self.class}#remove_ip_range."
|
280
|
-
remove_included_ip_range(from, to)
|
281
|
-
end
|
282
|
-
|
283
271
|
# Adds an asset to this site included scan targets, resolving whether an IP or hostname is
|
284
272
|
# provided.
|
285
273
|
#
|
@@ -289,15 +277,6 @@ module Nexpose
|
|
289
277
|
@included_scan_targets[:addresses] << HostOrIP.convert(asset)
|
290
278
|
end
|
291
279
|
|
292
|
-
# @deprecated Use {#include_asset} instead.
|
293
|
-
def add_asset(asset)
|
294
|
-
warn "[DEPRECATED] Use #{self.class}#include_asset instead of #{self.class}#add_asset."
|
295
|
-
include_asset(asset)
|
296
|
-
end
|
297
|
-
|
298
|
-
alias_method :add_host, :add_asset
|
299
|
-
alias_method :add_ip, :add_asset
|
300
|
-
|
301
280
|
# Remove an asset to this site included scan targets, resolving whether an IP or hostname is
|
302
281
|
# provided.
|
303
282
|
#
|
@@ -307,15 +286,6 @@ module Nexpose
|
|
307
286
|
@included_scan_targets[:addresses].reject! { |existing_asset| existing_asset == HostOrIP.convert(asset) }
|
308
287
|
end
|
309
288
|
|
310
|
-
# @deprecated Use {#remove_included_asset} instead.
|
311
|
-
def remove_asset(asset)
|
312
|
-
warn "[DEPRECATED] Use #{self.class}#remove_included_asset instead of #{self.class}#remove_asset."
|
313
|
-
remove_included_asset(asset)
|
314
|
-
end
|
315
|
-
|
316
|
-
alias_method :remove_host, :remove_asset
|
317
|
-
alias_method :remove_ip, :remove_asset
|
318
|
-
|
319
289
|
# Adds assets to this site excluded scan targets by IP address range.
|
320
290
|
#
|
321
291
|
# @param [String] from Beginning IP address of a range.
|
data/lib/nexpose/version.rb
CHANGED
data/lib/nexpose.rb
CHANGED
@@ -53,10 +53,10 @@ require 'rexml/document'
|
|
53
53
|
require 'net/https'
|
54
54
|
require 'net/http'
|
55
55
|
require 'uri'
|
56
|
-
require 'rex/mime'
|
57
56
|
require 'ipaddr'
|
58
57
|
require 'json'
|
59
58
|
require 'cgi'
|
59
|
+
require 'nexpose/rexlite/mime'
|
60
60
|
require 'nexpose/api'
|
61
61
|
require 'nexpose/json_serializer'
|
62
62
|
require 'nexpose/error'
|
@@ -116,11 +116,3 @@ module Nexpose
|
|
116
116
|
puts 'response: ' + object.response_xml.to_s
|
117
117
|
end
|
118
118
|
end
|
119
|
-
|
120
|
-
# Monkey patch from ActiveSupport which rex 2.0.3 incorrectly replies upon.
|
121
|
-
# This enables the multipart MIME handling in Connection#import_scan
|
122
|
-
class String
|
123
|
-
def blank?
|
124
|
-
self !~ /\S/
|
125
|
-
end
|
126
|
-
end
|
data/nexpose.gemspec
CHANGED
@@ -18,8 +18,6 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.required_ruby_version = '>= 2.1'
|
19
19
|
s.platform = 'ruby'
|
20
20
|
|
21
|
-
s.add_runtime_dependency('rex', '= 2.0.8')
|
22
|
-
|
23
21
|
s.add_development_dependency('bundler', '~> 1.3')
|
24
22
|
s.add_development_dependency('codeclimate-test-reporter', '~> 0.4.6')
|
25
23
|
s.add_development_dependency('simplecov', '~> 0.9.1')
|
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:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- HD Moore
|
@@ -13,22 +13,8 @@ authors:
|
|
13
13
|
autorequire:
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
|
-
date: 2015-12-
|
16
|
+
date: 2015-12-11 00:00:00.000000000 Z
|
17
17
|
dependencies:
|
18
|
-
- !ruby/object:Gem::Dependency
|
19
|
-
name: rex
|
20
|
-
requirement: !ruby/object:Gem::Requirement
|
21
|
-
requirements:
|
22
|
-
- - '='
|
23
|
-
- !ruby/object:Gem::Version
|
24
|
-
version: 2.0.8
|
25
|
-
type: :runtime
|
26
|
-
prerelease: false
|
27
|
-
version_requirements: !ruby/object:Gem::Requirement
|
28
|
-
requirements:
|
29
|
-
- - '='
|
30
|
-
- !ruby/object:Gem::Version
|
31
|
-
version: 2.0.8
|
32
18
|
- !ruby/object:Gem::Dependency
|
33
19
|
name: bundler
|
34
20
|
requirement: !ruby/object:Gem::Requirement
|
@@ -179,6 +165,12 @@ files:
|
|
179
165
|
- lib/nexpose/pool.rb
|
180
166
|
- lib/nexpose/report.rb
|
181
167
|
- lib/nexpose/report_template.rb
|
168
|
+
- lib/nexpose/rexlite/README.md
|
169
|
+
- lib/nexpose/rexlite/mime.rb
|
170
|
+
- lib/nexpose/rexlite/mime/encoding.rb
|
171
|
+
- lib/nexpose/rexlite/mime/header.rb
|
172
|
+
- lib/nexpose/rexlite/mime/message.rb
|
173
|
+
- lib/nexpose/rexlite/mime/part.rb
|
182
174
|
- lib/nexpose/role.rb
|
183
175
|
- lib/nexpose/scan.rb
|
184
176
|
- lib/nexpose/scan_template.rb
|