Class: Nexpose::Connection

Inherits:
Object
  • Object
show all
Includes:
XMLUtils
Defined in:
lib/nexpose/filter.rb,
lib/nexpose/tag.rb,
lib/nexpose/pool.rb,
lib/nexpose/role.rb,
lib/nexpose/scan.rb,
lib/nexpose/silo.rb,
lib/nexpose/site.rb,
lib/nexpose/user.rb,
lib/nexpose/vuln.rb,
lib/nexpose/group.rb,
lib/nexpose/maint.rb,
lib/nexpose/device.rb,
lib/nexpose/engine.rb,
lib/nexpose/manage.rb,
lib/nexpose/report.rb,
lib/nexpose/ticket.rb,
lib/nexpose/external.rb,
lib/nexpose/vuln_def.rb,
lib/nexpose/discovery.rb,
lib/nexpose/connection.rb,
lib/nexpose/silo_profile.rb,
lib/nexpose/scan_template.rb,
lib/nexpose/vuln_exception.rb,
lib/nexpose/report_template.rb,
lib/nexpose/multi_tenant_user.rb,
lib/nexpose/shared_credential.rb

Overview

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.

# Check Session ID
if nsc.session_id
    puts 'Login Successful'
else
    puts 'Login Failure'
end

# Logout
logout_success = nsc.logout

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from XMLUtils

#make_xml, #parse_xml, success?

Constructor Details

#initialize(ip, user, pass, port = 3780, silo_id = nil, token = nil, trust_cert = nil) ⇒ Connection

A constructor for Connection

Parameters:

  • ip (String)

    The IP address or hostname/FQDN of the Nexpose console.

  • user (String)

    The username for Nexpose sessions.

  • pass (String)

    The password for Nexpose sessions.

  • port (Fixnum) (defaults to: 3780)

    The port number of the Nexpose console.

  • silo_id (String) (defaults to: nil)

    The silo identifier for Nexpose sessions.

  • token (String) (defaults to: nil)

    The two-factor authentication (2FA) token for Nexpose sessions.

  • trust_cert (String) (defaults to: nil)

    The PEM-formatted web certificate of the Nexpose console. Used for SSL validation.



84
85
86
87
88
89
90
91
92
93
94
95
96
# 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

Instance Attribute Details

#hostObject (readonly)

The hostname or IP Address of the NSC



43
44
45
# File 'lib/nexpose/connection.rb', line 43

def host
  @host
end

#open_timeoutObject

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



67
68
69
# File 'lib/nexpose/connection.rb', line 67

def open_timeout
  @open_timeout
end

#passwordObject (readonly)

The password used to login to the NSC



49
50
51
# File 'lib/nexpose/connection.rb', line 49

def password
  @password
end

#portObject (readonly)

The port of the NSC (default is 3780)



45
46
47
# File 'lib/nexpose/connection.rb', line 45

def port
  @port
end

#request_xmlObject (readonly)

The last XML request sent by this object, useful for debugging.



55
56
57
# File 'lib/nexpose/connection.rb', line 55

def request_xml
  @request_xml
end

#response_xmlObject (readonly)

The last XML response received by this object, useful for debugging.



57
58
59
# File 'lib/nexpose/connection.rb', line 57

def response_xml
  @response_xml
end

#session_idObject (readonly)

Session ID of this connection



41
42
43
# File 'lib/nexpose/connection.rb', line 41

def session_id
  @session_id
end

#timeoutObject

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



63
64
65
# File 'lib/nexpose/connection.rb', line 63

def timeout
  @timeout
end

#tokenObject (readonly)

The token used to login to the NSC



53
54
55
# File 'lib/nexpose/connection.rb', line 53

def token
  @token
end

#trust_storeObject (readonly)

The trust store to validate connections against if any



59
60
61
# File 'lib/nexpose/connection.rb', line 59

def trust_store
  @trust_store
end

#urlObject (readonly)

The URL for communication



51
52
53
# File 'lib/nexpose/connection.rb', line 51

def url
  @url
end

#usernameObject (readonly)

The username used to login to the NSC



47
48
49
# File 'lib/nexpose/connection.rb', line 47

def username
  @username
end

Class Method Details

.from_uri(uri, user, pass, silo_id = nil, token = nil, trust_cert = nil) ⇒ Object

A constructor to load a Connection object from a URI



70
71
72
73
# 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

Instance Method Details

#_append_asset!(xml, asset) ⇒ Object

Utility method for appending a HostName or IPRange object into an XML object, in preparation for ad hoc scanning.

Parameters:

  • xml (REXML::Document)

    Prepared API call to execute.

  • asset (HostName|IPRange)

    Asset to append to XML.



235
236
237
238
239
240
241
242
243
# 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

#_maintenance_restartObject



59
60
61
62
63
64
65
# 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

#_scan_ad_hoc(xml) ⇒ Scan

Utility method for executing prepared XML and extracting Scan launch information.

Parameters:

  • xml (REXML::Document)

    Prepared API call to execute.

