class HeimdallTools::NetsparkerMapper

Public Class Methods

new(xml, _name = nil) click to toggle source
# File lib/heimdall_tools/netsparker_mapper.rb, line 24
def initialize(xml, _name = nil)
  @cwe_nist_mapping = parse_mapper(CWE_NIST_MAPPING_FILE)
  @owasp_nist_mapping = parse_mapper(OWASP_NIST_MAPPING_FILE)
  data = xml_to_hash(xml)

  @vulnerabilities = data['netsparker-enterprise']['vulnerabilities']['vulnerability']
  @scan_info = data['netsparker-enterprise']['target']
rescue StandardError => e
  raise "Invalid Netsparker XML file provided Exception: #{e}"
end

Public Instance Methods

to_hdf() click to toggle source
# File lib/heimdall_tools/netsparker_mapper.rb, line 35
def to_hdf
  controls = []
  @vulnerabilities.each do |vulnerability|
    @item = {}
    @item['id']                 = vulnerability['LookupId'].to_s
    @item['title']              = vulnerability['name'].to_s
    @item['desc']               = format_control_desc(vulnerability)
    @item['impact']             = impact(vulnerability['severity'])
    @item['tags']               = {}
    @item['descriptions']       = []

    @item['descriptions']       <<  desc_tags(format_check_text(vulnerability), 'check')
    @item['descriptions']       <<  desc_tags(format_fix_text(vulnerability), 'fix')
    @item['refs']               = NA_ARRAY
    @item['source_location']    = NA_HASH
    @item['tags']['nist']       = nist_tag(vulnerability['classification'])
    @item['code']               = ''
    @item['results']            = finding(vulnerability)

    controls << @item
  end
  controls = collapse_duplicates(controls)
  results = HeimdallDataFormat.new(profile_name: 'Netsparker Enterprise Scan',
                                   title: "Netsparker Enterprise Scan ID: #{@scan_info['scan-id']} URL: #{@scan_info['url']}",
                                   summary: 'Netsparker Enterprise Scan',
                                   target_id: @scan_info['url'],
                                   controls: controls)
  results.to_hdf
end

Private Instance Methods

collapse_duplicates(controls) click to toggle source

Netsparker report could have multiple issue entries for multiple findings of same issue type. The meta data is identical across entries method collapse_duplicates return unique controls with applicable findings collapsed into it.

# File lib/heimdall_tools/netsparker_mapper.rb, line 148
def collapse_duplicates(controls)
  unique_controls = []

  controls.map { |x| x['id'] }.uniq.each do |id|
    collapsed_results = controls.select { |x| x['id'].eql?(id) }.map { |x| x['results'] }
    unique_control = controls.find { |x| x['id'].eql?(id) }
    unique_control['results'] = collapsed_results.flatten
    unique_controls << unique_control
  end
  unique_controls
end
desc_tags(data, label) click to toggle source
# File lib/heimdall_tools/netsparker_mapper.rb, line 141
def desc_tags(data, label)
  { data: data || NA_STRING, label: label || NA_STRING }
end
finding(vulnerability) click to toggle source
# File lib/heimdall_tools/netsparker_mapper.rb, line 71
def finding(vulnerability)
  finding = {}
  finding['status'] = 'failed'
  finding['code_desc'] = []
  finding['code_desc'] << "http-request : #{parse_html(vulnerability['http-request']['content'])}"
  finding['code_desc'] << "method : #{vulnerability['http-request']['method']}"
  finding['code_desc'] = finding['code_desc'].join("\n")

  finding['message'] = []
  finding['message'] << "http-response : #{parse_html(vulnerability['http-response']['content'])}"
  finding['message'] << "duration : #{vulnerability['http-response']['duration']}"
  finding['message'] << "status-code : #{vulnerability['http-response']['status-code']}"
  finding['message'] = finding['message'].join("\n")
  finding['run_time'] = NA_FLOAT

  finding['start_time'] = @scan_info['initiated']
  [finding]
