Class: Nexpose::Service

Inherits:
Object
  • Object
show all
Defined in:
lib/nexpose/service.rb

Overview

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

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(xml_node, endpoint: nil) ⇒ Service

Accepts an XML node from Nokogiri::XML.

endpoint - If the Service is instantiated from the Endpoint class (e.g. from <endpoint><services>…) , it will have access to the parent data.



17
18
19
20
# File 'lib/nexpose/service.rb', line 17

def initialize(xml_node, endpoint: nil)
  @xml = xml_node
  @endpoint = endpoint
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args) ⇒ Object

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.



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/nexpose/service.rb', line 60

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

Instance Attribute Details

#endpointObject

Returns the value of attribute endpoint.



11
12
13
# File 'lib/nexpose/service.rb', line 11

def endpoint
  @endpoint
end

#xmlObject

Returns the value of attribute xml.



11
12
13
# File 'lib/nexpose/service.rb', line 11

def xml
  @xml
end

Instance Method Details

#respond_to?(method, include_private = false) ⇒ Boolean

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

Returns:

  • (Boolean)


49
50
51
52
# File 'lib/nexpose/service.rb', line 49

def respond_to?(method, include_private = false)
  return true if supported_tags.include?(method.to_sym)
  super
end

#supported_tagsObject

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



24
25
26
27
28
29
30
31
32
33
34
# File 'lib/nexpose/service.rb', line 24

def supported_tags
  [
    # attributes
    :name,

    # simple tags

    # multiple tags
    :fingerprints, :configurations, :tests
  ]
end

#tests(*args) ⇒ Object

Convert each ./test/test entry into a simple hash



37
38
39
40
41
42
43
44
45
# File 'lib/nexpose/service.rb', line 37

def tests(*args)
  xml.xpath('./tests/test').map do |xml_test|
    # Inject evidence with data from the node
    xml_test['port'] = endpoint[:port]
    xml_test['protocol'] = endpoint[:protocol]

    Nexpose::Test.new(xml_test)
  end
end