Returns:

  • (Scan)

    Scan launch information.



251
252
253
254
# File 'lib/nexpose/scan.rb', line 251

def _scan_ad_hoc(xml)
  r = execute(xml, '1.1', timeout: 60)
  Scan.parse(r.res)
end

#_scan_ad_hoc_with_schedules(xml) ⇒ Object

Utility method for executing prepared XML for adhoc with schedules

Parameters:

  • xml (REXML::Document)

    Prepared API call to execute.



260
261
262
263
# File 'lib/nexpose/scan.rb', line 260

def _scan_ad_hoc_with_schedules(xml)
  r = execute(xml, '1.1', timeout: 60)
  r.success
end

#activityArray[ScanData]

Retrieve a list of current scan activities across all Scan Engines managed by Nexpose. This method returns lighter weight objects than scan_activity.

Returns:

  • (Array[ScanData])

    Array of ScanData objects associated with each active scan on the engines.



320
321
322
323
324
325
326
327
328
329
# 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

#all_vulnsArray[VulnerabilityDefinition]

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.

Returns:



10
11
12
13
14
15
# 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

#asset_group_tags(asset_group_id) ⇒ Array[TagSummary] Also known as: group_tags, list_asset_group_tags

Lists all the tags on an asset_group

Parameters:

  • asset_group_id (Fixnum)

    id of the group on which tags are listed

Returns:

  • (Array[TagSummary])

    list of tags on asset group



80
81
82
83
84
85
86
87
# File 'lib/nexpose/tag.rb', line 80

def asset_group_tags(asset_group_id)
  tag_summary = []
  asset_group_tag = JSON.parse(AJAX.get(self, "/api/2.0/asset_groups/#{asset_group_id}/tags", AJAX::CONTENT_TYPE::JSON, { per_page: 2_147_483_647 }))
  asset_group_tag['resources'].each do |json|
    tag_summary << TagSummary.parse(json)
  end
  tag_summary
end

#asset_scan_history(asset_id) ⇒ Array[AssetScan]

Retrieve the scan history for an asset. Note: This is not optimized for querying many assets.

Parameters:

  • asset_id (Fixnum)

    Unique identifer of an asset.

Returns:

  • (Array[AssetScan])

    A list of scans for the asset.



142
143
144
145
146
147
148
149
# 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

#asset_tags(asset_id) ⇒ Array[TagSummary] Also known as: list_asset_tags

Lists all the tags on an asset

Parameters:

  • asset_id (Fixnum)

    of the asset to list the applied tags for

Returns:



32
33
34
35
36
37
38
39
# File 'lib/nexpose/tag.rb', line 32

def asset_tags(asset_id)
  tag_summary = []
  asset_tag   = JSON.parse(AJAX.get(self, "/api/2.0/assets/#{asset_id}/tags", AJAX::CONTENT_TYPE::JSON, { per_page: 2_147_483_647 }))
  asset_tag['resources'].select { |r| r['asset_ids'].find { |i| i == asset_id } }.each do |json|
    tag_summary << TagSummary.parse(json)
  end
  tag_summary
end

#backup(platform_independent = false, description = nil) ⇒ Boolean

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.

Parameters:

  • platform_independent (Boolean) (defaults to: false)

    Whether to make a platform independent backup.

  • description (String) (defaults to: nil)

    A note about this backup which will be visible through the web interface.

Returns:

  • (Boolean)

    Whether a backup is successfully initiated.



24
25
26
27
28
29
30
31
32
33
# 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

#completed_assets(scan_id) ⇒ Array[CompletedAsset]

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.

Parameters:

  • scan_id (Fixnum)

    Unique identifier of a scan.

Returns:



106
107
108
109
110
111
112
# 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

#completed_scans(site_id) ⇒ CompletedScan

Retrieve a history of the completed scans for a given site.

Parameters:

  • site_id (FixNum)

    Site ID to find scans for.

Returns:

  • (CompletedScan)

    details of the completed scans for the site.



70
71
72
73
74
# 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

#console_command(cmd_string) ⇒ Object

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.



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 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

#db_maintenance(clean_up = false, compress = false, reindex = false) ⇒ Boolean

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.

Parameters:

  • clean_up (Boolean) (defaults to: false)

    Removes any unnecessary data from the database.

  • compress (Boolean) (defaults to: false)

    Compresses the database tables and reclaims unused, allocated space.

  • reindex (Boolean) (defaults to: false)

    Drops and recreates the database indexes for improved performance.

Returns:

  • (Boolean)

    Whether a maintenance tasks are successfully initiated.



47
48
49
50
51
52
53
54
55
56
57
# 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_asset_group(id) ⇒ Boolean Also known as: delete_group

Delete an asset group and all associated data.

Parameters:

  • id (Fixnum)

    Asset group ID to delete.

Returns:

  • (Boolean)

    Whether group deletion succeeded.



12
13
14
15
# File 'lib/nexpose/group.rb', line 12

def delete_asset_group(id)
  r = execute(make_xml('AssetGroupDeleteRequest', { 'group-id' => id }))
  r.success
