class Nexpose::Connection
Object that represents a connection to a Nexpose
Security Console
.
Examples¶ ↑
# Create a new Nexpose::Connection on the default port nsc = Connection.new('10.1.40.10', 'nxadmin', 'password') # Create a new Nexpose::Connection from a URI or "URI" String nsc = Connection.from_uri('https://10.1.40.10:3780', 'nxadmin', 'password') # Create a new Nexpose::Connection with a specific port nsc = Connection.new('10.1.40.10', 'nxadmin', 'password', 443) # Create a new Nexpose::Connection with a silo identifier nsc = Connection.new('10.1.40.10', 'nxadmin', 'password', 3780, 'default') # Create a new Nexpose::Connection with a two-factor authentication (2FA) token nsc = Connection.new('10.1.40.10', 'nxadmin', 'password', 3780, nil, '123456') # Create a new Nexpose::Connection with an excplicitly trusted web certificate trusted_cert = ::File.read('cert.pem') nsc = Connection.new('10.1.40.10', 'nxadmin', 'password', 3780, nil, nil, trusted_cert) # Login to NSC and Establish a Session ID nsc.login # Check Session ID if nsc.session_id puts 'Login Successful' else puts 'Login Failure' end # Logout logout_success = nsc.logout
Object that represents a connection to a Nexpose
Security Console
.
Attributes
The hostname or IP Address of the NSC
The optional HTTP open_timeout
value, in seconds For more information visit the link below: ruby-doc.org/stdlib/libdoc/net/http/rdoc/Net/HTTP.html#open_timeout-attribute-method
The password used to login to the NSC
The port of the NSC (default is 3780)
The last XML
request sent by this object, useful for debugging.
The last XML
response received by this object, useful for debugging.
Session ID of this connection
The main HTTP read_timeout value, in seconds For more information visit the link below: ruby-doc.org/stdlib/libdoc/net/http/rdoc/Net/HTTP.html#read_timeout-attribute-method
The token used to login to the NSC
The trust store to validate connections against if any
The URL for communication
The username used to login to the NSC
Public Class Methods
A constructor to load a Connection
object from a URI
# File lib/nexpose/connection.rb, line 70 def self.from_uri(uri, user, pass, silo_id = nil, token = nil, trust_cert = nil) uri = URI.parse(uri) new(uri.host, user, pass, uri.port, silo_id, token, trust_cert) end
A constructor for Connection
@param [String] ip The IP address or hostname/FQDN of the Nexpose
console. @param [String] user The username for Nexpose
sessions. @param [String] pass The password for Nexpose
sessions. @param [Fixnum] port The port number of the Nexpose
console. @param [String] silo_id The silo identifier for Nexpose
sessions. @param [String] token The two-factor authentication (2FA) token for Nexpose
sessions. @param [String] trust_cert The PEM-formatted web certificate of the Nexpose
console. Used for SSL validation.
# File lib/nexpose/connection.rb, line 84 def initialize(ip, user, pass, port = 3780, silo_id = nil, token = nil, trust_cert = nil) @host = ip @username = user @password = pass @port = port @silo_id = silo_id @token = token @trust_store = create_trust_store(trust_cert) unless trust_cert.nil? @session_id = nil @url = "https://#{@host}:#{@port}/api/API_VERSION/xml" @timeout = 120 @open_timeout = 120 end
Public Instance Methods
Utility method for appending a HostName
or IPRange
object into an XML
object, in preparation for ad hoc scanning.
@param [REXML::Document] xml Prepared API call to execute. @param [HostName|IPRange] asset Asset
to append to XML
.
# File lib/nexpose/scan.rb, line 235 def _append_asset!(xml, asset) if asset.is_a? Nexpose::IPRange xml.add_element('range', 'from' => asset.from, 'to' => asset.to) else # Assume HostName host = REXML::Element.new('host') host.text = asset xml.add_element(host) end end
# File lib/nexpose/maint.rb, line 59 def _maintenance_restart parameters = { 'cancelAllTasks' => false, 'cmd' => 'restartServer', 'targetTask' => 'maintModeHandler' } xml = AJAX.form_post(self, '/admin/global/maintenance/maintCmd.txml', parameters) !!(xml =~ /succeded="true"/) end
Utility method for executing prepared XML
for adhoc with schedules
@param [REXML::Document] xml Prepared API call to execute.
# File lib/nexpose/scan.rb, line 260 def _scan_ad_hoc_with_schedules(xml) r = execute(xml, '1.1', timeout: 60) r.success end
Retrieve a list of current scan activities across all Scan
Engines managed by Nexpose
. This method returns lighter weight objects than scan_activity.
@return [Array] Array of ScanData
objects associated with
each active scan on the engines.
# File lib/nexpose/scan.rb, line 320 def activity r = execute(make_xml('ScanActivityRequest')) res = [] if r.success r.res.elements.each('//ScanSummary') do |scan| res << ScanData.parse(scan) end end res end
Retrieve all vulnerability definitions currently in a Nexpose
console.
Note, this can easily take 30 seconds to complete and will load over 55,000 vulnerability definitions.
@return [Array] Collection of vulnerability definitions.
# File lib/nexpose/vuln_def.rb, line 10 def all_vulns uri = '/api/2.0/vulnerability_definitions' resp = AJAX.get(self, uri, AJAX::CONTENT_TYPE::JSON, per_page: 2_147_483_647) json = JSON.parse(resp, symbolize_names: true) json[:resources].map { |e| VulnerabilityDefinition.new.object_from_hash(self, e) } end
Retrieve the scan history for an asset. Note: This is not optimized for querying many assets.
@param [Fixnum] asset_id Unique identifer of an asset. @return [Array] A list of scans for the asset.
# File lib/nexpose/device.rb, line 142 def asset_scan_history(asset_id) uri = "/data/assets/#{asset_id}/scans" AJAX.preserving_preference(self, 'asset-scan-history') do data = DataTable._get_json_table(self, uri, {}, 500, nil, true) data.each { |a| a['assetID'] = asset_id.to_s } data.map(&AssetScan.method(:parse_json)) end end
Create a backup of this security console's data. A restart will be initiated in order to put the product into maintenance mode while the backup is made. It will then restart automatically.
@param [Boolean] platform_independent Whether to make a platform
independent backup.
@param [String] description A note about this backup which will be
visible through the web interface.
@return [Boolean] Whether a backup is successfully initiated.
# File lib/nexpose/maint.rb, line 24 def backup(platform_independent = false, description = nil) parameters = { 'backup_desc' => description, 'cmd' => 'backup', 'platform_independent' => platform_independent, 'targetTask' => 'backupRestore' } xml = AJAX.form_post(self, '/admin/global/maintenance/maintCmd.txml', parameters) if !!(xml =~ /succeded="true"/) _maintenance_restart end end
Retrieve a list of assets which completed in a given scan. If called during a scan, this method returns currently completed assets. A “completed” asset can be in one of three states: completed successfully, failed due to an error, or stopped by a user.
@param [Fixnum] scan_id Unique identifier of a scan. @return [Array] List of completed assets.
# File lib/nexpose/device.rb, line 106 def completed_assets(scan_id) uri = "/data/asset/scan/#{scan_id}/complete-assets" AJAX.preserving_preference(self, 'scan-complete-assets') do data = DataTable._get_json_table(self, uri, {}, 500, nil, false) data.map(&CompletedAsset.method(:parse_json)) end end
Retrieve a history of the completed scans for a given site.
@param [FixNum] site_id Site
ID to find scans for. @return [CompletedScan] details of the completed scans for the site.
# File lib/nexpose/site.rb, line 70 def completed_scans(site_id) table = { 'table-id' => 'site-completed-scans' } data = DataTable._get_json_table(self, "/data/scan/site/#{site_id}", table) data.map(&CompletedScan.method(:parse_json)) end
Execute an arbitrary console command that is supplied as text via the supplied parameter. Console
commands are documented in the administrator's guide. If you use a command that is not listed in the administrator's guide, the application will return the XMLResponse.
# File lib/nexpose/manage.rb, line 12 def console_command(cmd_string) xml = make_xml('ConsoleCommandRequest', {}) cmd = REXML::Element.new('Command') cmd.text = cmd_string xml << cmd r = execute(xml) if r.success r.res.elements.each('//Output') do |out| return out.text.to_s end else false end end
Initiate database maintenance tasks to improve database performance and consistency. A restart will be initiated in order to put the product into maintenance mode while the tasks are run. It will then restart automatically.
@param [Boolean] clean_up Removes any unnecessary data from the database. @param [Boolean] compress Compresses the database tables and reclaims
unused, allocated space.
@param [Boolean] reindex Drops and recreates the database indexes for
improved performance.
@return [Boolean] Whether a maintenance tasks are successfully initiated.
# File lib/nexpose/maint.rb, line 47 def db_maintenance(clean_up = false, compress = false, reindex = false) return unless compress || clean_up || reindex parameters = { 'cmd' => 'startMaintenance', 'targetTask' => 'dbMaintenance' } parameters['cleanup'] = 1 if clean_up parameters['compress'] = 1 if compress parameters['reindex'] = 1 if reindex xml = AJAX.form_post(self, '/admin/global/maintenance/maintCmd.txml', parameters) if !!(xml =~ /succeded="true"/) _maintenance_restart end end
Delete an asset group and all associated data.
@param [Fixnum] id Asset
group ID to delete.
@return [Boolean] Whether group deletion succeeded.
# File lib/nexpose/group.rb, line 12 def delete_asset_group(id) r = execute(make_xml('AssetGroupDeleteRequest', { 'group-id' => id })) r.success end
# File lib/nexpose/device.rb, line 129 def delete_device(device_id) r = execute(make_xml('DeviceDeleteRequest', { 'device-id' => device_id })) r.success end
Delete an existing connection to a target used for dynamic discovery of assets.
@param [Fixnum] id ID of an existing discovery connection.
# File lib/nexpose/discovery.rb, line 23 def delete_discovery_connection(id) xml = make_xml('DiscoveryConnectionDeleteRequest', { 'id' => id }) response = execute(xml, '1.2') response.success end
Removes a scan engine from the list of available engines.
@param [Fixnum] engine_id Unique ID of an existing engine to remove. @param [String] scope Whether the engine is global or silo scoped. @return [Boolean] true if engine successfully deleted.
# File lib/nexpose/engine.rb, line 12 def delete_engine(engine_id, scope = 'silo') xml = make_xml('EngineDeleteRequest', { 'engine-id' => engine_id, 'scope' => scope }) response = execute(xml, '1.2') response.success end
Delete a previously generated report.
@param [Fixnum] report_id ID of individual report to delete.
# File lib/nexpose/report.rb, line 65 def delete_report(report_id) xml = make_xml('ReportDeleteRequest', { 'report-id' => report_id }) execute(xml).success end
Delete a previously generated report definition. Also deletes any reports generated from that configuration.
@param [Fixnum] report_config_id ID of the report configuration to remove.
# File lib/nexpose/report.rb, line 75 def delete_report_config(report_config_id) xml = make_xml('ReportDeleteRequest', { 'reportcfg-id' => report_config_id }) execute(xml).success end
Deletes an existing, custom report template. Cannot delete built-in templates.
@param [String] template_id Unique identifier of the report template to remove.
# File lib/nexpose/report_template.rb, line 29 def delete_report_template(template_id) AJAX.delete(self, "/data/report/templates/#{URI.escape(template_id)}") end
Delete a scan and all its data from a console. Warning, this method is destructive and not guaranteed to leave a site in a valid state. DBCC may need to be run to correct missing or empty assets.
@param [Fixnum] scan_id Scan
ID to remove data for.
# File lib/nexpose/scan.rb, line 480 def delete_scan(scan_id) AJAX.delete(self, "/data/scan/#{scan_id}") end
Delete a scan template from the console. Cannot be used to delete a built-in template.
@param [String] id Unique identifier of an existing scan template.
# File lib/nexpose/scan_template.rb, line 21 def delete_scan_template(id) AJAX.delete(self, "/data/scan/templates/#{URI.encode(id)}") end
Delete the specified silo
@return Whether or not the delete request succeeded.
# File lib/nexpose/silo.rb, line 27 def delete_silo(silo_id) r = execute(make_xml('SiloDeleteRequest', { 'silo-id' => silo_id }), '1.2') r.success end
Delete the specified silo profile
@return Whether or not the delete request succeeded.
# File lib/nexpose/silo_profile.rb, line 27 def delete_silo_profile(silo_profile_id) r = execute(make_xml('SiloProfileDeleteRequest', { 'silo-profile-id' => silo_profile_id }), '1.2') r.success end
Delete the specified silo user
@return Whether or not the delete request succeeded.
# File lib/nexpose/multi_tenant_user.rb, line 26 def delete_silo_user(user_id) r = execute(make_xml('MultiTenantUserDeleteRequest', { 'user-id' => user_id }), '1.2') r.success end
Delete the specified site and all associated scan data.
@return Whether or not the delete request succeeded.
# File lib/nexpose/site.rb, line 31 def delete_site(site_id) r = execute(make_xml('SiteDeleteRequest', { 'site-id' => site_id })) r.success end
Deletes a tag by ID
@param [Fixnum] tag_id ID of tag to delete
# File lib/nexpose/tag.rb, line 23 def delete_tag(tag_id) AJAX.delete(self, "/api/2.0/tags/#{tag_id}") end
Deletes a Nexpose
ticket.
@param [Fixnum] ticket Unique ID of the ticket to delete. @return [Boolean] Whether or not the ticket deletion succeeded.
# File lib/nexpose/ticket.rb, line 26 def delete_ticket(ticket) # TODO: Take Ticket object, too, and pull out IDs. delete_tickets([ticket]) end
Deletes a Nexpose
ticket.
@param [Array] tickets Array of unique IDs of tickets to delete. @return [Boolean] Whether or not the ticket deletions succeeded.
# File lib/nexpose/ticket.rb, line 36 def delete_tickets(tickets) # TODO: Take Ticket objects, too, and pull out IDs. xml = make_xml('TicketDeleteRequest') tickets.each do |id| xml.add_element('Ticket', { 'id' => id }) end (execute xml, '1.2').success end
Delete a user from the Nexpose
console.
@param [Fixnum] user_id Unique ID for the user to delete. @return [Boolean] Whether or not the user deletion succeeded.
# File lib/nexpose/user.rb, line 36 def delete_user(user_id) response = execute(make_xml('UserDeleteRequest', { 'id' => user_id })) response.success end
Delete an existing vulnerability exception.
@param [Fixnum] id The ID of a vuln exception. @return [Boolean] Whether or not deletion was successful.
# File lib/nexpose/vuln_exception.rb, line 110 def delete_vuln_exception(id) xml = make_xml('VulnerabilityExceptionDeleteRequest', { 'exception-id' => id }) execute(xml, '1.2').success end
Download a specific URL, typically a report. Include an optional file_name parameter to write the output to a file.
Note: XML
and HTML reports have charts not downloaded by this method.
Would need to do something more sophisticated to grab all the associated image files.
# File lib/nexpose/connection.rb, line 135 def download(url, file_name = nil) return nil if (url.nil? || url.empty?) uri = URI.parse(url) http = Net::HTTP.new(@host, @port) http.use_ssl = true if @trust_store.nil? http.verify_mode = OpenSSL::SSL::VERIFY_NONE # XXX: security issue else http.cert_store = @trust_store end headers = { 'Cookie' => "nexposeCCSessionID=#{@session_id}" } if file_name http.request_get(uri.to_s, headers) do |resp| ::File.open(file_name, 'wb') do |file| resp.read_body { |chunk| file.write(chunk) } end end else resp = http.get(uri.to_s, headers) resp.body end end
Provide a list of current scan activities for a specific Scan
Engine
.
@return [Array] Array of ScanSummary
objects associated with
each active scan on the engine.
# File lib/nexpose/engine.rb, line 55 def engine_activity(engine_id) xml = make_xml('EngineActivityRequest', { 'engine-id' => engine_id }) r = execute(xml) arr = [] if r.success r.res.elements.each('//ScanSummary') do |scan_event| arr << ScanSummary.parse(scan_event) end end arr end
Obtain the version information for each scan engine. Includes Product, Content, and Java versions.
# File lib/nexpose/manage.rb, line 49 def engine_versions info = console_command('version engines') versions = [] engines = info.sub('VERSION INFORMATION\n', '').split(/\n\n/) engines.each do |eng| engdata = {} eng.split(/\n/).each do |kv| key, value = kv.split(/:\s*/) key = key.sub('Local Engine ', '').sub('Remote Engine ', '') engdata[key] = value end versions << engdata end versions end
Execute an API request
# File lib/nexpose/connection.rb, line 119 def execute(xml, version = '1.1', options = {}) options.store(:timeout, @timeout) unless options.key?(:timeout) options.store(:open_timeout, @open_timeout) @request_xml = xml.to_s @api_version = version response = APIRequest.execute(@url, @request_xml, @api_version, options, @trust_store) @response_xml = response.raw_response_data response end
Export the data associated with a single scan, and optionally store it in a zip-compressed file under the provided name.
@param [Fixnum] scan_id Scan
ID to remove data for. @param [String] zip_file Filename to export scan data to. @return [Fixnum] On success, returned the number of bytes written to
zip_file, if provided. Otherwise, returns raw ZIP binary data.
# File lib/nexpose/scan.rb, line 413 def export_scan(scan_id, zip_file = nil) http = AJAX.https(self) headers = { 'Cookie' => "nexposeCCSessionID=#{@session_id}", 'Accept-Encoding' => 'identity' } resp = http.get("/data/scan/#{scan_id}/export", headers) case resp when Net::HTTPSuccess if zip_file ::File.open(zip_file, 'wb') { |file| file.write(resp.body) } else resp.body end when Net::HTTPForbidden raise Nexpose::PermissionError.new(resp) else raise Nexpose::APIError.new(resp, "#{resp.class}: Unrecognized response.") end end
Perform an asset filter search that will locate assets matching the provided conditions.
For example, the following call will return assets with Java installed:
nsc.filter(Search::Field::SOFTWARE, Search::Operator::CONTAINS, 'java')
@param [String] field Constant from Search::Field
@param [String] operator Constant from Search::Operator
@param [String] value Search
term or constant from Search::Value
@return [Array] List of matching assets.
# File lib/nexpose/filter.rb, line 15 def filter(field, operator, value = '') criterion = Criterion.new(field, operator, value) criteria = Criteria.new(criterion) search(criteria) end
Find a Device
by its address.
This is a convenience method for finding a single device from a SiteDeviceListing. If no site_id is provided, the first matching device will be returned when a device occurs across multiple sites.
@param [String] address Address of the device to find. Usually the IP address. @param [FixNum] site_id Site
ID to restrict search to. @return [Device] The first matching Device
with the provided address,
if found.
# File lib/nexpose/device.rb, line 17 def find_device_by_address(address, site_id = nil) r = execute(make_xml('SiteDeviceListingRequest', { 'site-id' => site_id })) if r.success device = REXML::XPath.first(r.res, "SiteDeviceListingResponse/SiteDevices/device[@address='#{address}']") if device return Device.new(device.attributes['id'].to_i, device.attributes['address'], device.parent.attributes['site-id'], device.attributes['riskfactor'].to_f, device.attributes['riskscore'].to_f) end end nil end
Search
for Vulnerability
Checks.
@param [String] search_term Search
terms to search for. @param [Boolean] partial_words Allow partial word matches. @param [Boolean] all_words All words must be present. @return [Array] List of matching Vulnerability
Checks.
# File lib/nexpose/vuln.rb, line 76 def find_vuln_check(search_term, partial_words = true, all_words = true) uri = "/data/vulnerability/vulnerabilities/dyntable.xml?tableID=VulnCheckSynopsis&phrase=#{URI.encode(search_term)}&allWords=#{all_words}" data = DataTable._get_dyn_table(self, uri) data.map do |vuln| XML::VulnCheck.new(vuln) end end
Search
for any vulnerability definitions which refer to a given CVE.
@param [String] cve A valid CVE. @return [Array] A list of vuln definitions which check the CVE.
# File lib/nexpose/vuln_def.rb, line 22 def find_vulns_by_cve(cve) uri = '/api/2.0/vulnerability_definitions' resp = AJAX.get(self, uri, AJAX::CONTENT_TYPE::JSON, cve: cve) json = JSON.parse(resp, symbolize_names: true) json[:resources].map { |e| VulnerabilityDefinition.new.object_from_hash(self, e) } end
Find vulnerabilities by date available in Nexpose
. This is not the date the original vulnerability was published, but the date the check was made available in Nexpose
.
@param [String] from Vulnerability
publish date in format YYYY-MM-DD. @param [String] to Vulnerability
publish date in format YYYY-MM-DD. @return [Array] List of vulnerabilities published in
Nexpose between the provided dates.
# File lib/nexpose/vuln.rb, line 93 def find_vulns_by_date(from, to = nil) uri = "/data/vulnerability/synopsis/dyntable.xml?addedMin=#{from}" uri += "&addedMax=#{to}" if to DataTable._get_dyn_table(self, uri).map { |v| VulnSynopsis.new(v) } end
Search
for any vulnerability definitions which refer to a given reference ID.
Examples:
find_vulns_by_ref('oval', 'OVAL10476') find_vulns_by_ref('bid', 35067) find_vulns_by_ref('secunia', 35188)
@param [String] source External
vulnerability reference source. @param [String] id Unique vulnerability reference ID. @return [Array] A list of vuln definitions which
check the vulnerability.
# File lib/nexpose/vuln_def.rb, line 42 def find_vulns_by_ref(source, id) uri = '/api/2.0/vulnerability_definitions' resp = AJAX.get(self, uri, AJAX::CONTENT_TYPE::JSON, source: source, id: id) json = JSON.parse(resp, symbolize_names: true) json[:resources].map { |e| VulnerabilityDefinition.new.object_from_hash(self, e) } end
Search
for any vulnerability definitions which refer to a given title.
Note: This method will return a maximum of 500 results. If the search yields a high number of results, consider add more specific words to the title.
@param [String] title A (partial) title to search for. @param [Boolean] all_words Whether to include all words from the search
phrase in the search.
@return [Array] A list of vuln definitions with titles matching
the provided value.
# File lib/nexpose/vuln_def.rb, line 64 def find_vulns_by_title(title, all_words = true) uri = '/api/2.0/vulnerability_definitions' params = { title: title, all_words: all_words } resp = AJAX.get(self, uri, AJAX::CONTENT_TYPE::JSON, params) json = JSON.parse(resp, symbolize_names: true) json[:resources].map { |e| VulnerabilityDefinition.new.object_from_hash(self, e) } end
Generate a new report using the specified report definition.
# File lib/nexpose/report.rb, line 25 def generate_report(report_id, wait = false) xml = make_xml('ReportGenerateRequest', { 'report-id' => report_id }) response = execute(xml) if response.success response.res.elements.each('//ReportSummary') do |summary| summary = ReportSummary.parse(summary) # If not waiting or the report is finished, return now. return summary unless wait && summary.status == 'Started' end end so_far = 0 while wait summary = last_report(report_id) return summary unless summary.status == 'Started' sleep 5 so_far += 5 if (so_far % 60).zero? puts "Still waiting. Current status: #{summary.status}" end end nil end
Get a list of all assets currently associated with a group.
@param [Fixnum] group_id Unique identifier of an asset group. @return [Array] List of group assets.
# File lib/nexpose/device.rb, line 72 def group_assets(group_id) payload = { 'sort' => 'assetName', 'table-id' => 'group-assets', 'groupID' => group_id } results = DataTable._get_json_table(self, '/data/asset/group', payload) results.map { |a| FilteredAsset.new(a) } end
Import external assets into a Nexpose
console.
This method will synchronously import a collection of assets into the console. Each call to this method will be treated as a single event.
This method should only be used against “static” sites, i.e., those not tied to a dynamic population service like vSphere, AWS, etc.
If a paused scan exists on the site at the time of import, the newly imported assets will not be included in the scan when it resumes.
@param [Fixnum] site_id Existing site to import assets into. @param [Array] assets External
assets to import. @return [Array] collection of import results.
# File lib/nexpose/external.rb, line 18 def import_assets(site_id, assets) json = JSON.generate(Array(assets).map(&:to_h)) import_assets_from_json(site_id, json) end
Import external assets into a Nexpose
console.
@param [Fixnum] site_id Existing site to import assets into. @param [String] json JSON representation of assets to import. @return [Array] collection of import results.
# File lib/nexpose/external.rb, line 29 def import_assets_from_json(site_id, json) uri = "/api/2.1/sites/#{site_id}/assets" # Wait up to 5 minutes for a response. resp = AJAX.post(self, uri, json, AJAX::CONTENT_TYPE::JSON, 300) arr = JSON.parse(resp, symbolize_names: true) arr.map { |e| External::ImportResult.new.object_from_hash(self, e) } end
Import scan data into a site.
This method is designed to work with export_scan
to migrate scan data from one console to another. This method will import the data as if run from a local scan engine.
Scan
importing is restricted to only importing scans in chronological order. It assumes that it is the latest scan for a given site, and will abort if attempting to import an older scan.
@param [Fixnum] site_id Site
ID of the site to import the scan into. @param [String] zip_file Path to a previously exported scan archive. @return [Fixnum] The scan ID on success.
# File lib/nexpose/scan.rb, line 446 def import_scan(site_id, zip_file) data = Rexlite::MIME::Message.new data.add_part(site_id.to_s, nil, nil, 'form-data; name="siteid"') data.add_part(session_id, nil, nil, 'form-data; name="nexposeCCSessionID"') ::File.open(zip_file, 'rb') do |scan| data.add_part(scan.read, 'application/zip', 'binary', "form-data; name=\"scan\"; filename=\"#{zip_file}\"") end post = Net::HTTP::Post.new('/data/scan/import') post.body = data.to_s post.set_content_type('multipart/form-data', boundary: data.bound) # Avoiding AJAX#request, because the data can cause binary dump on error. http = AJAX.https(self) AJAX.headers(self, post) response = http.request(post) case response when Net::HTTPOK response.body.empty? ? response.body : response.body.to_i when Net::HTTPUnauthorized raise Nexpose::PermissionError.new(response) else raise Nexpose::APIError.new(post, response.body) end end
Retrieve a list of assets which are incomplete in a given scan. If called during a scan, this method returns currently incomplete assets which may be in progress.
@param [Fixnum] scan_id Unique identifier of a scan. @return [Array] List of incomplete assets.
# File lib/nexpose/device.rb, line 121 def incomplete_assets(scan_id) uri = "/data/asset/scan/#{scan_id}/incomplete-assets" AJAX.preserving_preference(self, 'scan-incomplete-assets') do data = DataTable._get_json_table(self, uri, {}, 500, nil, false) data.map(&IncompleteAsset.method(:parse_json)) end end
Get details of the last report generated with the specified report id.
# File lib/nexpose/report.rb, line 56 def last_report(report_config_id) history = report_history(report_config_id) history.sort { |a, b| b.generated_on <=> a.generated_on }.first end
Retrieve the scan summary statistics for the latest completed scan on a site.
Method will not return data on an active scan.
@param [FixNum] site_id Site
ID to find latest scan for. @return [ScanSummary] details of the last completed scan for a site.
# File lib/nexpose/site.rb, line 61 def last_scan(site_id) site_scan_history(site_id).select(&:end_time).max_by(&:end_time) end
Retrieve an array of all asset groups the user is authorized to view or manage.
@return [Array] Array of AssetGroupSummary
objects.
# File lib/nexpose/group.rb, line 24 def list_asset_groups r = execute(make_xml('AssetGroupListingRequest')) groups = [] if r.success r.res.elements.each('AssetGroupListingResponse/AssetGroupSummary') do |group| groups << AssetGroupSummary.new(group.attributes['id'].to_i, group.attributes['name'], group.attributes['description'], group.attributes['riskscore'].to_f, group.attributes['dynamic'].to_i == 1) end end groups end
List the vulnerability findings for a given device ID.
@param [Fixnum] dev_id Unique identifier of a device (asset). @return [Array] List of vulnerability findings.
# File lib/nexpose/device.rb, line 85 def list_device_vulns(dev_id) parameters = { 'devid' => dev_id, 'table-id' => 'vulnerability-listing' } json = DataTable._get_json_table(self, '/data/vulnerability/asset-vulnerabilities', parameters) json.map { |vuln| VulnFinding.new(vuln) } end
Retrieve information about all available connections for dynamic discovery of assets, including whether or not connections are active.
# File lib/nexpose/discovery.rb, line 8 def list_discovery_connections xml = make_xml('DiscoveryConnectionListingRequest') response = execute(xml, '1.2') connections = [] response.res.elements.each('DiscoveryConnectionListingResponse/DiscoveryConnectionSummary') do |conn| connections << DiscoveryConnection.parse(conn) end connections end
Retrieve a list of all Scan
Engine
Pools managed by the Security Console
.
@return [Array] Array of EnginePoolSummary
objects
associated with each engine associated with this security console.
# File lib/nexpose/pool.rb, line 11 def list_engine_pools response = execute(make_xml('EnginePoolListingRequest'), '1.2') arr = [] if response.success response.res.elements.each('EnginePoolListingResponse/EnginePoolSummary') do |pool| arr << EnginePoolSummary.new(pool.attributes['id'], pool.attributes['name'], pool.attributes['scope']) end end arr end
Retrieve a list of all Scan
Engines managed by the Security Console
.
@return [Array] Array of EngineSummary
objects associated
with each engine associated with this security console.
# File lib/nexpose/engine.rb, line 72 def list_engines response = execute(make_xml('EngineListingRequest')) arr = [] if response.success response.res.elements.each('//EngineSummary') do |engine| arr << EngineSummary.new(engine.attributes['id'].to_i, engine.attributes['name'], engine.attributes['address'], engine.attributes['port'].to_i, engine.attributes['status'], engine.attributes['scope']) end end arr end
Provide a list of all report templates the user can access on the Security Console
.
@return [Array] List of current report templates.
# File lib/nexpose/report_template.rb, line 11 def list_report_templates r = execute(make_xml('ReportTemplateListingRequest', {})) templates = [] if r.success r.res.elements.each('//ReportTemplateSummary') do |template| templates << ReportTemplateSummary.parse(template) end end templates end
Provide a listing of all report definitions the user can access on the Security Console
.
@return [Array] List of current report configuration.
# File lib/nexpose/report.rb, line 11 def list_reports r = execute(make_xml('ReportListingRequest')) reports = [] if r.success r.res.elements.each('//ReportConfigSummary') do |report| reports << ReportConfigSummary.parse(report) end end reports end
List the scan templates currently configured on the console.
@return [Array] list of scan template summary objects.
# File lib/nexpose/scan_template.rb, line 9 def list_scan_templates templates = JSON.parse(AJAX.get(self, '/api/2.0/scan_templates')) templates['resources'].map { |t| ScanTemplateSummary.new(t) } end
Retrieve a list of all silos the user is authorized to view or manage.
@return [Array] Array of SiloSummary
objects.
# File lib/nexpose/silo_profile.rb, line 10 def list_silo_profiles r = execute(make_xml('SiloProfileListingRequest'), '1.2') arr = [] if r.success r.res.elements.each('SiloProfileListingResponse/SiloProfileSummaries/SiloProfileSummary') do |profile| arr << SiloProfileSummary.parse(profile) end end arr end
Retrieve a list of all users the user is authorized to view or manage.
@return [Array] Array of MultiTenantUserSummary
objects.
# File lib/nexpose/multi_tenant_user.rb, line 10 def list_silo_users r = execute(make_xml('MultiTenantUserListingRequest'), '1.2') arr = [] if r.success r.res.elements.each('MultiTenantUserListingResponse/MultiTenantUserSummaries/MultiTenantUserSummary') do |user| arr << MultiTenantUserSummary.parse(user) end end arr end
Retrieve a list of all silos the user is authorized to view or manage.
@return [Array] Array of SiloSummary
objects.
# File lib/nexpose/silo.rb, line 10 def list_silos r = execute(make_xml('SiloListingRequest'), '1.2') arr = [] if r.success r.res.elements.each('SiloListingResponse/SiloSummaries/SiloSummary') do |silo| arr << SiloSummary.parse(silo) end end arr end
Retrieve a list of all of the assets in a site.
If no site-id is specified, then return all of the assets for the Nexpose
console, grouped by site-id.
@param [FixNum] site_id Site
ID to request device listing for. Optional. @return [Array] Array of devices associated with the site, or
all devices on the console if no site is provided.
# File lib/nexpose/device.rb, line 43 def list_site_devices(site_id = nil) r = execute(make_xml('SiteDeviceListingRequest', { 'site-id' => site_id })) devices = [] if r.success r.res.elements.each('SiteDeviceListingResponse/SiteDevices') do |site| site_id = site.attributes['site-id'].to_i site.elements.each('device') do |device| devices << Device.new(device.attributes['id'].to_i, device.attributes['address'], site_id, device.attributes['riskfactor'].to_f, device.attributes['riskscore'].to_f) end end end devices end
Retrieve a list of all sites the user is authorized to view or manage.
@return [Array] Array of SiteSummary
objects.
# File lib/nexpose/site.rb, line 10 def list_sites r = execute(make_xml('SiteListingRequest')) arr = [] if r.success r.res.elements.each('SiteListingResponse/SiteSummary') do |site| arr << SiteSummary.new(site.attributes['id'].to_i, site.attributes['name'], site.attributes['description'], site.attributes['riskfactor'].to_f, site.attributes['riskscore'].to_f) end end arr end
# File lib/nexpose/ticket.rb, line 6 def list_tickets # TODO: Should take in filters as arguments. xml = make_xml('TicketListingRequest') r = execute(xml, '1.2') tickets = [] if r.success r.res.elements.each('TicketListingResponse/TicketSummary') do |summary| tickets << TicketSummary.parse(summary) end end tickets end
Retrieve a list of all users configured on this console.
@return [Array] Array of users.
# File lib/nexpose/user.rb, line 10 def list_users r = execute(make_xml('UserListingRequest')) arr = [] if r.success r.res.elements.each('UserListingResponse/UserSummary') do |summary| arr << UserSummary.parse(summary) end end arr end
Retrieve a list of the different vulnerability check categories.
@return [Array] Array of currently valid check categories.
# File lib/nexpose/vuln.rb, line 37 def list_vuln_categories data = DataTable._get_dyn_table(self, '/data/vulnerability/categories/dyntable.xml?tableID=VulnCategorySynopsis') data.map { |c| c['Category'] } end
Retrieve all active vulnerability exceptions.
@param [String] status Filter
exceptions by the current status.
@see Nexpose::VulnException::Status
@return [Array] List of matching vulnerability exceptions.
# File lib/nexpose/vuln_exception.rb, line 12 def list_vuln_exceptions(status = nil) unless is_valid_vuln_exception_status?(status) raise "Unknown Status ~> '#{status}' :: For available options refer to Nexpose::VulnException::Status" end status = Nexpose::VulnException::Status.const_get(status_string_to_constant(status)) unless status.nil? results = [] ajax_data = [] url_size = 500 url_page = 0 req = Nexpose::AJAX.get(self, "/api/3/vulnerability_exceptions?size=#{url_size}&page=#{url_page}") data = JSON.parse(req, object_class: OpenStruct) ajax_data << data.resources if data.links.count > 1 loop do url_page += 1 req = Nexpose::AJAX.get(self, "/api/3/vulnerability_exceptions?size=#{url_size}&page=#{url_page}") data = JSON.parse(req, object_class: OpenStruct) ajax_data << data.resources links = data.links.select { |ll| ['self', 'last'].include?(ll.rel) } break if links[0].href == links[1].href end end ajax_data.compact! ajax_data.flatten! ajax_data.each do |vuln_excep| ve = VulnException.new(vuln_excep.scope.vulnerabilityID, vuln_excep.scope.type, vuln_excep.submit.reason, vuln_excep.state) ve.id = vuln_excep.id ve.submitter = vuln_excep.submit.name ve.submitter_comment = vuln_excep.submit.comment ve.submit_date = Time.parse(vuln_excep.submit.date) unless vuln_excep.submit.date.nil? ve.asset_id = vuln_excep.scope.assetID ve.site_id = vuln_excep.scope.siteID ve.asset_group_id = vuln_excep.scope.assetGroupID ve.port = vuln_excep.scope.port ve.vuln_key = vuln_excep.scope.key ve.expiration = Time.parse(vuln_excep.expires) unless vuln_excep.expires.nil? unless vuln_excep.review.nil? ve.reviewer = vuln_excep.review.name ve.reviewer_comment = vuln_excep.review.comment ve.review_date = Time.parse(vuln_excep.review.date) unless vuln_excep.review.date.nil? end results << ve end results.keep_if { |v| v.status == status } unless status.nil? results end
Retrieve summary details of all vulnerabilities.
@param [Boolean] full Whether or not to gather the full summary.
Without the flag, only id, title, and severity are returned. It can take twice a long to retrieve full summary information.
@return [Array] Collection of all known vulnerabilities.
# File lib/nexpose/vuln.rb, line 12 def list_vulns(full = false) xml = make_xml('VulnerabilityListingRequest') # TODO: Add a flag to do stream parsing of the XML to improve performance. response = execute(xml, '1.2') vulns = [] if response.success response.res.elements.each('VulnerabilityListingResponse/VulnerabilitySummary') do |vuln| if full vulns << XML::VulnerabilitySummary.parse(vuln) else vulns << XML::Vulnerability.new(vuln.attributes['id'], vuln.attributes['title'], vuln.attributes['severity'].to_i) end end end vulns end
Establish a new connection and Session ID
# File lib/nexpose/connection.rb, line 99 def login login_hash = { 'sync-id' => 0, 'password' => @password, 'user-id' => @username, 'token' => @token } login_hash['silo-id'] = @silo_id if @silo_id r = execute(make_xml('LoginRequest', login_hash)) if r.success @session_id = r.sid true end rescue APIError raise AuthenticationFailed.new(r) end
Logout of the current connection
# File lib/nexpose/connection.rb, line 112 def logout r = execute(make_xml('LogoutRequest', { 'sync-id' => 0 })) return true if r.success raise APIError.new(r, 'Logout failed') end
Get a history of past scans for this console, sorted by most recent first.
Please note that for consoles with a deep history of scanning, this method could return an excessive amount of data (and take quite a bit of time to retrieve). Consider limiting the amount of data with the optional argument.
@param [Fixnum] limit The maximum number of records to return from this call. @return [Array] List of completed scans, ordered by most
recently completed first.
# File lib/nexpose/scan.rb, line 372 def past_scans(limit = nil) uri = '/data/scan/global/scan-history' rows = AJAX.row_pref_of(limit) params = { 'sort' => 'endTime', 'dir' => 'DESC', 'startIndex' => 0 } AJAX.preserving_preference(self, 'global-completed-scans') do data = DataTable._get_json_table(self, uri, params, rows, limit) data.map(&CompletedScan.method(:parse_json)) end end
Pauses a scan.
@param [Fixnum] scan_id The scan ID.
# File lib/nexpose/scan.rb, line 308 def pause_scan(scan_id) r = execute(make_xml('ScanPauseRequest', 'scan-id' => scan_id)) r.success ? r.attributes['success'] == '1' : false end
Get paused scans. Provide a site ID to get paused scans for a site. With no site ID, all paused scans are returned.
@param [Fixnum] site_id Site
ID to retrieve paused scans for. @param [Fixnum] limit The maximum number of records to return from this call. @return [Array] List of paused scans.
# File lib/nexpose/scan.rb, line 389 def paused_scans(site_id = nil, limit = nil) if site_id uri = "/data/scan/site/#{site_id}?status=active" rows = AJAX.row_pref_of(limit) params = { 'sort' => 'endTime', 'dir' => 'DESC', 'startIndex' => 0 } AJAX.preserving_preference(self, 'site-active-scans') do data = DataTable._get_json_table(self, uri, params, rows, limit).select { |scan| scan['paused'] } data.map(&ActiveScan.method(:parse_json)) end else uri = '/data/site/scans/dyntable.xml?printDocType=0&tableID=siteScansTable&activeOnly=true' data = DataTable._get_dyn_table(self, uri).select { |scan| (scan['Status'].include? 'Paused') } data.map(&ActiveScan.method(:parse_dyntable)) end end
Recall a vulnerability exception. Recall is used by a submitter to undo an exception request that has not been approved yet.
You can only recall a vulnerability exception that has 'Under Review' status.
@param [Fixnum] id Unique identifier of the exception to resubmit. @return [Boolean] Whether or not the recall was accepted by the console.
# File lib/nexpose/vuln_exception.rb, line 99 def recall_vuln_exception(id) xml = make_xml('VulnerabilityExceptionRecallRequest', { 'exception-id' => id }) execute(xml, '1.2').success end
Remove (or delete) one or more assets from a site. With asset linking enabled, this will remove the association of an asset from the given site. If this is the only site of which an asset is a member, the asset will be deleted. If asset linking is disabled, the assets will be deleted.
@param [Array] asset_ids The asset IDs to be removed from the site. @param [Fixnum] site_id The site ID to remove the assets from.
# File lib/nexpose/device.rb, line 159 def remove_assets_from_site(asset_ids, site_id) AJAX.post(self, "/data/assets/bulk-delete?siteid=#{site_id}", asset_ids, Nexpose::AJAX::CONTENT_TYPE::JSON) end
Removes a tag from an asset
@param [Fixnum] asset_id on which to remove tag @param [Fixnum] tag_id to remove from asset
# File lib/nexpose/tag.rb, line 47 def remove_tag_from_asset(asset_id, tag_id) AJAX.delete(self, "/api/2.0/assets/#{asset_id}/tags/#{tag_id}") end
Removes a tag from an asset_group
@param [Fixnum] asset_group_id id of group on which to remove tag @param [Fixnum] tag_id of the tag to remove from asset group
# File lib/nexpose/tag.rb, line 96 def remove_tag_from_asset_group(asset_group_id, tag_id) AJAX.delete(self, "/api/2.0/asset_groups/#{asset_group_id}/tags/#{tag_id}") end
Removes a tag from a site
@param [Fixnum] site_id id of the site on which to remove the tag @param [Fixnum] tag_id id of the tag to remove
# File lib/nexpose/tag.rb, line 71 def remove_tag_from_site(site_id, tag_id) AJAX.delete(self, "/api/2.0/sites/#{site_id}/tags/#{tag_id}") end
Provide a history of all reports generated with the specified report definition.
# File lib/nexpose/report.rb, line 50 def report_history(report_config_id) xml = make_xml('ReportHistoryRequest', { 'reportcfg-id' => report_config_id }) ReportSummary.parse_all(execute(xml)) end
Restart the application.
There is no response to a RestartRequest. When the application shuts down as part of the restart process, it terminates any active connections. Therefore, the application cannot issue a response when it restarts.
# File lib/nexpose/manage.rb, line 79 def restart execute(make_xml('RestartRequest', {})).success end
Resubmit a vulnerability exception request with a new comment and reason after an exception has been rejected.
You can only resubmit a request that has a “Rejected” status; if an exception is “Approved” or “Under Review” you will receive an error message stating that the exception request cannot be resubmitted.
@param [Fixnum] id Unique identifier of the exception to resubmit. @param [String] comment Comment to justify the exception resubmission. @param [String] reason The reason for the exception status, if changing.
@see Nexpose::VulnException::Reason
@return [Boolean] Whether or not the resubmission was valid.
# File lib/nexpose/vuln_exception.rb, line 80 def resubmit_vuln_exception(id, comment, reason = nil) options = { 'exception-id' => id } options['reason'] = reason if reason xml = make_xml('VulnerabilityExceptionResubmitRequest', options) comment_xml = make_xml('comment', {}, comment, false) xml.add_element(comment_xml) r = execute(xml, '1.2') r.success end
Resumes a scan.
@param [Fixnum] scan_id The scan ID.
# File lib/nexpose/scan.rb, line 299 def resume_scan(scan_id) r = execute(make_xml('ScanResumeRequest', 'scan-id' => scan_id), '1.1', timeout: 60) r.success ? r.attributes['success'] == '1' : false end
Reverses the direction of a connection to an engine If the connection is currently initiated from the console this method will have the engine initiate the connection. If the connection is currently initiated by the engine this method with initiate the connection from the console instead. Requires a restart of the console for the connection to be properly established.
@param [Fixnum] engine_id Unique ID of the engine. @return [Boolean] true if the connection is successfully reversed.
# File lib/nexpose/engine.rb, line 28 def reverse_engine_connection(engine_id) uri = "/api/2.1/engine/#{engine_id}/reverseConnection" response = AJAX.put(self, uri) response.eql?('true') end
# File lib/nexpose/role.rb, line 68 def role_delete(role, scope = Scope::SILO) xml = make_xml('RoleDeleteRequest') xml.add_element('Role', { 'name' => role, 'scope' => scope }) response = execute(xml, '1.2') response.success end
Returns a summary list of all roles.
# File lib/nexpose/role.rb, line 54 def role_listing xml = make_xml('RoleListingRequest') r = execute(xml, '1.2') roles = [] if r.success r.res.elements.each('RoleListingResponse/RoleSummary') do |summary| roles << RoleSummary.parse(summary) end end roles end
Retrieve a list of current scan activities across all Scan
Engines managed by Nexpose
.
@return [Array] Array of ScanSummary
objects associated with
each active scan on the engines.
# File lib/nexpose/scan.rb, line 337 def scan_activity r = execute(make_xml('ScanActivityRequest')) res = [] if r.success r.res.elements.each('//ScanSummary') do |scan| res << ScanSummary.parse(scan) end end res end
Perform an ad hoc scan of a single asset of a site at a specific time
@param [Fixnum] site_id Site
ID that the assets belong to. @param [HostName|IPRange] asset Asset
to scan. @param [Array] list of scheduled times at which to run @return [Status] whether the request was successful
# File lib/nexpose/scan.rb, line 93 def scan_asset_with_schedule(site_id, asset, schedule) scan_assets_with_schedule(site_id, [asset], schedule) end
Perform an ad hoc scan of a subset of assets for a site. Only assets from a single site should be submitted per request. Method is designed to take objects filtered from Site#assets.
For example:
site = Site.load(nsc, 5) nsc.scan_assets(5, site.assets.take(10))
@param [Fixnum] site_id Site
ID that the assets belong to. @param [Array] assets List of assets to scan. @return [Scan] Scan
launch information.
# File lib/nexpose/scan.rb, line 109 def scan_assets(site_id, assets) xml = make_xml('SiteDevicesScanRequest', 'site-id' => site_id) hosts = REXML::Element.new('Hosts') assets.each { |asset| _append_asset!(hosts, asset) } xml.add_element(hosts) _scan_ad_hoc(xml) end
Perform an ad hoc scan of a subset of assets for a site by adding a specific runtime. Only assets from a single site should be submitted per request. Method is designed to take objects filtered from Site#assets.
For example:
site = Site.load(nsc, 5) nsc.scan_assets_with_schedule(5, site.assets.take(10), schedules)
@param [Fixnum] site_id Site
ID that the assets belong to. @param [Array] assets List of assets to scan. @param [Array] list of scheduled times at which to run @return [Status] whether the request was successful
# File lib/nexpose/scan.rb, line 131 def scan_assets_with_schedule(site_id, assets, schedules) xml = make_xml('SiteDevicesScanRequest', 'site-id' => site_id) hosts = REXML::Element.new('Hosts') assets.each { |asset| _append_asset!(hosts, asset) } xml.add_element(hosts) scheds = REXML::Element.new('Schedules') schedules.each { |sched| scheds.add_element(sched.as_xml) } xml.add_element(scheds) _scan_ad_hoc_with_schedules(xml) end
Initiate an ad-hoc scan on a subset of site assets with a specific scan template and scan engine, which may differ from the site's defined scan template and scan engine.
@param [Fixnum] site_id Site
ID to scan. @param [Array] assets Hostnames and/or IP addresses to scan. @param [String] scan_template The scan template ID. @param [Fixnum] scan_engine The scan engine ID. @return [Fixnum] Scan
ID.
# File lib/nexpose/scan.rb, line 219 def scan_assets_with_template_and_engine(site_id, assets, scan_template, scan_engine) uri = "/data/site/#{site_id}/scan" assets.size > 1 ? addresses = assets.join(',') : addresses = assets.first params = { 'addressList' => addresses, 'template' => scan_template, 'scanEngine' => scan_engine } scan_id = AJAX.form_post(self, uri, params) scan_id.to_i end
Perform an ad hoc scan of a single device at a specific time.
@param [Device] device Device
to scan. @param [Array] list of scheduled times at which to run @return [Status] whether the request was successful
# File lib/nexpose/scan.rb, line 20 def scan_device_with_schedule(device, schedule) scan_devices_with_schedule([device], schedule) end
Perform an ad hoc scan of a subset of devices for a site. Nexpose
only allows devices from a single site to be submitted per request. Method is designed to take objects from a Device
listing.
For example:
devices = nsc.devices(5) nsc.scan_devices(devices.take(10))
@param [Array] devices List of devices to scan. @return [Scan] Scan
launch information.
# File lib/nexpose/scan.rb, line 36 def scan_devices(devices) site_id = devices.map(&:site_id).uniq.first xml = make_xml('SiteDevicesScanRequest', 'site-id' => site_id) elem = REXML::Element.new('Devices') devices.each do |device| elem.add_element('device', 'id' => "#{device.id}") end xml.add_element(elem) _scan_ad_hoc(xml) end
Perform an ad hoc scan of a subset of devices for a site. Nexpose
only allows devices from a single site to be submitted per request. Method is designed to take objects from a Device
listing.
For example:
devices = nsc.devices(5) nsc.scan_devices(devices.take(10))
@param [Array] devices List of devices to scan. @param [Array] list of scheduled times at which to run @return [Status] whether the request was successful
# File lib/nexpose/scan.rb, line 61 def scan_devices_with_schedule(devices, schedules) site_id = devices.map(&:site_id).uniq.first xml = make_xml('SiteDevicesScanRequest', 'site-id' => site_id) elem = REXML::Element.new('Devices') devices.each do |device| elem.add_element('device', 'id' => "#{device.id}") end xml.add_element(elem) scheds = REXML::Element.new('Schedules') schedules.each { |sched| scheds.add_element(sched.as_xml) } xml.add_element(scheds) _scan_ad_hoc_with_schedules(xml) end
Perform an ad hoc scan of a subset of IP addresses for a site. Only IPs from a single site can be submitted per request, and IP addresses must already be included in the site configuration. Method is designed for scanning when the targets are coming from an external source that does not have access to internal identfiers.
For example:
to_scan = ['192.168.2.1', '192.168.2.107'] nsc.scan_ips(5, to_scan)
@param [Fixnum] site_id Site
ID that the assets belong to. @param [Array] ip_addresses Array of IP addresses to scan. @return [Scan] Scan
launch information.
# File lib/nexpose/scan.rb, line 185 def scan_ips(site_id, ip_addresses) xml = make_xml('SiteDevicesScanRequest', 'site-id' => site_id) hosts = REXML::Element.new('Hosts') ip_addresses.each do |ip| xml.add_element('range', 'from' => ip) end xml.add_element(hosts) _scan_ad_hoc(xml) end
Perform an ad hoc scan of a subset of IP addresses for a site at a specific time. Only IPs from a single site can be submitted per request, and IP addresses must already be included in the site configuration. Method is designed for scanning when the targets are coming from an external source that does not have access to internal identfiers.
For example:
to_scan = ['192.168.2.1', '192.168.2.107'] nsc.scan_ips(5, to_scan)
@param [Fixnum] site_id Site
ID that the assets belong to. @param [Array] ip_addresses Array of IP addresses to scan. @return [Status] whether the request was successful
# File lib/nexpose/scan.rb, line 157 def scan_ips_with_schedule(site_id, ip_addresses, schedules) xml = make_xml('SiteDevicesScanRequest', 'site-id' => site_id) hosts = REXML::Element.new('Hosts') ip_addresses.each do |ip| xml.add_element('range', 'from' => ip) end xml.add_element(hosts) scheds = REXML::Element.new('Schedules') schedules.each { |sched| scheds.add_element(sched.as_xml) } xml.add_element(scheds) _scan_ad_hoc_with_schedules(xml) end
Initiate a site scan.
@param [Fixnum] site_id Site
ID to scan. @param [Boolean] blackout_override Optional. Given suffencent permissions, force bypass blackout and start scan. @return [Scan] Scan
launch information.
# File lib/nexpose/scan.rb, line 202 def scan_site(site_id, blackout_override = false) xml = make_xml('SiteScanRequest', 'site-id' => site_id) xml.add_attributes({ 'force' => true }) if blackout_override response = execute(xml) Scan.parse(response.res) if response.success end
Get scan statistics, including node and vulnerability breakdowns.
@param [Fixnum] scan_id Scan
ID to retrieve statistics for. @return [ScanSummary] ScanSummary
object providing statistics for the scan.
# File lib/nexpose/scan.rb, line 353 def scan_statistics(scan_id) r = execute(make_xml('ScanStatisticsRequest', 'scan-id' => scan_id)) if r.success ScanSummary.parse(r.res.elements['//ScanSummary']) else false end end
Retrieve the status of a scan.
@param [Fixnum] scan_id The scan ID. @return [String] Current status of the scan. See Nexpose::Scan::Status
.
# File lib/nexpose/scan.rb, line 290 def scan_status(scan_id) r = execute(make_xml('ScanStatusRequest', 'scan-id' => scan_id)) r.success ? r.attributes['status'] : nil end
Perform a search that will match the criteria provided.
For example, the following call will return assets with Java and .NET:
java_criterion = Criterion.new(Search::Field::SOFTWARE, Search::Operator::CONTAINS, 'java') dot_net_criterion = Criterion.new(Search::Field::SOFTWARE, Search::Operator::CONTAINS, '.net') criteria = Criteria.new([java_criterion, dot_net_criterion]) results = nsc.search(criteria)
@param [Criteria] criteria Criteria
search object. @return [Array] List of matching assets.
# File lib/nexpose/filter.rb, line 36 def search(criteria) results = DataTable._get_json_table(self, '/data/asset/filterAssets', criteria._to_payload) results.map { |a| FilteredAsset.new(a) } end
Returns the criticality value which takes precedent for an asset
@param [Fixnum] asset_id id of asset on which criticality tag is selected @return [String] selected_criticality string of the relevant criticality; nil if not tagged
# File lib/nexpose/tag.rb, line 106 def selected_criticality_tag(asset_id) selected_criticality = AJAX.get(self, "/data/asset/#{asset_id}/selected-criticality-tag") selected_criticality.empty? ? nil : JSON.parse(selected_criticality)['name'] end
Output diagnostic information into log files, zip the files, and encrypt the archive with a PGP public key that is provided as a parameter for the API call. Then upload the archive using HTTPS to a URL that is specified as an API parameter.
@param uri Upload server to send the support log package to.
# File lib/nexpose/manage.rb, line 90 def send_log(uri = 'https://support.rapid7.com') url = REXML::Element.new('URL') url.text = uri tpt = REXML::Element.new('Transport') tpt.add_attribute('protocol', 'https') tpt << url xml = make_xml('SendLogRequest') xml << tpt execute(xml).success end
Retrieve a list of all previous scans of the site.
@param [FixNum] site_id Site
ID to request scan history for. @return [Array] Array of ScanSummary
objects representing
each scan run to date on the site provided.
# File lib/nexpose/site.rb, line 42 def site_scan_history(site_id) r = execute(make_xml('SiteScanHistoryRequest', { 'site-id' => site_id })) scans = [] if r.success r.res.elements.each('SiteScanHistoryResponse/ScanSummary') do |scan_event| scans << ScanSummary.parse(scan_event) end end scans end
Induce the application to retrieve required updates and restart if necessary.
# File lib/nexpose/manage.rb, line 68 def start_update execute(make_xml('StartUpdateRequest', {})).success end
Stop a running or paused scan.
@param [Fixnum] scan_id ID of the scan to stop. @param [Fixnum] wait_sec Number of seconds to wait for status to be
updated.
# File lib/nexpose/scan.rb, line 271 def stop_scan(scan_id, wait_sec = 0) r = execute(make_xml('ScanStopRequest', 'scan-id' => scan_id)) if r.success so_far = 0 while so_far < wait_sec status = scan_status(scan_id) return status if status == 'stopped' sleep 5 so_far += 5 end end r.success end
Obtain system data, such as total RAM, free RAM, total disk space, free disk space, CPU speed, number of CPU cores, and other vital information.
# File lib/nexpose/manage.rb, line 32 def system_information r = execute(make_xml('SystemInformationRequest', {})) if r.success res = {} r.res.elements.each('//Statistic') do |stat| res[stat.attributes['name'].to_s] = stat.text.to_s end res else false end end
Kicks off an update on a single engine. A return result of true should be taken only to mean that the update was sent, not that it correctly applied.
Nexpose::APIError
will be raised if the engine is already updating, or if the engine is offline or unresponsive.
@param [Fixnum] engine_id Unique ID of the engine. @return [Boolean] true if the update was sent
or if engine is already up to date.
# File lib/nexpose/engine.rb, line 45 def update_engine(engine_id) uri = "/data/engine/#{engine_id}/update" AJAX.post(self, uri) end
Retrieve details for a vulnerability.
@param [String] vuln_id Nexpose
vulnerability ID, such as 'windows-duqu-cve-2011-3402'. @return [VulnerabilityDetail] Details of the requested vulnerability.
# File lib/nexpose/vuln.rb, line 59 def vuln_details(vuln_id) xml = make_xml('VulnerabilityDetailsRequest', { 'vuln-id' => vuln_id }) response = execute(xml, '1.2') if response.success response.res.elements.each('VulnerabilityDetailsResponse/Vulnerability') do |vuln| return XML::VulnerabilityDetail.parse(vuln) end end end
Retrieve a list of the different vulnerability check types.
@return [Array] Array of currently valid check types.
# File lib/nexpose/vuln.rb, line 48 def vuln_types data = DataTable._get_dyn_table(self, '/data/vulnerability/checktypes/dyntable.xml?tableID=VulnCheckCategorySynopsis') data.map { |c| c['Category'] } end
Private Instance Methods
# File lib/nexpose/connection.rb, line 159 def create_trust_store(trust_cert) store = OpenSSL::X509::Store.new store.trust store.add_cert(OpenSSL::X509::Certificate.new(trust_cert)) store end
# File lib/nexpose/vuln_exception.rb, line 118 def is_valid_vuln_exception_status?(status) return true if status.nil? valid_status = [] Nexpose::VulnException::Status.constants.each { |con| valid_status << Nexpose::VulnException::Status.const_get(con) } valid_status << Nexpose::VulnException::Status.constants.map(&:to_s).map(&:downcase) valid_status.flatten.map(&:downcase).include?(status.downcase) end
# File lib/nexpose/vuln_exception.rb, line 126 def status_string_to_constant(status) Nexpose::VulnException::Status.constants.find do |name| Nexpose::VulnException::Status.const_get(name).to_s.downcase == status.downcase || status.to_sym.downcase == name.downcase end end