class Risu::Parsers::Nessus::NessusSaxListener

NessusSaxListener

Constants

DYNAMIC_END_METHOD_NAMES

@see DYNAMIC_START_MEHTOD_NAMES

@note only one argument for the methods

@example call end_policy_name for a “policyName” element

element = "policyName"
send(DYNAMIC_END_METHOD_NAMES[element], element)

@param element [String] the name of the element ending

DYNAMIC_START_METHOD_NAMES

Used to map element names to private methods so

that the methods can be called when the parser
encounters the opening of an element.

{“ElementName” => :start_method_to_be_called}

@example call start_policy for a “Policy” element

element = "Policy"
send(DYNAMIC_START_METHOD_NAMES[element], element, attributes)

@param element [String] the element name starting to be parsed @param attributes [Array<Hash{Sring=>String}>] the array of

key value pairs for the element that is starting to be parsed
HOST_PROPERTIES_MAPPING

Map of host properties to symbols for use with ActiveRecord

interfaces

These are the more commonly used host properties,

mapping them here to store in the host table
VALID_ELEMENTS

An array of all valid elements expected during parsing

VALID_HOST_PROPERTIES

An array of valid host properties

VALID_HOST_PROPERTIES_REGEX

TODO: documentation. These are never used in the class

VALID_REFERENCES

An array of valid reference element names

Attributes

new_tags[RW]

Public Class Methods

new() click to toggle source

vals tracks state for elements encountered during parsing

# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 155
def initialize
        @vals = Hash.new
        @new_tags = Array.new
end

Public Instance Methods

on_characters(text) click to toggle source

Called when the inner text of a element is reached

@param text

# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 182
def on_characters(text)
        if @vals[@tag] == nil then
                @vals[@tag] = text
        else
                @vals[@tag] << text
        end
end
on_end_element(element) click to toggle source

Called when the end of the XML element is reached

@param element

# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 193
def on_end_element(element)
        @tag = nil

        if DYNAMIC_END_METHOD_NAMES.key?(element)
                # Dynamic dispatch to private instance methods in the const hash
                #   DYNAMIC_END_METHOD_NAMES where {"element" => :end_element}
                send(DYNAMIC_END_METHOD_NAMES[element], element)
        elsif VALID_REFERENCES.include?(element)
                end_valid_reference(element)
        end
end
on_start_element(element, attributes) click to toggle source

Callback for when the start of a XML element is reached

@param element XML element @param attributes Attributes for the XML element

# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 164
def on_start_element(element, attributes)
        @tag = element
        @vals[@tag] = ""

        if !VALID_ELEMENTS.include?(element)
                @new_tags << "New XML element detected: #{element}. Please report this at #{Risu::GITHUB}/issues/new or via email to #{Risu::EMAIL}"
        end

        if DYNAMIC_START_METHOD_NAMES.key?(element)
                # Dynamic dispatch to private instance "policyComments"methods in the const hash
                #   DYNAMIC_START_METHOD_NAMES where {"element" => :start_element}
                send(DYNAMIC_START_METHOD_NAMES[element], element, attributes)
        end
end

Private Instance Methods

end_attachment(_) click to toggle source
# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 416
def end_attachment(_)
        @attachment.update(:ahash => @vals['attachment'])
end
end_family_item(_) click to toggle source
# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 332
def end_family_item(_)
        @family.update( :family_name => @vals["FamilyName"],
                :status => @vals["Status"])
end
end_item(_) click to toggle source

This takes a really long time, there is about 34,000 pluginIDs in this field and it takes about 36 minutes to parse just this info =\ lets pre-populate the plugins table with the known plugin_id's

if @vals == “plugin_set”

@all_plugins = @vals["value"].split(";")

@all_plugins.each { |p|
               @plug = Plugin.find_or_create_by_id(p)
               @plug.save
}

end

# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 323
def end_item(_)
        @item.update(:plugin_name => @vals["pluginName"],
                :plugin_id => @vals["pluginId"], :fullname => @vals["fullName"],
                :preference_name => @vals["preferenceName"],
                :preference_type => @vals["preferenceType"],
                :preference_values => @vals["preferenceValues"],
                :selected_values => @vals["selectedValue"])