end

#delete_device(device_id) ⇒ Object Also known as: delete_asset



129
130
131
132
# 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_discovery_connection(id) ⇒ Object

Delete an existing connection to a target used for dynamic discovery of assets.

Parameters:

  • id (Fixnum)

    ID of an existing discovery connection.



23
24
25
26
27
# 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

#delete_engine(engine_id, scope = 'silo') ⇒ Boolean

Removes a scan engine from the list of available engines.

Parameters:

  • engine_id (Fixnum)

    Unique ID of an existing engine to remove.

  • scope (String) (defaults to: 'silo')

    Whether the engine is global or silo scoped.

Returns:

  • (Boolean)

    true if engine successfully deleted.



12
13
14
15
16
# 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_report(report_id) ⇒ Object

Delete a previously generated report.

Parameters:

  • report_id (Fixnum)

    ID of individual report to delete.



65
66
67
68
# 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_report_config(report_config_id) ⇒ Object

Delete a previously generated report definition. Also deletes any reports generated from that configuration.

Parameters:

  • report_config_id (Fixnum)

    ID of the report configuration to remove.



75
76
77
78
# 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

#delete_report_template(template_id) ⇒ Object

Deletes an existing, custom report template. Cannot delete built-in templates.

Parameters:

  • template_id (String)

    Unique identifier of the report template to remove.



29
30
31
# 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_scan(scan_id) ⇒ Object

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.

Parameters:

  • scan_id (Fixnum)

    Scan ID to remove data for.



480
481
482
# File 'lib/nexpose/scan.rb', line 480

def delete_scan(scan_id)
  AJAX.delete(self, "/data/scan/#{scan_id}")
end

#delete_scan_template(id) ⇒ Object

Delete a scan template from the console. Cannot be used to delete a built-in template.

Parameters:

  • id (String)

    Unique identifier of an existing scan template.



21
22
23
# File 'lib/nexpose/scan_template.rb', line 21

def delete_scan_template(id)
  AJAX.delete(self, "/data/scan/templates/#{URI.encode(id)}")
end

#delete_shared_credential(id) ⇒ Object Also known as: delete_shared_cred



17
18
19
# File 'lib/nexpose/shared_credential.rb', line 17

def delete_shared_credential(id)
  AJAX.post(self, "/data/credential/shared/delete?credid=#{id}")
end

#delete_silo(silo_id) ⇒ Object

Delete the specified silo

Returns:

  • Whether or not the delete request succeeded.



27
28
29
30
# 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_silo_profile(silo_profile_id) ⇒ Object

Delete the specified silo profile

Returns:

  • Whether or not the delete request succeeded.



27
28
29
30
# 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_silo_user(user_id) ⇒ Object

Delete the specified silo user

Returns:

  • Whether or not the delete request succeeded.



26
27
28
29
# 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_site(site_id) ⇒ Object

Delete the specified site and all associated scan data.

Returns:

  • Whether or not the delete request succeeded.



31
32
33
34
# File 'lib/nexpose/site.rb', line 31

def delete_site(site_id)
  r = execute(make_xml('SiteDeleteRequest', { 'site-id' => site_id }))
  r.success
end

#delete_tag(tag_id) ⇒ Object

Deletes a tag by ID

Parameters:

  • tag_id (Fixnum)

    ID of tag to delete



23
24
25
# File 'lib/nexpose/tag.rb', line 23

def delete_tag(tag_id)
  AJAX.delete(self, "/api/2.0/tags/#{tag_id}")
end

#delete_ticket(ticket) ⇒ Boolean

Deletes a Nexpose ticket.

Parameters:

  • ticket (Fixnum)

    Unique ID of the ticket to delete.

Returns:

  • (Boolean)

    Whether or not the ticket deletion succeeded.



26
27
28
29
# File 'lib/nexpose/ticket.rb', line 26

def delete_ticket(ticket)
  # TODO: Take Ticket object, too, and pull out IDs.
  delete_tickets([ticket])
end

#delete_tickets(tickets) ⇒ Boolean

Deletes a Nexpose ticket.

Parameters:

  • tickets (Array[Fixnum])

    Array of unique IDs of tickets to delete.

Returns:

  • (Boolean)

    Whether or not the ticket deletions succeeded.



36
37
38
39
40
41
42
43
44
# 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_user(user_id) ⇒ Boolean

Delete a user from the Nexpose console.

Parameters:

  • user_id (Fixnum)

    Unique ID for the user to delete.

Returns:

  • (Boolean)

    Whether or not the user deletion succeeded.



36
37
38
39
# File 'lib/nexpose/user.rb', line 36

def delete_user(user_id)
  response = execute(make_xml('UserDeleteRequest', { 'id' => user_id }))
  response.success
end

#delete_vuln_exception(id) ⇒ Boolean

Delete an existing vulnerability exception.

Parameters:

  • id (Fixnum)

    The ID of a vuln exception.

Returns:

  • (Boolean)

    Whether or not deletion was successful.



