class Risu::Parsers::Nessus::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” elementelement = "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” elementelement = "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
Public Class Methods
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
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
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
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
# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 416 def end_attachment(_) @attachment.update(:ahash => @vals['attachment']) end
# 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
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
# 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
# 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
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
# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 299 def end_policy_owner(_) @policy.update(:owner => @vals["policyOwner"]) end
# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 307 def end_preference(_) @sp.update(:name => @vals["name"], :value => @vals["value"]) end
# 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
# 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
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
# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 303 def end_visibility(_) @policy.update(:visibility => @vals["visibility"]) end
# 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
# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 220 def start_family_item(_element, _attributes) @family = @policy.family_selections.create end
# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 216 def start_item(_element, _attributes) @item = @policy.plugins_preferences.create end
# 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
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
# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 212 def start_preference(_element, _attributes) @sp = @policy.server_preferences.create end
# File lib/risu/parsers/nessus/nessus_sax_listener.rb, line 228 def start_report(_element, attributes) @report = @policy.reports.create(:name => attributes["name"]) end
# 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
# 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
# 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