module Nexpose
# This class represents each of the /NexposeReport[@version='1.0']/nodes/node/endpoints/endpoint/services/service
# elements in the Nexpose Full XML document.
#
# It provides a convenient way to access the information scattered all over
# the XML in attributes and nested tags.
#
# Instead of providing separate methods for each supported property we rely
# on Ruby's #method_missing to do most of the work.
class Service
# Accepts an XML node from Nokogiri::XML.
def initialize(xml_node)
@xml = xml_node
end
# List of supported tags. They can be attributes, simple descendans or
# collections (e.g. , )
def supported_tags
[
# attributes
:name,
# simple tags
# multiple tags
:fingerprints, :configurations, :tests
]
end
# Convert each ./test/test entry into a simple hash
def tests(*args)
@xml.xpath('./tests/test').collect do |xml_test|
content = if xml_test.at_xpath('./Paragraph')
xml_test.at_xpath('./Paragraph').text.split("\n").collect(&:strip).reject{|line| line.empty?}.join("\n")
else
'n/a'
end
{
id: xml_test.attributes['id'],
status: xml_test.attributes['status'],
content: content
}
end
end
# This allows external callers (and specs) to check for implemented
# properties
def respond_to?(method, include_private=false)
return true if supported_tags.include?(method.to_sym)
super
end
# This method is invoked by Ruby when a method that is not defined in this
# instance is called.
#
# In our case we inspect the @method@ parameter and try to find the
# attribute, simple descendent or collection that it maps to in the XML
# tree.
def method_missing(method, *args)
# We could remove this check and return nil for any non-recognized tag.
# The problem would be that it would make tricky to debug problems with
# typos. For instance: <>.potr would return nil instead of raising an
# exception
unless supported_tags.include?(method)
super
return
end
# First we try the attributes. In Ruby we use snake_case, but in XML
# CamelCase is used for some attributes
translations_table = {
}
method_name = translations_table.fetch(method, method.to_s)
return @xml.attributes[method_name].value if @xml.attributes.key?(method_name)
# Finally the enumerations: references, tags
if ['fingerprints', 'configurations'].include?(method_name)
xpath_selector = {
'fingerprints' => './fingerprints/fingerprint',
'configurations' => './configuration/config'
}[method_name]
@xml.xpath(xpath_selector).collect do |xml_item|
{:text => xml_item.text}.merge(
Hash[
xml_item.attributes.collect do |name, xml_attribute|
[name.sub(/-/,'_').to_sym, xml_attribute.value]
end
]
)
end
else
# nothing found, the tag is valid but not present in this ReportItem
return nil
end
end
end
end