110
111
112
113
114
# 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(url, file_name = nil) ⇒ Object

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.


135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# 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

#engine_activity(engine_id) ⇒ Array[ScanSummary]

Provide a list of current scan activities for a specific Scan Engine.

Returns:

  • (Array[ScanSummary])

    Array of ScanSummary objects associated with each active scan on the engine.



55
56
57
58
59
60
61
62
63
64
65
# 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

#engine_versionsObject

Obtain the version information for each scan engine. Includes Product, Content, and Java versions.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# 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(xml, version = '1.1', options = {}) ⇒ Object

Execute an API request



119
120
121
122
123
124
125
126
127
# 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_scan(scan_id, zip_file = nil) ⇒ Fixnum

Export the data associated with a single scan, and optionally store it in a zip-compressed file under the provided name.

Parameters:

  • scan_id (Fixnum)

    Scan ID to remove data for.

  • zip_file (String) (defaults to: nil)

    Filename to export scan data to.

Returns:

  • (Fixnum)

    On success, returned the number of bytes written to zip_file, if provided. Otherwise, returns raw ZIP binary data.



413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
# 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

#filter(field, operator, value = '') ⇒ Array[FilteredAsset]

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')

Parameters:

  • field (String)

    Constant from Search::Field

  • operator (String)

    Constant from Search::Operator

  • value (String) (defaults to: '')

    Search term or constant from Search::Value

Returns:



15
16
17
18
19
# 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_device_by_address(address, site_id = nil) ⇒ Device Also known as: find_asset_by_address

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.

Parameters:

  • address (String)

    Address of the device to find. Usually the IP address.

  • site_id (FixNum) (defaults to: nil)

    Site ID to restrict search to.

Returns:

  • (Device)

    The first matching Device with the provided address, if found.



17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 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

#find_vuln_check(search_term, partial_words = true, all_words = true) ⇒ Array[VulnCheck]

Search for Vulnerability Checks.

Parameters:

  • search_term (String)

    Search terms to search for.

  • partial_words (Boolean) (defaults to: true)

    Allow partial word matches.

  • all_words (Boolean) (defaults to: true)

    All words must be present.

Returns:

  • (Array[VulnCheck])

    List of matching Vulnerability Checks.



76
77
78
79
80
81
82
# 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

#find_vulns_by_cve(cve) ⇒ Array[VulnerabilityDefinition]

Search for any vulnerability definitions which refer to a given CVE.

Parameters:

  • cve (String)

    A valid CVE.

Returns:



22
23
24
25
26
27
# 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_vulns_by_date(from, to = nil) ⇒ Array[VulnSynopsis]

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.

Parameters:

  • from (String)

    Vulnerability publish date in format YYYY-MM-DD.

  • to (String) (defaults to: nil)

    Vulnerability publish date in format YYYY-MM-DD.

Returns:

  • (Array[VulnSynopsis])

    List of vulnerabilities published in Nexpose between the provided dates.



93
94
95
96
97
# 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

#find_vulns_by_ref(source, id) ⇒ Array[VulnerabilityDefinition]

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)

Parameters:

  • source (String)

    External vulnerability reference source.

  • id (String)

    Unique vulnerability reference ID.

Returns:



42
43
44
45
46
47
48
49
50
# 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

#find_vulns_by_title(title, all_words = true) ⇒ Array[VulnerabilityDefinition]

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.

Parameters:

  • title (String)

    A (partial) title to search for.

  • all_words (Boolean) (defaults to: true)

    Whether to include all words from the search phrase in the search.

Returns:



64
65
66
67
68
69
70
# 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_report(report_id, wait = false) ⇒ Object

Generate a new report using the specified report definition.



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 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_user_id(user_name) ⇒ Object

Retrieve the User ID based upon the user’s login name.

Parameters:

  • user_name (String)

    User name to search for.



27
28
29
# File 'lib/nexpose/user.rb', line 27

def get_user_id(user_name)
  users.find { |user| user.name.eql? user_name }
end

#group_assets(group_id) ⇒ Array[FilteredAsset]

Get a list of all assets currently associated with a group.

Parameters:

  • group_id (Fixnum)

    Unique identifier of an asset group.

Returns:



72
73
74
75
76
77
78
# 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_assets(site_id, assets) ⇒ Array[ImportResult]

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.

Parameters:

  • site_id (Fixnum)

    Existing site to import assets into.

  • assets (Array[External::Asset])

    External assets to import.

Returns:

  • (Array[ImportResult])

    collection of import results.



18
19
20
21
# 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_assets_from_json(site_id, json) ⇒ Array[ImportResult]

Import external assets into a Nexpose console.

Parameters:

  • site_id (Fixnum)

    Existing site to import assets into.

  • json (String)

    JSON representation of assets to import.

Returns:

  • (Array[ImportResult])

    collection of import results.



29
30
31
32
33
34
35
# 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(site_id, zip_file) ⇒ Fixnum

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.

Parameters:

  • site_id (Fixnum)

    Site ID of the site to import the scan into.

  • zip_file (String)

    Path to a previously exported scan archive.

