Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Refactors Site for usability.

Also adds rspec tests.
  • Loading branch information...
commit 75fa6aa3d1560f71519d9b701334b02f60eb51cd 1 parent d8caa0f
Michael Daines mdaines-r7 authored
4 README.markdown
View
@@ -8,8 +8,10 @@ For assistance with using the gem, to share your scripts, or to discuss differen
This package is currently a work in progress. Most of the Nexpose API is exposed, but there are some operations missing. Also, there are stylistic differences between different calls that we hope to eliminate over time. We welcome contributions to this package. We ask only that pull requests and patches adhere to our coding standards.
-* Unless otherwise noted, code should adhere to the Ruby Style Guide: https://github.com/bbatsov/ruby-style-guide
* Favor returning classes over key-value maps. Classes tend to be easier for users to manipulate and use.
+* Unless otherwise noted, code should adhere to the Ruby Style Guide: https://github.com/bbatsov/ruby-style-guide
+* Use YARDoc comment style to improve the API documentation of the gem.
## Credits
+
Rapid7 LLC
57 lib/nexpose/scan.rb
View
@@ -215,47 +215,6 @@ def initialize(connection, scan_id)
# Object that represents the scanning configuration for a Site.
#
class ScanConfig
- # A unique ID for this scan configuration
- attr_reader :configID
- # The name of the scan template
- attr_reader :name
- # The ID of the scan template used full-audit, exhaustive-audit, web-audit, dos-audit, internet-audit, network-audit
- attr_reader :templateID
- # The configuration version (default is 2)
- attr_reader :configVersion
- attr_accessor :engine_id
- # Array of (Schedule)* ... TODO: There can be only 0 or 1 schedules
- attr_reader :schedules
- # Array of (ScanTrigger)*
- attr_reader :scanTriggers
-
- def initialize(configID, name, templateID, configVersion = 2, engine_id = nil)
- @configID = configID
- @name = name
- @templateID = templateID
- @configVersion = configVersion
- @engine_id = engine_id
- @schedules = []
- @scanTriggers = []
- end
-
- # Adds a new Schedule for this ScanConfig
- def addSchedule(schedule)
- @schedules.push(schedule)
- end
-
- # Adds a new ScanTrigger to the scanTriggers array
- def addScanTrigger(scanTrigger)
- @scanTriggers.push(scanTrigger)
- end
-
- def _set_configID(configID)
- @configID = configID
- end
-
- def _set_name(name)
- @name = name
- end
def self.parse(xml)
config = ScanConfig.new(xml.attributes['configID'],
@@ -270,9 +229,6 @@ def self.parse(xml)
sched.attributes['enabled'])
config.addSchedule(schedule)
end
- # TODO
- # xml.elements.each('ScanTriggers') do |trigger|
- # end
config
end
end
@@ -292,17 +248,4 @@ def initialize(scan_stop, scan_failed, scan_start)
@scanStart = scan_start
end
end
-
- # === Description
- # Object that holds an event that triggers the start of a scan.
- class ScanTrigger
- attr_reader :type, :enabled, :incremental
-
- def initialize(type, incremental, enabled = 1)
- @type = type
- @incremental = incremental
- @enabled = enabled
- end
- end
-
end
553 lib/nexpose/site.rb
View
@@ -11,7 +11,6 @@ def site_device_listing(site_id)
if (r.success)
res = []
r.res.elements.each("//device") do |device|
- puts device
res << {
:device_id => device.attributes['id'].to_i,
# TODO Covert to using?
@@ -158,146 +157,131 @@ def site_device_scan_start(site_id, devices, hosts)
#
# status = site.saveSite()
#-------------------------------------------------------------------------------------------------------------------
+
+
+
+ # Configuration object representing a Nexpose site.
class Site
- # true if an error condition exists; false otherwise
- attr_reader :error
- # Error message string
- attr_reader :error_msg
- # The last XML request sent by this object
- attr_reader :request_xml
- # The last XML response received by this object
- attr_reader :response_xml
- # The NSC Connection associated with this object
- attr_reader :connection
- # The Site ID
- # site_id = -1 means create a new site. The NSC will assign a new site_id on SiteSave.
- attr_reader :site_id
- # A summary overview of this site
- # SiteSummary Object
- attr_reader :site_summary
- # The configuration of this site
- # SiteConfig Object
- attr_reader :site_config
- # The device listing for this site
- # SiteDeviceListing Object
- attr_reader :site_device_listing
- # The scan history of this site
- # SiteScanHistory Object
- attr_reader :site_scan_history
-
- def initialize(connection = nil, site_id = -1)
- @error = false
- @connection = connection
- @site_id = site_id
- # If site_id > 0 then retrieve site configuration from security console.
- if (@site_id.to_i > 0)
- # Create new SiteConfig object
- @site_config = SiteConfig.new()
- # Populate SiteConfig Obect with Data from the NSC
- @site_config.getSiteConfig(@connection, @site_id)
- @site_summary = SiteSummary.new(@site_id, @site_config.site_name, @site_config.description, @site_config.riskfactor)
- @site_scan_history = SiteScanHistory.new(@connection, @site_id)
- @site_device_listing = SiteDeviceListing.new(@connection, @site_id)
+ # The site ID. An ID of -1 is used to designate a site that has not been
+ # saved to a Nexpose console.
+ attr_accessor :id
- else
- # Just in case user enters a number > -1 or = 0
- @site_id = -1
+ # Unique name of the site. Required.
+ attr_accessor :name
- @site_config = SiteConfig.new()
- # setSiteConfig("New Site " + rand(999999999999).to_s, "")
- @site_summary = nil
+ # Description of the site.
+ attr_accessor :description
- end
- end
+ # [Array] Collection of assets. May be IPv4, IPv6, or DNS names.
+ # @see HostName
+ # @see IPRange
+ attr_accessor :assets
- # Unique identifier for this site.
- def id
- @site_config.id
- end
+ # Scan template to use when starting a scan job. Default: full-audit
+ attr_accessor :scan_template
- # Set the ID for this site. -1 indicates that this configuration will be
- # saved as a new site when saved.
- def id=(value)
- @site_config.id = value
- end
+ # Friendly name of scan template to use when starting a scan job.
+ # Value is populated when a site is saved or loaded from a console.
+ attr_accessor :scan_template_name
- # The name of this site.
- def name
- @site_config.name
- end
+ # Scan Engine to use. Will use the default engine if nil or -1.
+ attr_accessor :engine
- # Set the name of this site.
- def name=(value)
- @site_config.name = value
- end
+ # [Array] Schedule starting dates and times for scans, and set their frequency.
+ attr_accessor :schedules
- # A description of this site configuration.
- def description
- @site_config.description
- end
+ # The risk factor associated with this site. Default: 1.0
+ attr_accessor :risk_factor
- # Set the description for this site.
- def description=(value)
- @site_config.description = value
- end
+ # [Array] Collection of credentials associated with this site.
+ attr_accessor :credentials
- # The risk factor associated with assets on this site.
- def risk_factor
- @site_config.riskfactor
- end
+ # [Array] Collection of real-time alerts.
+ # @see SMTPAlert
+ # @see SNMPAlert
+ # @see SyslogAlert
+ attr_accessor :alerts
- # Set the risk factor for this site.
- def risk_factor=(value)
- @site_config.riskfactor = value
- end
+ # Configuration version. Default: 3
+ attr_accessor :config_version
- # The hosts (IPRange or HostName) associated with this site.
- def hosts
- @site_config.hosts
- end
+ # Whether or not this site is dynamic.
+ # Dynamic sites are created through Asset Discovery Connections.
+ # Modifying their behavior through the API is not recommended.
+ attr_accessor :is_dynamic
- # Set the hosts array with user provided value.
- def hosts=(value)
- @site_config.hosts = value
- end
+ # Site constructor. Both arguments are optional.
+ #
+ # @param [String] name Unique name of the site.
+ # @param [String] scan_template ID of the scan template to use.
+ def initialize(name = nil, scan_template = 'full-audit')
+ @name = name;
+ @scan_template = scan_template
- # Add the host to the existing lists of hosts for this site.
- def add_host(host)
- @site_config.hosts << host
+ @id = -1
+ @risk_factor = 1.0
+ @config_version = 3
+ @is_dynamic = false
+ @assets = []
+ @schedules = []
+ @credentials = []
+ @alerts = []
end
- # Set the host for this site to the value supplied, overriding any previous
- # values.
- def set_host(host)
- @site_config.hosts = [host]
+ # Returns true when the site is dynamic.
+ def dynamic?
+ is_dynamic
end
- # Credentials to be used when scanning this site.
- def credentials
- @site_config.credentials
+ # Load an existing configuration from a Nexpose instance.
+ #
+ # @param [Connection] connection Connection to console where scan will be launched.
+ # @param [Fixnum] id Site ID of an existing site.
+ # @return [Site] Site configuration loaded from a Nexpose console.
+ def self.load(connection, id)
+ r = APIRequest.execute(connection.url, %Q(<SiteConfigRequest session-id="#{connection.session_id}" site-id="#{id}"/>))
+ parse(r.res)
end
- # Creates a new site summary
- def setSiteSummary(site_name, description, riskfactor = 1.0)
- @site_summary = SiteSummary.new(-1, site_name, description, riskfactor)
-
+ # Copy an existing configuration from a Nexpose instance.
+ #
+ # @param [Connection] connection Connection to console where scan will be launched.
+ # @param [Fixnum] id Site ID of an existing site.
+ # @return [Site] Site configuration loaded from a Nexpose console.
+ def self.copy(connection, id)
+ site = self.load(connection, id)
+ site.id = -1
+ site.name = "#{site.name} Copy"
+ site
end
- # Creates a new site configuration
- def setSiteConfig(site_name, description, riskfactor = 1.0)
- setSiteSummary(site_name, description, riskfactor)
- @site_config = SiteConfig.new()
- @site_config._set_site_id(-1)
- @site_config._set_site_name(site_name)
- @site_config._set_description(description)
- @site_config._set_riskfactor(riskfactor)
- @site_config._set_scanConfig(ScanConfig.new(-1, "tmp", "full-audit"))
- @site_config._set_connection(@connection)
+ # Saves this site to a Nexpose console.
+ #
+ # @param [Connection] connection Connection to console where this site will be saved.
+ # @return [Fixnum] Site ID assigned to this configuration, if successful.
+ def save(connection)
+ r = connection.execute('<SiteSaveRequest session-id="' + connection.session_id + '">' + to_xml + ' </SiteSaveRequest>')
+ if (r.success)
+ @id = r.attributes['site-id']
+ return @id
+ end
+ end
+ # Delete this site from a Nexpose console.
+ #
+ # @param [Connection] connection Connection to console where this site will be saved.
+ # @return [Boolean] Whether or not the site was successfully deleted.
+ def delete(connection)
+ r = connection.execute('<SiteDeleteRequest session-id="' + connection.session_id.to_s + '" site-id="' + @id + '"/>')
+ r.success
end
# Scan this site.
+ #
+ # @param [Connection] connection Connection to console where scan will be launched.
+ # @param [String] sync_id Optional syncronization token.
+ # @return [Fixnum, Fixnum] Scan ID and engine ID where the scan was launched.
def scan(connection, sync_id = nil)
xml = REXML::Element.new('SiteScanRequest')
xml.add_attributes({'session-id' => connection.session_id,
@@ -307,78 +291,18 @@ def scan(connection, sync_id = nil)
response = connection.execute(xml)
if response.success
response.res.elements.each('/SiteScanResponse/Scan/') do |scan|
- return [scan.attributes['scan-id'], scan.attributes['engine-id']]
+ return [scan.attributes['scan-id'].to_i, scan.attributes['engine-id'].to_i]
end
end
end
- # Initiates a scan of this site. If successful returns scan_id and engine_id in an associative array. Returns false if scan is unsuccessful.
- def scanSite()
- r = @connection.execute('<SiteScanRequest session-id="' + "#{@connection.session_id}" + '" site-id="' + "#{@site_id}" + '"/>')
- if (r.success)
- res = {}
- r.res.elements.each('//Scan/') do |s|
- res[:scan_id] = s.attributes['scan-id']
- res[:engine_id] = s.attributes['engine-id']
- end
- return res
- else
- return false
- end
- end
-
- # Saves this site in the NSC
- def saveSite
- r = @connection.execute('<SiteSaveRequest session-id="' + @connection.session_id + '">' + getSiteXML + ' </SiteSaveRequest>')
- if (r.success)
- @site_id = r.attributes['site-id']
- @site_config._set_site_id(@site_id)
- @site_config.scanConfig._set_configID(@site_id)
- @site_config.scanConfig._set_name(@site_id)
- return true
- else
- return false
- end
- end
-
- def deleteSite
- r = @connection.execute('<SiteDeleteRequest session-id="' + @connection.session_id.to_s + '" site-id="' + @site_id + '"/>')
- r.success
- end
-
-
- def printSite
- puts "Site ID: " + @site_summary.id
- puts "Site Name: " + @site_summary.site_name
- puts "Site Description: " + @site_summary.description
- puts "Site Risk Factor: " + @site_summary.riskfactor
- end
-
- def to_xml_elem
- xml = REXML::Element.new('Site')
- xml.add_attributes({'id' => id,
- 'name' => name,
- 'description' => description,
- 'riskfactor' => risk_factor})
-
- host_xml = REXML::Element.new('Hosts', xml)
- hosts.each { |host| host_xml.add_element(host.to_xml_elem) }
-
- unless credentials.empty?
- cred_xml = REXML::Element.new('Credentials', xml)
- credentials.each { |cred| cred_xml.add_element(cred.to_xml_elem) }
- end
-
- # TODO
- xml
- end
-
- def getSiteXML
-
+ # Generate an XML representation of this site configuration
+ # @return [String] XML valid for submission as part of other requests.
+ def to_xml
xml = %Q(<Site id='#{id}' name='#{name}' description='#{description}' riskfactor='#{risk_factor}'>)
xml << '<Hosts>'
- xml << hosts.reduce('') { |acc, host| acc << host.to_xml }
+ xml << assets.reduce('') { |acc, host| acc << host.to_xml }
xml << '</Hosts>'
unless credentials.empty?
@@ -386,40 +310,109 @@ def getSiteXML
credentials.each do |c|
xml << c.to_xml if c.respond_to? :to_xml
end
- xml << ' </Credentials>'
+ xml << '</Credentials>'
end
- unless @site_config.alerts.empty?
- xml << ' <Alerting>'
- @site_config.alerts.each do |a|
+ unless alerts.empty?
+ xml << '<Alerting>'
+ alerts.each do |a|
xml << a.to_xml if a.respond_to? :to_xml
end
- xml << ' </Alerting>'
+ xml << '</Alerting>'
end
- xml << %Q{<ScanConfig configID="#{@site_config.scanConfig.configID}" name="#{@site_config.scanConfig.name}" templateID="#{@site_config.scanConfig.templateID}" configVersion="#{@site_config.scanConfig.configVersion}" engineID="#{@site_config.scanConfig.engine_id}">}
+ xml << %Q(<ScanConfig configID="#{@id}" name="#{@scan_template_name || @scan_template}" templateID="#{@scan_template}" configVersion="#{@config_version || 3}" engineID="#{@engine}">)
- xml << ' <Schedules>'
- @site_config.scanConfig.schedules.each do |s|
- xml << %Q{<Schedule enabled="#{s.enabled ? 1 : 0}" type="#{s.type}" interval="#{s.interval}" start="#{s.start}" />}
+ xml << '<Schedules>'
+ @schedules.each do |sched|
+ xml << %Q{<Schedule enabled="#{sched.enabled ? 1 : 0}" type="#{sched.type}" interval="#{sched.interval}" start="#{sched.start}" />}
end
- xml << ' </Schedules>'
+ xml << '</Schedules>'
+ xml << '</ScanConfig>'
+ xml << '</Site>'
+ end
+
+ # Parse a response from a Nexpose console into a valid Site object.
+ #
+ # @param [REXML::Document] rexml XML document to parse.
+ # @return [Site] Site object represented by the XML.
+ # ## TODO What is returned on failure?
+ def self.parse(rexml)
+ rexml.elements.each('SiteConfigResponse/Site') do |s|
+ site = Site.new(s.attributes['name'])
+ site.id = s.attributes['id'].to_i
+ site.description = s.attributes['description']
+ site.risk_factor = s.attributes['riskfactor'] || 1.0
+ site.is_dynamic = true if s.attributes['isDynamic'] == '1'
+
+ s.elements.each('Hosts/range') do |r|
+ site.assets << IPRange.new(r.attributes['from'], r.attributes['to'])
+ end
+ s.elements.each('Hosts/host') do |host|
+ site.assets << HostName.new(host.text)
+ end
- unless @site_config.scanConfig.scanTriggers.empty?
- xml << ' <ScanTriggers>'
- @site_config.scanConfig.scanTriggers.each do |s|
- if (s.class.to_s == "Nexpose::AutoUpdate")
- xml << ' <autoUpdate enabled="' + s.enabled + '" incremental="' + s.incremental + '"/>'
+ s.elements.each('ScanConfig') do |scan_config|
+ site.scan_template_name = scan_config.attributes['name']
+ site.scan_template = scan_config.attributes['templateID']
+ site.config_version = scan_config.attributes['configVersion'].to_i
+ site.engine = scan_config.attributes['engineID'].to_i
+ scan_config.elements.each('Schedules/Schedule') do |sched|
+ schedule = Schedule.new(sched.attributes['type'],
+ sched.attributes['interval'],
+ sched.attributes['start'],
+ sched.attributes['enabled'])
+ site.schedules << schedule
end
end
- xml << ' </ScanTriggers>'
- end
- xml << ' </ScanConfig>'
+ s.elements.each('Credentials') do |cred|
+ # TODO
+ end
- xml << ' </Site>'
+ s.elements.each('Alerting/Alert') do |a|
+ a.elements.each('smtpAlert') do |smtp|
+ smtp_alert = SMTPAlert.new(a.attributes['name'], smtp.attributes['sender'], smtp.attributes['limitText'], a.attributes['enabled'])
- xml
+ smtp.elements.each('recipient') do |recipient|
+ smtp_alert.addRecipient(recipient.text)
+ end
+ site.alerts << smtp_alert
+ end
+
+ a.elements.each('snmpAlert') do |snmp|
+ snmp_alert = SNMPAlert.new(a.attributes['name'], snmp.attributes['community'], snmp.attributes['server'], a.attributes['enabled'])
+ site.alerts << snmp_alert
+ end
+
+ a.elements.each('syslogAlert') do |syslog|
+ syslog_alert = SyslogAlert.new(a.attributes['name'], syslog.attributes['server'], a.attributes['enabled'])
+ site.alerts << syslog_alert
+ end
+
+ a.elements.each('vulnFilter') do |vulnFilter|
+
+ #vulnfilter = new VulnFilter.new(a.attributes["typemask"], a.attributes["severityThreshold"], $attrs["MAXALERTS"])
+ # Pop off the top alert on the stack
+ #$alert = @alerts.pop()
+ # Add the new recipient string to the Alert Object
+ #$alert.setVulnFilter($vulnfilter)
+ # Push the alert back on to the alert stack
+ #array_push($this->alerts, $alert)
+ end
+
+ a.elements.each('scanFilter') do |scanFilter|
+ #<scanFilter scanStop='0' scanFailed='0' scanStart='1'/>
+ #scanfilter = ScanFilter.new(scanFilter.attributes['scanStop'],scanFilter.attributes['scanFailed'],scanFilter.attributes['scanStart'])
+ #alert = @alerts.pop()
+ #alert.setScanFilter(scanfilter)
+ #@alerts.push(alert)
+ end
+ end
+
+ return site
+ end
+ nil
end
end
@@ -513,166 +506,6 @@ def _set_id(id)
end
# === Description
- # Object that represents the configuration of a Site. This object is automatically created when a new Site object is instantiated.
- #
- class SiteConfig
- # true if an error condition exists; false otherwise
- attr_reader :error
- # Error message string
- attr_reader :error_msg
- # The last XML request sent by this object
- attr_reader :request_xml
- # The last XML response received by this object
- attr_reader :response_xml
- # The NSC Connection associated with this object
- attr_reader :connection
- # The Site ID
- attr_accessor :id
- attr_reader :site_id
- # The Site Name
- attr_accessor :name
- attr_reader :site_name
- # A Description of the Site
- attr_accessor :description
- # User assigned risk multiplier
- attr_accessor :riskfactor
- # Array containing ((IPRange|HostName)*)
- attr_accessor :hosts
- # Array containing (AdminCredentials*)
- attr_accessor :credentials
- # Array containing ((SmtpAlera|SnmpAlert|SyslogAlert)*)
- attr_accessor :alerts
- # ScanConfig object which holds Schedule and ScanTrigger Objects
- attr_accessor :scanConfig
- attr_reader :is_dynamic
-
- def initialize()
- @id = -1
- @xml_tag_stack = []
- @hosts = []
- @credentials = []
- @alerts = []
- @error = false
- end
-
- # Adds a new host to the hosts array
- def addHost(host)
- @hosts.push(host)
- end
-
- # Adds a new alert to the alerts array
- def addAlert(alert)
- @alerts.push(alert)
- end
-
- # Adds a new set of credentials to the credentials array
- def addCredentials(credential)
- @credentials.push(credential)
- end
-
- # TODO
- def getSiteConfig(connection, site_id)
- @connection = connection
- @id = @site_id = site_id
-
- r = APIRequest.execute(@connection.url, %Q(<SiteConfigRequest session-id="#{@connection.session_id}" site-id="#{@site_id}"/>))
- parse(r.res)
- end
-
- def self.get_site_config(connection, site_id)
- config = SiteConfig.new
- config.getSiteConfig(connection, site_id)
- config
- end
-
- def _set_site_id(site_id)
- @site_id = site_id
- end
-
- def _set_site_name(site_name)
- @site_name = site_name
- end
-
- def _set_description(description)
- @description = description
- end
-
- def _set_riskfactor(riskfactor)
- @riskfactor = riskfactor
- end
-
- def _set_scanConfig(scanConfig)
- @scanConfig = scanConfig
- end
-
- def _set_connection(connection)
- @connection = connection
- end
-
- def parse(response)
- response.elements.each('SiteConfigResponse/Site') do |s|
- @id = @site_id = s.attributes['id']
- @name = @site_name = s.attributes['name']
- @description = s.attributes['description']
- @riskfactor = s.attributes['riskfactor'] || 1.0
- @is_dynamic = s.attributes['isDynamic']
- s.elements.each('Hosts/range') do |r|
- @hosts.push(IPRange.new(r.attributes['from'], r.attributes['to']))
- end
- s.elements.each('Hosts/host') do |host|
- @hosts << HostName.new(host.text)
- end
- # TODO: This should just be passed to ScanConfig to parse.
- s.elements.each('ScanConfig') do |c|
- @scanConfig = ScanConfig.parse(c)
- end
- s.elements.each('Credentials') do |cred|
- # TODO
- end
- s.elements.each('Alerting/Alert') do |a|
-
- a.elements.each('smtpAlert') do |smtp|
- smtp_alert = SmtpAlert.new(a.attributes["name"], smtp.attributes["sender"], smtp.attributes["limitText"], a.attributes["enabled"])
-
- smtp.elements.each('recipient') do |recipient|
- smtp_alert.addRecipient(recipient.text)
- end
- @alerts.push(smtp_alert)
- end
-
- a.elements.each('snmpAlert') do |snmp|
- snmp_alert = SnmpAlert.new(a.attributes["name"], snmp.attributes["community"], snmp.attributes["server"], a.attributes["enabled"])
- @alerts.push(snmp_alert)
- end
- a.elements.each('syslogAlert') do |syslog|
- syslog_alert = SyslogAlert.new(a.attributes["name"], syslog.attributes["server"], a.attributes["enabled"])
- @alerts.push(syslog_alert)
- end
-
- a.elements.each('vulnFilter') do |vulnFilter|
-
- #vulnfilter = new VulnFilter.new(a.attributes["typemask"], a.attributes["severityThreshold"], $attrs["MAXALERTS"])
- # Pop off the top alert on the stack
- #$alert = @alerts.pop()
- # Add the new recipient string to the Alert Object
- #$alert.setVulnFilter($vulnfilter)
- # Push the alert back on to the alert stack
- #array_push($this->alerts, $alert)
- end
-
- a.elements.each('scanFilter') do |scanFilter|
- #<scanFilter scanStop='0' scanFailed='0' scanStart='1'/>
- #scanfilter = ScanFilter.new(scanFilter.attributes['scanStop'],scanFilter.attributes['scanFailed'],scanFilter.attributes['scanStart'])
- #alert = @alerts.pop()
- #alert.setScanFilter(scanfilter)
- #@alerts.push(alert)
- end
- end
- end
- end
- end
-
- # === Description
# Object that represents the scan history of a site.
#
class SiteScanHistory
@@ -839,7 +672,7 @@ def to_xml
# === Description
# Object that represents an SNMP Alert.
#
- class SnmpAlert
+ class SNMPAlert
include Sanitize
# A unique name for this alert
@@ -886,7 +719,7 @@ def to_xml
# === Description
# Object that represents an SMTP (Email) Alert.
#
- class SmtpAlert
+ class SMTPAlert
# A unique name for this alert
attr_reader :name
# If this alert is enabled or not
25 test/site-config.xml
View
@@ -0,0 +1,25 @@
+<SiteConfigResponse success="1">
+ <Site id="2" name="Test Site" description="Description goes here" riskfactor="2.0" isDynamic="0">
+ <Hosts>
+ <range from="127.0.0.1"/>
+ <range from="10.0.0.1" to="10.0.0.26"/>
+ <host>test.rapid7.com</host>
+ </Hosts>
+ <Credentials>
+ </Credentials>
+ <Alerting>
+ <Alert name="notify" enabled="1" maxAlerts="1">
+ <scanFilter scanStart="0" scanStop="1" scanFailed="0" scanResumed="0" scanPaused="0"/>
+ <vulnFilter severityThreshold="1" confirmed="1" unconfirmed="0" potential="0"/>
+ <smtpAlert sender="rspec@rapid7.com" server="smtp.rapid7.com" limitText="0">
+ <recipient>rspec@rapid7.com</recipient>
+ </smtpAlert>
+ </Alert>
+ </Alerting>
+ <ScanConfig configID="2" name="Full audit" templateID="full-audit" engineID="2" configVersion="3">
+ <Schedules>
+ <Schedule enabled="1" incremental="0" type="monthly-date" interval="1" start="20121008T160000000" maxDuration="30" repeaterType="continue"/>
+ </Schedules>
+ </ScanConfig>
+ </Site>
+</SiteConfigResponse>
46 test/site-load-failure.xml
View
@@ -0,0 +1,46 @@
+<SiteConfigResponse success="0">
+<Failure>
+<Exception>
+<message>java.lang.NullPointerException</message>
+<stacktrace>java.lang.NullPointerException
+ at com.rapid7.nexpose.assets.SiteConfig.&lt;init&gt;(SiteConfig.java:40)
+ at com.rapid7.nexpose.assets.SiteManager.getSiteConfig(SiteManager.java:750)
+ at com.rapid7.nexpose.assets.SiteManager.getSiteConfig(SiteManager.java:709)
+ at com.rapid7.nexpose.nsc.http.proglets.api.SiteConfigRequestHandler.handleRequest(SiteConfigRequestHandler.java:46)
+ at com.rapid7.nexpose.nsc.http.proglets.api.RequestHandler.handle(RequestHandler.java:56)
+ at com.rapid7.nexpose.nsc.http.proglets.api.Api10Handler$XmlRequestHandler.handle(Api10Handler.java:163)
+ at com.rapid7.nexpose.nsc.http.proglets.api.Api10Handler.handle_xml(Api10Handler.java:65)
+ at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
+ at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
+ at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
+ at java.lang.reflect.Method.invoke(Method.java:601)
+ at com.rapid7.nexpose.nsc.http.proglets.ApiProglet.doGet(ApiProglet.java:151)
+ at com.rapid7.nexpose.nsc.http.proglets.ApiProglet.doPost(ApiProglet.java:204)
+ at com.rapid7.nexpose.nsc.http.Proglet.service(Proglet.java:190)
+ at com.rapid7.nexpose.nsc.http.httpd.RequestHandler.handleRequest(RequestHandler.java:535)
+ at com.rapid7.nexpose.nsc.http.httpd.RequestHandler.handle(RequestHandler.java:265)
+ at com.rapid7.nexpose.nsc.http.httpd.WebServerManager.executeRequest(WebServerManager.java:1318)
+ at com.rapid7.nexpose.nsc.tomcat.TomcatForwardingServlet.service(TomcatForwardingServlet.java:40)
+ at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
+ at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
+ at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
+ at com.rapid7.nexpose.nsc.tomcat.SessionValidationFilter.doFilter(SessionValidationFilter.java:124)
+ at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
+ at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
+ at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
+ at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
+ at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:470)
+ at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
+ at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
+ at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
+ at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
+ at org.apache.coyote.http11.Http11NioProcessor.process(Http11NioProcessor.java:889)
+ at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:744)
+ at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:2274)
+ at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
+ at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
+ at java.lang.Thread.run(Thread.java:722)
+</stacktrace>
+</Exception>
+</Failure>
+</SiteConfigResponse>
158 test/test_site.rb
View
@@ -0,0 +1,158 @@
+require 'rspec/given'
+
+require 'rexml/document'
+require 'nexpose'
+include Nexpose
+
+describe Site do
+
+ # Stub object to return REXML response.
+ class Response
+ attr_accessor :res
+ def initialize(res)
+ @res = res
+ end
+ end
+
+ # Add Response behavior to an Object.
+ def add_response
+ obj = Object.new
+ class << obj
+ def url
+ Response.new(REXML::Document.new(File.open('site-config.xml')))
+ end
+ def session_id
+ 'FEDCBA'
+ end
+ end
+ obj
+ end
+
+ # Failure from SiteConfigRequest
+ def load_failure
+ obj = Object.new
+ class << obj
+ def url
+ Response.new(REXML::Document.new(File.open('site-load-failure.xml')))
+ end
+ def session_id
+ 'FEDCBA'
+ end
+ end
+ obj
+ end
+
+ # Success from SiteDeleteRequest
+ def delete_success
+ obj = Object.new
+ class << obj
+ def session_id
+ 'FEDCBA'
+ end
+ end
+ obj
+ end
+
+ # Failure from SiteDeleteRequest
+ def delete_failure
+ obj = Object.new
+ class << obj
+ def session_id
+ 'FAIL'
+ end
+ end
+ obj
+ end
+
+ # Monkey patch API behavior to give static responses.
+ class Nexpose::APIRequest
+ def self.execute(url, req, api_version='1.1')
+ url
+ end
+ end
+
+ # Load in Site object from configuration.
+ def load_site
+ nsc = add_response
+ Site.load(nsc, 2)
+ end
+
+ context "default constructor" do
+ When(:site) { Site.new }
+
+ Then { site.id.should == -1 }
+ Then { site.risk_factor.to_f.should == 1.0 }
+ Then { site.scan_template.should == 'full-audit' }
+ end
+
+ context "constructor with arguments" do
+ When(:site) { Site.new('name', 'web-audit') }
+
+ Then { site.id.should == -1 }
+ Then { site.risk_factor.to_f.should == 1.0 }
+ Then { site.name.should == 'name' }
+ Then { site.scan_template.should == 'web-audit' }
+ end
+
+ Given(:xml) { REXML::Document.new(File.open('site-config.xml')) }
+
+ context "parse from xml" do
+ When(:site) { Site.parse(xml) }
+
+ Then { site.id.to_i.should == 2 }
+ Then { site.assets.size.should == 3 }
+ Then { site.schedules.size.should == 1 }
+ end
+
+ context "load a site: success" do
+ When(:site) { load_site }
+
+ Then { site.id.to_i.should == 2 }
+ Then { site.assets.size.should == 3 }
+ Then { site.schedules.size.should == 1 }
+ end
+
+ context "load a site: failure" do
+ Given(:nsc) { load_failure }
+ When(:site) { Site.load(nsc, 1337) }
+
+ Then { site.should == nil }
+ end
+
+ context "copy a site" do
+ Given(:nsc) { add_response }
+ When(:site) { Site.copy(nsc, 2) }
+
+ Then { site.id.to_i.should == -1 }
+ Then { site.assets.size.should == 3 }
+ Then { site.schedules.size.should == 1 }
+ end
+
+ context "save a site: success" do
+ end
+
+ context "save a site: failure" do
+ end
+
+ context "scan a site: success" do
+ end
+
+ context "scan a site: failure" do
+ end
+
+ context "delete a site: success" do
+ Given(:site) { load_site}
+ Given(:nsc) { delete_success }
+ # When(:response) { site.delete(nsc) }
+
+ # Then { response.should == true }
+ end
+
+ context "delete a site: failure" do
+ Given(:site) { load_site}
+ Given(:nsc) { delete_failure }
+ # When(:response) { site.delete(nsc) }
+
+ # Then { response.should == false }
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.