class Netsparker::Vulnerability

This class represents each of the /netsparker/vulnerability elements in the Netsparker 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.

Attributes

xml[RW]

Public Class Methods

new(xml_node) click to toggle source

Accepts an XML node from Nokogiri::XML.

# File lib/netsparker/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/netsparker/vulnerability.rb, line 62
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

  # Any fields where a simple .camelcase() won't work we need to translate,
  # this includes acronyms (e.g. :cwe would become 'Cwe') and simple nested
  # tags.
  translations_table = {
    actions_to_take:                            'actionsToTake',
    classification_asvs40:                      'classification/ASVS40',
    classification_capec:                       'classification/CAPEC',
    classification_cwe:                         'classification/CWE',
    classification_cvss_vector:                 'classification/CVSS/vector',
    classification_cvss_base_value:             "classification/CVSS/score/type[text()='Base']/../value",
    classification_cvss_base_severity:          "classification/CVSS/score/type[text()='Base']/../severity",
    classification_cvss_environmental_value:    "classification/CVSS/score/type[text()='Environmental']/../value",
    classification_cvss_environmental_severity: "classification/CVSS/score/type[text()='Environmental']/../severity",
    classification_cvss_temporal_value:         "classification/CVSS/score/type[text()='Temporal']/../value",
    classification_cvss_temporal_severity:      "classification/CVSS/score/type[text()='Temporal']/../severity",
    classification_disastig:                    'classification/DISASTIG',
    classification_hipaa:                       'classification/HIPAA',
    classification_iso27001:                    'classification/ISO27001',
    classification_nistsp80053:                 'classification/NISTSP80053',
    classification_owasp2013:                   'classification/OWASP2013',
    classification_owasp2017:                   'classification/OWASP2017',
    classification_owasppc:                     'classification/OWASPPC',
    classification_pci31:                       'classification/PCI31',
    classification_pci32:                       'classification/PCI32',
    classification_wasc:                        'classification/WASC',
    external_references:                        'externalReferences',
    remedy_references:                          'remedyReferences',
    required_skills_for_exploitation:           'requiredSkillsForExploitation'
  }
  method_name = translations_table.fetch(method, method.to_s)

  # first we try the attributes:
  # return @xml.attributes[method_name].value if @xml.attributes.key?(method_name)

  # then we try the children tags
  tag = xml.at_xpath("./#{method_name}")
  if tag && !tag.text.blank?
    text = tag.text
    return tags_with_html_content.include?(method) ? cleanup_html(text) : text
  else
    return 'n/a'
  end

  # nothing found
  return 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/netsparker/vulnerability.rb, line 51
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.

# File lib/netsparker/vulnerability.rb, line 20
def supported_tags
  [
    # simple tags
    :actions_to_take, :certainty, :description, :external_references,
    :extrainformation, :impact, :knownvulnerabilities,
    :rawrequest, :rawresponse, :remedy,
    :remedy_references, :required_skills_for_exploitation, :severity,
    :title, :type, :url,

    # tags that correspond to Evidence
    :vulnerableparameter, :vulnerableparametertype, :vulnerableparametervalue,

    # nested tags
    :classification_asvs40, :classification_capec,

    :classification_cvss_vector,
    :classification_cvss_base_value, :classification_cvss_base_severity,
    :classification_cvss_environmental_value, :classification_cvss_environmental_severity,
    :classification_cvss_temporal_value, :classification_cvss_temporal_severity,

    :classification_cwe, :classification_disastig, :classification_hipaa,
    :classification_iso27001, :classification_nistsp80053,
    :classification_owasp2013, :classification_owasp2017, :classification_owasppc,
    :classification_pci31, :classification_pci32, :classification_wasc,

    # multiple tags
  ]
end

Private Instance Methods

cleanup_html(source) click to toggle source
# File lib/netsparker/vulnerability.rb, line 122
def cleanup_html(source)
  result = source.dup
  result.gsub!(/&quot;/, '"')
  result.gsub!(/&amp;/, '&')
  result.gsub!(/&lt;/, '<')
  result.gsub!(/&gt;/, '>')
  result.gsub!(/&#39;/, '\'')

  result.gsub!(/\<\!\[CDATA\[(.*?)\]\]\>/m, '\1')

  result.gsub!(/<b>(.*?)<\/b>/) { "*#{$1}*" }
  result.gsub!(/<i>(.*?)<\/i>/) { "_#{$1}_" }
  result.gsub!(/<em>(.*?)<\/em>/) { "*#{$1}*" }
  result.gsub!(/<h2>(.*?)<\/h2>/) { "*#{$1}*" }
  result.gsub!(/<strong>(.*?)<\/strong>/) { "*#{$1}* " }

  result.gsub!(/(<br>)|(<br(\s*)\/>)/, "\n")
  result.gsub!(/(<div>)|(<\/div>)/, "")
  result.gsub!(/<font.*?>(.*?)<\/font>/m, '\1')
  result.gsub!(/<p (.*?)>(.*?)<\/p>/) { "\n#{$2}\n" }
  result.gsub!(/<span(.*?)>(.*?)<\/span>/, '\2')
  result.gsub!(/<span(.*?)>|<\/span>/, "")
  result.gsub!(/(<p>)|(<\/p>)/, "\n")
  result.gsub!(/\n[a-z]\. /, "\n\* ")

  result.gsub!(/<a .*?href=(?:\"|\')(.*?)(?:\"|\').*?>(?:<i.*?<\/i>)?(.*?)<\/a>/i) { "\"#{$2.strip}\":#{$1.strip}" }

  result.gsub!(/<code><pre.*?>(.*?)<\/pre><\/code>/m) {|m| "\n\nbc.. #{$1}\n\np.  \n" }
  result.gsub!(/<pre.*?>(.*?)<\/pre>/m) {|m| "\n\nbc.. #{$1}\n\np.  \n" }
  result.gsub!(/<code>(.*?)<\/code>/m) {|m| "\n\nbc.. #{$1}\n\np.  \n" }

  result.gsub!(/(<ul>)|(<\/ul>|(<ol>)|(<\/ol>))/, "\n")
  result.gsub!(/<li(.*?)>(.*?)<\/li>/m) {"\n* #{$2}\n" }
  result.gsub!(/<li>/, "\n* ")
  result.gsub!(/<\/li>/, "\n")

  result
end
tags_with_html_content() click to toggle source

Some of the values have embedded HTML conent that we need to strip

# File lib/netsparker/vulnerability.rb, line 162
def tags_with_html_content
  [
    :actions_to_take, :description, :external_references, :extrainformation,
    :impact, :remedy, :remedy_references, :required_skills_for_exploitation
  ]
end