Returns:

  • (Fixnum)

    The scan ID on success.



446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
# 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

#incomplete_assets(scan_id) ⇒ Array[IncompleteAsset]

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.

Parameters:

  • scan_id (Fixnum)

    Unique identifier of a scan.

Returns:



121
122
123
124
125
126
127
# 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

#last_report(report_config_id) ⇒ Object

Get details of the last report generated with the specified report id.



56
57
58
59
# 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

#last_scan(site_id) ⇒ ScanSummary

Retrieve the scan summary statistics for the latest completed scan on a site.

Method will not return data on an active scan.

Parameters:

  • site_id (FixNum)

    Site ID to find latest scan for.

Returns:

  • (ScanSummary)

    details of the last completed scan for a site.



61
62
63
# 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

#list_asset_groupsArray[AssetGroupSummary] Also known as: groups, asset_groups

Retrieve an array of all asset groups the user is authorized to view or manage.

Returns:



24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 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_backupsArray[Backup]

Retrieve a list of all backups currently stored on the Console.

Returns:

  • (Array[Backup])

    List of backups.



9
10
11
12
# File 'lib/nexpose/maint.rb', line 9

def list_backups
  data = DataTable._get_dyn_table(self, '/data/admin/backups?tableID=BackupSynopsis')
  data.map { |b| Backup.parse(b) }
end

#list_device_vulns(dev_id) ⇒ Array[VulnFinding] Also known as: list_asset_vulns, asset_vulns, device_vulns

List the vulnerability findings for a given device ID.

Parameters:

  • dev_id (Fixnum)

    Unique identifier of a device (asset).

Returns:

  • (Array[VulnFinding])

    List of vulnerability findings.



85
86
87
88
89
90
91
92
# 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

#list_discovery_connectionsObject Also known as: discovery_connections

Retrieve information about all available connections for dynamic discovery of assets, including whether or not connections are active.



8
9
10
11
12
13
14
15
16
# 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

#list_engine_poolsArray[EnginePoolSummary] Also known as: engine_pools

Retrieve a list of all Scan Engine Pools managed by the Security Console.

Returns:

  • (Array[EnginePoolSummary])

    Array of EnginePoolSummary objects associated with each engine associated with this security console.



11
12
13
14
15
16
17
18
19
20
21
22
# 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

#list_enginesArray[EngineSummary] Also known as: engines

Retrieve a list of all Scan Engines managed by the Security Console.

Returns:

  • (Array[EngineSummary])

    Array of EngineSummary objects associated with each engine associated with this security console.



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# 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

#list_report_templatesArray[ReportTemplateSummary] Also known as: report_templates

Provide a list of all report templates the user can access on the Security Console.

Returns:



11
12
13
14
15
16
17
18
19
20
# 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

#list_reportsArray[ReportConfigSummary] Also known as: reports

Provide a listing of all report definitions the user can access on the Security Console.

Returns:



11
12
13
14
15
16
17
18
19
20
# 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_scan_templatesArray[ScanTemplateSummary] Also known as: scan_templates

List the scan templates currently configured on the console.

Returns:



9
10
11
12
# 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

#list_shared_credentialsObject Also known as: list_shared_creds, shared_credentials, shared_creds



5
6
7
8
9
10
11
# File 'lib/nexpose/shared_credential.rb', line 5

def list_shared_credentials
  creds = DataTable._get_json_table(self,
                                    '/data/credential/shared/listing',
                                    { 'sort' => -1,
                                      'table-id' => 'credential-listing' })
  creds.map { |c| SharedCredentialSummary.from_json(c) }
end

#list_silo_profilesArray[SiloProfileSummary] Also known as: silo_profiles

Retrieve a list of all silos the user is authorized to view or manage.

Returns:



10
11
12
13
14
15
16
17
18
19
# 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

#list_silo_usersArray[MultiTenantUserSummary] Also known as: silo_users

Retrieve a list of all users the user is authorized to view or manage.

Returns:



10
11
12
13
14
15
16
17
18
19
# 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

#list_silosArray[SiloSummary] Also known as: silos

Retrieve a list of all silos the user is authorized to view or manage.

Returns:

  • (Array[SiloSummary])

    Array of SiloSummary objects.



10
11
12
13
14
15
16
17
18
19
# 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

#list_site_devices(site_id = nil) ⇒ Array[Device] Also known as: devices, list_devices, assets, list_assets

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.

Parameters:

  • site_id (FixNum) (defaults to: nil)

    Site ID to request device listing for. Optional.

Returns:

  • (Array[Device])

    Array of devices associated with the site, or all devices on the console if no site is provided.



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# 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

#list_sitesArray[SiteSummary] Also known as: sites

Retrieve a list of all sites the user is authorized to view or manage.

Returns:

  • (Array[SiteSummary])

    Array of SiteSummary objects.



10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 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

#list_ticketsObject Also known as: tickets



6
7
8
9
10
11
12
13
14
15
16
17
# 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

#list_usersArray[UserSummary] Also known as: users

