module Nessus
# This class represents each of the /NessusClientData_v2/Report/ReportHost/ReportItem
# elements in the Nessus 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 ReportItem
# 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
:plugin_family, :plugin_id, :plugin_name, :port, :protocol, :svc_name, :severity,
# simple tags
:cvss3_base_score, :cvss3_temporal_score, :cvss3_temporal_vector, :cvss3_vector,
:cvss_base_score, :cvss_temporal_score, :cvss_temporal_vector, :cvss_vector,
:description, :exploit_available, :exploit_framework_canvas, :exploit_framework_core,
:exploitability_ease, :exploit_framework_metasploit,
:metasploit_name, :patch_publication_date, :plugin_modification_date, :plugin_output,
:plugin_publication_date, :plugin_version, :risk_factor,
:solution, :synopsis, :vuln_publication_date,
# multiple tags
:bid_entries, :cve_entries, :see_also_entries, :xref_entries,
# compliance tags
:cm_actual_value, :cm_audit_file, :cm_check_id, :cm_check_name, :cm_info,
:cm_output, :cm_policy_value, :cm_reference, :cm_result, :cm_see_also,
:cm_solution
]
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: port, svc_name, protocol, severity,
# plugin_id, plugin_name, plugin_family
translations_table = {
# @port = xml.attributes["port"]
# @svc_name = xml.attributes["svc_name"]
# @protocol = xml.attributes["protocol"]
# @severity = xml.attributes["severity"]
:plugin_id => 'pluginID',
:plugin_name => 'pluginName',
:plugin_family => 'pluginFamily'
}
method_name = translations_table.fetch(method, method.to_s)
return @xml.attributes[method_name].value if @xml.attributes.key?(method_name)
# then we try the children tags: solution, risk_factor, description,
# plugin_publication_date, metasploit_name, cvss_vector,
# cvss_temporal_vector, synopsis, exploit_available,
# patch_publication_date, plugin_modification_date, cvss_temporal_score,
# cvss_base_score, plugin_output, plugin_version, exploitability_ease,
# vuln_publication_date, exploit_framework_canvas,
# exploit_framework_metasploit, exploit_framework_core
tag = @xml.xpath("./#{method_name}").first
if tag
return tag.text
end
# then the custom XML tags (cm: namespace)
if method_name.starts_with?('cm_')
method_name = method_name.sub(/cm_/, 'cm:compliance-').gsub(/_/, '-')
cm_value = @xml.at_xpath("./#{method_name}", { 'cm' => 'http://www.nessus.org/cm' })
if cm_value
return cm_value.text
else
return nil
end
end
# finally the enumerations: bid_entries, cve_entries, xref_entries
translations_table = {
:bid_entries => 'bid',
:cve_entries => 'cve',
:see_also_entries => 'see_also',
:xref_entries => 'xref'
}
method_name = translations_table.fetch(method, nil)
if method_name
@xml.xpath("./#{method_name}").collect(&:text)
else
# nothing found, the tag is valid but not present in this ReportItem
return nil
end
end
end
end