end
format_check_text(vulnerability) click to toggle source
# File lib/heimdall_tools/netsparker_mapper.rb, line 105
def format_check_text(vulnerability)
  text = []
  text << "Exploitation-skills: #{parse_html(vulnerability['exploitation-skills'])}" unless vulnerability['exploitation-skills'].nil?
  text << "Proof-of-concept: #{parse_html(vulnerability['proof-of-concept'])}" unless vulnerability['proof-of-concept'].nil?
  text.join('<br>')
end
format_control_desc(vulnerability) click to toggle source
# File lib/heimdall_tools/netsparker_mapper.rb, line 90
def format_control_desc(vulnerability)
  text = []
  text << parse_html(vulnerability['description']).to_s unless vulnerability['description'].nil?
  text << "Exploitation-skills: #{parse_html(vulnerability['exploitation-skills'])}" unless vulnerability['exploitation-skills'].nil?
  text << "Extra-information: #{vulnerability['extra-information']}" unless vulnerability['extra-information'].nil?
  text << "Classification: #{vulnerability['classification']}" unless vulnerability['classification'].nil?
  text << "Impact: #{parse_html(vulnerability['impact'])}" unless vulnerability['impact'].nil?
  text << "FirstSeenDate: #{vulnerability['FirstSeenDate']}" unless vulnerability['FirstSeenDate'].nil?
  text << "LastSeenDate: #{vulnerability['LastSeenDate']}" unless vulnerability['LastSeenDate'].nil?
  text << "Certainty: #{vulnerability['certainty']}" unless vulnerability['certainty'].nil?
  text << "Type: #{vulnerability['type']}" unless vulnerability['type'].nil?
  text << "Confirmed: #{vulnerability['confirmed']}" unless vulnerability['confirmed'].nil?
  text.join('<br>')
end
format_fix_text(vulnerability) click to toggle source
# File lib/heimdall_tools/netsparker_mapper.rb, line 112
def format_fix_text(vulnerability)
  text = []
  text << "Remedial-actions: #{parse_html(vulnerability['remedial-actions'])}" unless vulnerability['remedial-actions'].nil?
  text << "Remedial-procedure: #{parse_html(vulnerability['remedial-procedure'])}" unless vulnerability['remedial-procedure'].nil?
  text << "Remedy-references: #{parse_html(vulnerability['remedy-references'])}" unless vulnerability['remedy-references'].nil?
  text.join('<br>')
end
impact(severity) click to toggle source
# File lib/heimdall_tools/netsparker_mapper.rb, line 129
def impact(severity)
  IMPACT_MAPPING[severity.to_sym]
end
nist_tag(classification) click to toggle source
# File lib/heimdall_tools/netsparker_mapper.rb, line 120
def nist_tag(classification)
  tags = []
  entries = @cwe_nist_mapping.select { |x| classification['cwe'].include?(x[:cweid].to_s) && !x[:nistid].nil? }
  tags << entries.map { |x| x[:nistid] }
  entries = @owasp_nist_mapping.select { |x| classification['owasp'].include?(x[:owaspid].to_s) && !x[:nistid].nil? }
  tags << entries.map { |x| x[:nistid] }
  tags.flatten.empty? ? DEFAULT_NIST_TAG : tags.flatten.uniq
end
parse_html(block) click to toggle source
# File lib/heimdall_tools/netsparker_mapper.rb, line 67
def parse_html(block)
  block['#cdata-section'].to_s.strip unless block.nil?
end
parse_mapper(mapping_file) click to toggle source
# File lib/heimdall_tools/netsparker_mapper.rb, line 133
def parse_mapper(mapping_file)
  csv_data = CSV.read(mapping_file, { encoding: 'UTF-8',
                                               headers: true,
                                               header_converters: :symbol,
                                               converters: :all })
  csv_data.map(&:to_hash)
end