Retrieve a list of all users configured on this console.

Returns:



10
11
12
13
14
15
16
17
18
19
# 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

#list_vuln_categoriesArray[String] Also known as: vuln_categories

Retrieve a list of the different vulnerability check categories.

Returns:

  • (Array[String])

    Array of currently valid check categories.



37
38
39
40
# 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

#list_vuln_exceptions(status = nil) ⇒ Array[VulnException] Also known as: vuln_exceptions

Retrieve all active vulnerability exceptions.

Parameters:

  • status (String) (defaults to: nil)

    Filter exceptions by the current status. @see Nexpose::VulnException::Status

Returns:

  • (Array[VulnException])

    List of matching vulnerability exceptions.



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# 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

#list_vulns(full = false) ⇒ Array[Vulnerability|VulnerabilitySummary] Also known as: vulns

Retrieve summary details of all vulnerabilities.

Parameters:

  • full (Boolean) (defaults to: false)

    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.

Returns:

  • (Array[Vulnerability|VulnerabilitySummary])

    Collection of all known vulnerabilities.



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 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

#loginObject

Establish a new connection and Session ID



99
100
101
102
103
104
105
106
107
108
109
# File 'lib/nexpose/connection.rb', line 99

def 
   = { 'sync-id' => 0, 'password' => @password, 'user-id' => @username, 'token' => @token }
  ['silo-id'] = @silo_id if @silo_id
  r = execute(make_xml('LoginRequest', ))
  if r.success
    @session_id = r.sid
    true
  end
rescue APIError
  raise AuthenticationFailed.new(r)
end

#logoutObject

Logout of the current connection

Raises:



112
113
114
115
116
# 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

#past_scans(limit = nil) ⇒ Array[CompletedScan]

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.

Parameters:

  • limit (Fixnum) (defaults to: nil)

    The maximum number of records to return from this call.

Returns:

  • (Array[CompletedScan])

    List of completed scans, ordered by most recently completed first.



372
373
374
375
376
377
378
379
380
# 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

#pause_scan(scan_id) ⇒ Object

Pauses a scan.

Parameters:

  • scan_id (Fixnum)

    The scan ID.



308
309
310
311
# 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

#paused_scans(site_id = nil, limit = nil) ⇒ Array[ActiveScan]

Get paused scans. Provide a site ID to get paused scans for a site. With no site ID, all paused scans are returned.

Parameters:

  • site_id (Fixnum) (defaults to: nil)

    Site ID to retrieve paused scans for.

  • limit (Fixnum) (defaults to: nil)

    The maximum number of records to return from this call.

Returns:



389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
# 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_vuln_exception(id) ⇒ Boolean

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.

Parameters:

  • id (Fixnum)

    Unique identifier of the exception to resubmit.

Returns:

  • (Boolean)

    Whether or not the recall was accepted by the console.



99
100
101
102
103
# 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_assets_from_site(asset_ids, site_id) ⇒ Object

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.

Parameters:

  • asset_ids (Array[Fixnum])

    The asset IDs to be removed from the site.

  • site_id (Fixnum)

    The site ID to remove the assets from.



159
160
161
# 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

#remove_tag_from_asset(asset_id, tag_id) ⇒ Object

Removes a tag from an asset

Parameters:

  • asset_id (Fixnum)

    on which to remove tag

  • tag_id (Fixnum)

    to remove from asset



47
48
49
# 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

#remove_tag_from_asset_group(asset_group_id, tag_id) ⇒ Object Also known as: remove_tag_from_group

Removes a tag from an asset_group

Parameters:

  • asset_group_id (Fixnum)

    id of group on which to remove tag

  • tag_id (Fixnum)

    of the tag to remove from asset group



96
97
98
# 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

#remove_tag_from_site(site_id, tag_id) ⇒ Object

Removes a tag from a site

Parameters:

  • site_id (Fixnum)

    id of the site on which to remove the tag

  • tag_id (Fixnum)

    id of the tag to remove



71
72
73
# 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

#report_history(report_config_id) ⇒ Object

Provide a history of all reports generated with the specified report definition.



50
51
52
53
# 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

#restartObject

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.



79
80
81
# File 'lib/nexpose/manage.rb', line 79

def restart
  execute(make_xml('RestartRequest', {})).success
end

#resubmit_vuln_exception(id, comment, reason = nil) ⇒ Boolean

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.

Parameters:

  • id (Fixnum)

    Unique identifier of the exception to resubmit.

  • comment (String)

    Comment to justify the exception resubmission.

  • reason (String) (defaults to: nil)

    The reason for the exception status, if changing. @see Nexpose::VulnException::Reason

Returns:

  • (Boolean)

    Whether or not the resubmission was valid.



80
81
82
83
84
85
86
87
88
# 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

#resume_scan(scan_id) ⇒ Object

Resumes a scan.

Parameters:

  • scan_id (Fixnum)

    The scan ID.



299
300
301
302
# 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

#reverse_engine_connection(engine_id) ⇒ Boolean

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.