end
end_plugin_item(_) click to toggle source
# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 337
def end_plugin_item(_)
        @plugin_selection.update(:plugin_id => @vals["PluginId"],
                :plugin_name => @vals["PluginName"],
                :family => @vals["Family"], :status => @vals["Status"])
end
end_policy_comments(element) click to toggle source
# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 294
def end_policy_comments(element)
        return unless element == "policyComments" || element == "policy_comments"
        @policy.update(:comments => @vals[element])
end
end_policy_name(_) click to toggle source

Dynamic dispatch end element methods

# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 290
def end_policy_name(_)
        @policy.update(:name => @vals["policyName"])
end
end_policy_owner(_) click to toggle source
# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 299
def end_policy_owner(_)
        @policy.update(:owner => @vals["policyOwner"])
end
end_preference(_) click to toggle source
# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 307
def end_preference(_)
        @sp.update(:name => @vals["name"], :value => @vals["value"])
end
end_report_item(_) click to toggle source
# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 365
def end_report_item(_)
        @ri.update(:plugin_output => @vals["plugin_output"],
                :plugin_name => @vals["plugin_name"],
                :cm_compliance_info => @vals["cm:compliance-info"],
                :cm_compliance_actual_value => @vals["cm:compliance-actual-value"],
                :cm_compliance_check_id => @vals["cm:compliance-check-id"],
                :cm_compliance_policy_value => @vals["cm:compliance-policy-value"],
                :cm_compliance_audit_file => @vals["cm:compliance-audit-file"],
                :cm_compliance_check_name => @vals["cm:compliance-check-name"],
                :cm_compliance_result => @vals["cm:compliance-result"],
                :cm_compliance_output => @vals["cm:compliance-output"],
                :cm_compliance_reference => @vals["cm:compliance-reference"],
                :cm_compliance_see_also => @vals["cm:compliance-see-also" ],
                :cm_compliance_solution => @vals["cm:compliance-solution"],
                :rollup_finding => false
        )

        @plugin.update(:solution => @vals["solution"],
                :risk_factor => @vals["risk_factor"],
                :description => @vals["description"],
                :plugin_publication_date => @vals["plugin_publication_date"],
                :plugin_modification_date => @vals["plugin_modification_date"],
                :synopsis => @vals["synopsis"],
                :plugin_type => @vals["plugin_type"],
                :cvss_vector => @vals["cvss_vector"],
                :cvss_base_score => @vals["cvss_base_score"].to_f,
                :vuln_publication_date => @vals["vuln_publication_date"],
                :plugin_version => @vals["plugin_version"],
                :cvss_temporal_score => @vals["cvss_temporal_score"],
                :cvss_temporal_vector => @vals["cvss_temporal_vector"],
                :exploitability_ease => @vals["exploitability_ease"],
                :exploit_framework_core => @vals["exploit_framework_core"],
                :exploit_available => @vals["exploit_available"] == "true",
                :exploit_framework_metasploit => @vals["exploit_framework_metasploit"],
                :metasploit_name => @vals["metasploit_name"],
                :exploit_framework_canvas => @vals["exploit_framework_canvas"],
                :canvas_package => @vals["canvas_package"],
                :exploit_framework_exploithub => @vals["exploit_framework_exploithub"],
                :exploithub_sku => @vals["exploithub_sku"],
                :stig_severity => @vals["stig_severity"],
                :fname => @vals["fname"],
                :always_run => @vals["always_run"],
                :script_version => @vals["script_version"],
                :exploited_by_malware => @vals["exploited_by_malware"],
                :compliance => @vals["compliance"],
                :agent => @vals["agent"],
                :in_the_news => @vals["in_the_news"]

        )
end
end_tag(_) click to toggle source
# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 343
def end_tag(_)
        return if @attr.nil?

        if @attr =~ /[M|m][S|s]\d{2}-\d{2,}/
                @patch = @rh.patches.create(:name => @attr, :value => @vals['tag'])
        else
                if HOST_PROPERTIES_MAPPING.key?(@attr)
                        @rh.update(HOST_PROPERTIES_MAPPING[@attr] => @vals["tag"].gsub("\n", ","))
                end

                @hp.update(:name => @attr, :value => @vals['tag'])
        end
