class Nexpose::Vulnerability

This class represents each of the /NexposeReport/VulnerabilityDefinitions/vulnerability 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.

Constants

SSL_CIPHER_VULN_IDS

Public Class Methods

new(xml_node) click to toggle source

Accepts an XML node from Nokogiri::XML.

# File lib/nexpose/vulnerability.rb, line 14
def initialize(xml_node)
  @xml = xml_node
end

Public Instance Methods

method_missing(method, *args) click to toggle source

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.

Calls superclass method
# File lib/nexpose/vulnerability.rb, line 51
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 = {
    :nexpose_id => 'id',
    :pci_severity => 'pciSeverity',
    :cvss_score => 'cvssScore',
    :cvss_vector =>'cvssVector'
  }

  method_name = translations_table.fetch(method, method.to_s)
  return @xml.attributes[method_name].value if @xml.attributes.key?(method_name)

  # Then we try simple children tags: description, solution
  tag = @xml.xpath("./#{method_name}/ContainerBlockElement").first
  # Then we try the tags with nested content
  nest = @xml.xpath("./#{method_name}").first

  # We need to clean up tags that have HTML content in them
  if tags_with_html_content.include?(method)
    result = cleanup_html(tag)
    result = add_bc_to_ssl_cipher_list(result) if SSL_CIPHER_VULN_IDS.include?(@xml.attributes['id'].value)
    return result
  # And we need to clean up the tags with nested content in them
  elsif tags_with_nested_content.include?(method)
    return cleanup_nested(nest)
  else
    return tag
  end

  # Handle evidence creation
  if method_name == 'details'
    vuln_id = @xml.attributes['id'].value

    return @xml.xpath("//test[@id='#{vuln_id}']/Paragraph").
      text.split("\n").
      collect(&:strip).
      reject{|line| line.empty?}.join("\n")
  end

  nil
end
respond_to?(method, include_private=false) click to toggle source

This allows external callers (and specs) to check for implemented properties

Calls superclass method
# File lib/nexpose/vulnerability.rb, line 40
def respond_to?(method, include_private=false)
  return true if supported_tags.include?(method.to_sym)
  super
end
supported_tags() click to toggle source

List of supported tags. They can be attributes, simple descendans or collections (e.g. <references/>, <tags/>)

# File lib/nexpose/vulnerability.rb, line 20
def supported_tags
  [
    # attributes
    :nexpose_id, :title, :severity, :pci_severity, :cvss_score, :cvss_vector,
    :published, :added, :modified,

    # simple tags
    :description, :solution,

    # multiple tags
    :references, :tags,

    # evidence tag
    :details
  ]
end

Private Instance Methods

add_bc_to_ssl_cipher_list(source) click to toggle source
# File lib/nexpose/vulnerability.rb, line 106
def add_bc_to_ssl_cipher_list(source)
  result = source.to_s
  result.gsub!(/\n(.*?)!(.*?)/){"\nbc. #{ $1 }!#{ $2 }\n"}
  result
end
cleanup_html(source) click to toggle source
# File lib/nexpose/vulnerability.rb, line 112
def cleanup_html(source)
  result = source.to_s
  result.gsub!(/<ContainerBlockElement>(.*?)<\/ContainerBlockElement>/m){|m| "#{ $1 }"}
  result.gsub!(/<Paragraph preformat=\"true\">(.*?)<\/Paragraph>/mi){|m| "\nbc. #{ $1 }\n\n"}
  result.gsub!(/<Paragraph>(.*?)<\/Paragraph>/m){|m| "#{ $1 }\n"}
  result.gsub!(/<Paragraph>|<\/Paragraph>/, '')
  result.gsub!(/<UnorderedList>(.*?)<\/UnorderedList>/m){|m| "#{ $1 }"}
  result.gsub!(/<OrderedList(.*?)>(.*?)<\/OrderedList>/m){|m| "#{ $2 }"}
  result.gsub!(/<ListItem>|<\/ListItem>/, '')
  result.gsub!(/          /, '')
  result.gsub!(/   /, '')
  result.gsub!(/\t\t/, '')
  result.gsub!(/<URLLink(.*)LinkURL=\"(.*?)\"(.*?)>(.*?)<\/URLLink>/i) { "\"#{$4.strip}\":#{$2.strip} " }
  result.gsub!(/<URLLink LinkTitle=\"(.*?)\"(.*?)LinkURL=\"(.*?)\"\/>/i) { "\"#{$1.strip}\":#{$3.strip} " }
  result.gsub!(/<URLLink LinkURL=\"(.*?)\"(.*?)LinkTitle=\"(.*?)\"\/>/i) { "\"#{$3.strip}\":#{$1.strip} " }
  result.gsub!(/&gt;/, '>')
  result.gsub!(/&lt;/, '<')
  result
end
cleanup_nested(source) click to toggle source
# File lib/nexpose/vulnerability.rb, line 132
def cleanup_nested(source)
  result = source.to_s
  result.gsub!(/<references>/, '')
  result.gsub!(/<\/references>/, '')
  result.gsub!(/<reference source=\"(.*?)\">(.*?)<\/reference>/i) {"#{$1.strip}: #{$2.strip}\n"}
  result.gsub!(/<tags>/, '')
  result.gsub!(/<\/tags>/, '')
  result.gsub!(/<tag>(.*?)<\/tag>/) {"#{$1}\n"}
  result.gsub!(/        /, '')
  result
end
tags_with_html_content() click to toggle source
# File lib/nexpose/vulnerability.rb, line 144
def tags_with_html_content
  [:description, :solution]
end
tags_with_nested_content() click to toggle source
# File lib/nexpose/vulnerability.rb, line 148
def tags_with_nested_content
  [:references, :tags]
end