Parameters:

  • engine_id (Fixnum)

    Unique ID of the engine.

Returns:

  • (Boolean)

    true if the connection is successfully reversed.



28
29
30
31
32
# 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

#role_delete(role, scope = Scope::SILO) ⇒ Object Also known as: delete_role



68
69
70
71
72
73
# 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

#role_listingObject Also known as: roles

Returns a summary list of all roles.



54
55
56
57
58
59
60
61
62
63
64
# 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

#scan_activityArray[ScanSummary]

Retrieve a list of current scan activities across all Scan Engines managed by Nexpose.

Returns:

  • (Array[ScanSummary])

    Array of ScanSummary objects associated with each active scan on the engines.



337
338
339
340
341
342
343
344
345
346
# 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

#scan_asset(site_id, asset) ⇒ Scan

Perform an ad hoc scan of a single asset of a site.

Parameters:

  • site_id (Fixnum)

    Site ID that the assets belong to.

  • asset (HostName|IPRange)

    Asset to scan.

Returns:

  • (Scan)

    Scan launch information.



82
83
84
# File 'lib/nexpose/scan.rb', line 82

def scan_asset(site_id, asset)
  scan_assets(site_id, [asset])
end

#scan_asset_with_schedule(site_id, asset, schedule) ⇒ Status

Perform an ad hoc scan of a single asset of a site at a specific time

Parameters:

  • site_id (Fixnum)

    Site ID that the assets belong to.

  • asset (HostName|IPRange)

    Asset to scan.

  • list (Array[adhoc_schedules])

    of scheduled times at which to run

Returns:

  • (Status)

    whether the request was successful



93
94
95
# 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

#scan_assets(site_id, assets) ⇒ Scan

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))

Parameters:

  • site_id (Fixnum)

    Site ID that the assets belong to.

  • assets (Array[HostName|IPRange])

    List of assets to scan.

Returns:

  • (Scan)

    Scan launch information.



109
110
111
112
113
114
115
116
# 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

#scan_assets_with_schedule(site_id, assets, schedules) ⇒ Status

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)

Parameters:

  • site_id (Fixnum)

    Site ID that the assets belong to.

  • assets (Array[HostName|IPRange])

    List of assets to scan.

  • list (Array[adhoc_schedules])

    of scheduled times at which to run

Returns:

  • (Status)

    whether the request was successful



131
132
133
134
135
136
137
138
139
140
141
# 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

#scan_assets_with_template_and_engine(site_id, assets, scan_template, scan_engine) ⇒ Fixnum

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.

Parameters:

  • site_id (Fixnum)

    Site ID to scan.

  • assets (Array[String])

    Hostnames and/or IP addresses to scan.

  • scan_template (String)

    The scan template ID.

  • scan_engine (Fixnum)

    The scan engine ID.

Returns:

  • (Fixnum)

    Scan ID.



219
220
221
222
223
224
225
226
227
# 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

#scan_device(device) ⇒ Scan

Perform an ad hoc scan of a single device.

Parameters:

  • device (Device)

    Device to scan.

Returns:

  • (Scan)

    Scan launch information.



10
11
12
# File 'lib/nexpose/scan.rb', line 10

def scan_device(device)
  scan_devices([device])
end

#scan_device_with_schedule(device, schedule) ⇒ Status

Perform an ad hoc scan of a single device at a specific time.

Parameters:

  • device (Device)

    Device to scan.

  • list (Array[adhoc_schedules])

    of scheduled times at which to run

Returns:

  • (Status)

    whether the request was successful



20
21
22
# File 'lib/nexpose/scan.rb', line 20

def scan_device_with_schedule(device, schedule)
  scan_devices_with_schedule([device], schedule)
end

#scan_devices(devices) ⇒ Scan

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))

Parameters:

  • devices (Array[Device])

    List of devices to scan.

Returns:

  • (Scan)

    Scan launch information.



36
37
38
39
40
41
42
43
44
45
46
# 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

#scan_devices_with_schedule(devices, schedules) ⇒ Status

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))

Parameters:

  • devices (Array[Device])

    List of devices to scan.

  • list (Array[adhoc_schedules])

    of scheduled times at which to run

Returns:

  • (Status)

    whether the request was successful



61
62
63
64
65
66
67
68
69
70
71
72
73
74
# 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

#scan_ips(site_id, ip_addresses) ⇒ Scan

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)

Parameters:

  • site_id (Fixnum)

    Site ID that the assets belong to.

  • ip_addresses (Array[String])

    Array of IP addresses to scan.

Returns:

  • (Scan)

    Scan launch information.



185
186
187
188
189
190
191
192
193
194
# 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

#scan_ips_with_schedule(site_id, ip_addresses, schedules) ⇒ Status

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)

Parameters:

  • site_id (Fixnum)

    Site ID that the assets belong to.

  • ip_addresses (Array[String])

    Array of IP addresses to scan.

Returns:

  • (Status)

    whether the request was successful



157
158
159
160
161
162
163
164
165
166
167
168
169
# 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