end
end_valid_reference(element) click to toggle source

We cannot handle the references in the same block as the rest of the ReportItem tag because there tends to be more than of the different types of reference per ReportItem, this causes issue for a sax parser. To solve this we do the references before the final plugin data, Valid references must be added the VALID_REFERENCE set at the top to be parsed.

# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 361
def end_valid_reference(element)
        @ref = @plugin.references.create(:reference_name => element, :value => @vals["#{element}"])
end
end_visibility(_) click to toggle source
# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 303
def end_visibility(_)
        @policy.update(:visibility => @vals["visibility"])
end
start_attachment(_element, attributes) click to toggle source
# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 285
def start_attachment(_element, attributes)
        @attachment = @ri.attachments.create(:name => attributes['name'], :ttype => attributes['type'])
end
start_family_item(_element, _attributes) click to toggle source
# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 220
def start_family_item(_element, _attributes)
        @family = @policy.family_selections.create
end
start_item(_element, _attributes) click to toggle source
# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 216
def start_item(_element, _attributes)
        @item = @policy.plugins_preferences.create
end
start_plugin_item(_element, _attributes) click to toggle source
# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 224
def start_plugin_item(_element, _attributes)
        @plugin_selection = @policy.individual_plugin_selections.create
end
start_policy(_element, _attributes) click to toggle source

Dynamic dispatch start element methods

# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 208
def start_policy(_element, _attributes)
        @policy = Risu::Models::Policy.create
end
start_preference(_element, _attributes) click to toggle source
# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 212
def start_preference(_element, _attributes)
        @sp = @policy.server_preferences.create
end
start_report(_element, attributes) click to toggle source
# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 228
def start_report(_element, attributes)
        @report = @policy.reports.create(:name => attributes["name"])
end
start_report_host(_element, attributes) click to toggle source
# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 232
def start_report_host(_element, attributes)
        @rh = @report.hosts.create(:name => attributes["name"])
end
start_report_item(_element, attributes) click to toggle source
# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 263
def start_report_item(_element, attributes)
        @vals = Hash.new # have to clear this out or everything has the same references

        if attributes["pluginID"] == "0"
                @plugin = Risu::Models::Plugin.find_or_create_by(:id => 1)
        else
                @plugin = Risu::Models::Plugin.find_or_create_by(:id => attributes["pluginID"]) do |plugin|
                        plugin.plugin_name = attributes["pluginName"]
                        plugin.family_name = attributes["pluginFamily"]
                end
        end

        @ri = @rh.items.create(:port => attributes["port"],
                                        :svc_name => attributes["svc_name"],
                                        :protocol => attributes["protocol"],
                                        :severity => attributes["severity"],
                                        :plugin_id => @plugin.id
                                )

        @plugin.save
end
start_tag(_, attributes) click to toggle source
# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 236
def start_tag(_, attributes)
        @attr = nil
        @hp = @rh.host_properties.create

        if attributes["name"] =~ /[M|m][S|s]\d{2,}-\d{2,}/
                @attr = attributes["name"]
        #Ugly as fuck. Really this needs to be rewritten. Fuck.
        elsif attributes['name'] =~ /
                                        (?:patch-summary-cve-num)
                                        | (?:patch-summary-cves)
                                        | (?:patch-summary-txt)
                                        | (?:cpe-\d+)
                                        | (?:KB\d+)
                                /x
                @attr = attributes["name"]
        elsif VALID_HOST_PROPERTIES.include?(attributes["name"])
                @attr = attributes["name"]
        end

        # implicit nil check?
        if attributes["name"] !~ /(netstat-(?:established|listen)-(?:tcp|udp)\d+-\d+)/ \
                        && attributes["name"] !~ /traceroute-hop-\d+/ \
                        && @attr.nil?
                @new_tags << "New HostProperties attribute: #{attributes["name"]}. Please report this at #{Risu::GITHUB}/issues/new or via email to #{Risu::EMAIL}\n"
        end
end