#scan_site(site_id, blackout_override = false) ⇒ Scan

Initiate a site scan.

Parameters:

  • site_id (Fixnum)

    Site ID to scan.

  • blackout_override (Boolean) (defaults to: false)

    Optional. Given suffencent permissions, force bypass blackout and start scan.

Returns:

  • (Scan)

    Scan launch information.



202
203
204
205
206
207
# 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

#scan_statistics(scan_id) ⇒ ScanSummary

Get scan statistics, including node and vulnerability breakdowns.

Parameters:

  • scan_id (Fixnum)

    Scan ID to retrieve statistics for.

Returns:

  • (ScanSummary)

    ScanSummary object providing statistics for the scan.



353
354
355
356
357
358
359
360
# 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

#scan_status(scan_id) ⇒ String

Retrieve the status of a scan.

Parameters:

  • scan_id (Fixnum)

    The scan ID.

Returns:

  • (String)

    Current status of the scan. See Nexpose::Scan::Status.



290
291
292
293
# 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

#search(criteria) ⇒ Array[FilteredAsset]

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)

Parameters:

  • criteria (Criteria)

    Criteria search object.

Returns:



36
37
38
39
# 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

#selected_criticality_tag(asset_id) ⇒ String

Returns the criticality value which takes precedent for an asset

Parameters:

  • asset_id (Fixnum)

    id of asset on which criticality tag is selected

Returns:

  • (String)

    selected_criticality string of the relevant criticality; nil if not tagged



106
107
108
109
# 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

#send_log(uri = 'https://support.rapid7.com') ⇒ Object

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.

Parameters:

  • uri (defaults to: 'https://support.rapid7.com')

    Upload server to send the support log package to.



90
91
92
93
94
95
96
97
98
99
100
# 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

#site_scan_history(site_id) ⇒ Array[ScanSummary]

Retrieve a list of all previous scans of the site.

Parameters:

  • site_id (FixNum)

    Site ID to request scan history for.

Returns:

  • (Array[ScanSummary])

    Array of ScanSummary objects representing each scan run to date on the site provided.



42
43
44
45
46
47
48
49
50
51
# 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

#site_tags(site_id) ⇒ Array[TagSummary] Also known as: list_site_tags

Lists all the tags on a site

Parameters:

  • site_id (Fixnum)

    id of the site to get the applied tags

Returns:



56
57
58
59
60
61
62
63
# File 'lib/nexpose/tag.rb', line 56

def site_tags(site_id)
  tag_summary = []
  site_tag = JSON.parse(AJAX.get(self, "/api/2.0/sites/#{site_id}/tags", AJAX::CONTENT_TYPE::JSON, { per_page: 2_147_483_647 }))
  site_tag['resources'].each do |json|
    tag_summary << TagSummary.parse(json)
  end
  tag_summary
end

#start_updateObject

Induce the application to retrieve required updates and restart if necessary.



68
69
70
# File 'lib/nexpose/manage.rb', line 68

def start_update
  execute(make_xml('StartUpdateRequest', {})).success
end

#stop_scan(scan_id, wait_sec = 0) ⇒ Object

Stop a running or paused scan.

Parameters:

  • scan_id (Fixnum)

    ID of the scan to stop.

  • wait_sec (Fixnum) (defaults to: 0)

    Number of seconds to wait for status to be updated.



271
272
273
274
275
276
277
278
279
280
281
282
283
# 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

#system_informationObject

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.



32
33
34
35
36
37
38
39
40
41
42
43
44
# 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

#tagsArray[TagSummary] Also known as: list_tags

Lists all tags

Returns:



9
10
11
12
13
14
15
16
# File 'lib/nexpose/tag.rb', line 9

def tags
  tag_summary = []
  tags        = JSON.parse(AJAX.get(self, '/api/2.0/tags', AJAX::CONTENT_TYPE::JSON, { per_page: 2_147_483_647 }))
  tags['resources'].each do |json|
    tag_summary << TagSummary.parse(json)
  end
  tag_summary
end

#update_engine(engine_id) ⇒ Boolean

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.

Parameters:

  • engine_id (Fixnum)

    Unique ID of the engine.

Returns:

  • (Boolean)

    true if the update was sent or if engine is already up to date.



45
46
47
48
# File 'lib/nexpose/engine.rb', line 45

def update_engine(engine_id)
  uri = "/data/engine/#{engine_id}/update"
  AJAX.post(self, uri)
end

#vuln_details(vuln_id) ⇒ VulnerabilityDetail

Retrieve details for a vulnerability.

Parameters:

  • vuln_id (String)

    Nexpose vulnerability ID, such as ‘windows-duqu-cve-2011-3402’.

Returns:

  • (VulnerabilityDetail)

    Details of the requested vulnerability.



59
60
61
62
63
64
65
66
67
# 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

#vuln_typesArray[String] Also known as: list_vuln_types

Retrieve a list of the different vulnerability check types.

Returns:

  • (Array[String])

    Array of currently valid check types.



48
49
50
51
# 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