class HeimdallTools::JfrogXrayMapper

Public Class Methods

new(xray_json, _name = nil) click to toggle source
# File lib/heimdall_tools/jfrog_xray_mapper.rb, line 30
def initialize(xray_json, _name = nil)
  @xray_json = xray_json

  begin
    @cwe_nist_mapping = parse_mapper
    @project = JSON.parse(xray_json)
  rescue StandardError => e
    raise "Invalid JFrog Xray JSON file provided Exception: #{e}"
  end
end

Public Instance Methods

collapse_duplicates(controls) click to toggle source

Xray report could have multiple vulnerability 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/jfrog_xray_mapper.rb, line 98
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/jfrog_xray_mapper.rb, line 91
def desc_tags(data, label)
  { data: data || NA_STRING, label: label || NA_STRING }
end
finding(vulnerability) click to toggle source
# File lib/heimdall_tools/jfrog_xray_mapper.rb, line 41
def finding(vulnerability)
  finding = {}
  finding['status'] = 'failed'
  finding['code_desc'] = []
  finding['code_desc'] << "source_comp_id : #{vulnerability['source_comp_id']}"
  finding['code_desc'] << "vulnerable_versions : #{vulnerability['component_versions']['vulnerable_versions']}"
  finding['code_desc'] << "fixed_versions : #{vulnerability['component_versions']['fixed_versions']}"
  finding['code_desc'] << "issue_type : #{vulnerability['issue_type']}"
  finding['code_desc'] << "provider : #{vulnerability['provider']}"
  finding['code_desc'] = finding['code_desc'].join("\n")
  finding['run_time'] = NA_FLOAT

  # Xray results does not profile scan timestamp; using current time to satisfy HDF format
  finding['start_time'] = NA_STRING
  [finding]
end
format_control_desc(vulnerability) click to toggle source
# File lib/heimdall_tools/jfrog_xray_mapper.rb, line 58
def format_control_desc(vulnerability)
  text = []
  info = vulnerability['component_versions']['more_details']
  text << info['description'].to_s
  text << "cves: #{info['cves']}" unless info['cves'].nil?
  text.join('<br>')
end
impact(severity) click to toggle source
# File lib/heimdall_tools/jfrog_xray_mapper.rb, line 79
def impact(severity)
  IMPACT_MAPPING[severity.downcase.to_sym]
end
nist_tag(cweid) click to toggle source
# File lib/heimdall_tools/jfrog_xray_mapper.rb, line 66
def nist_tag(cweid)
  entries = @cwe_nist_mapping.select { |x| cweid.include?(x[:cweid].to_s) && !x[:nistid].nil? }
  tags = entries.map { |x| x[:nistid] }
  tags.empty? ? DEFAULT_NIST_TAG : tags.flatten.uniq
end
parse_identifiers(vulnerability, ref) click to toggle source
# File lib/heimdall_tools/jfrog_xray_mapper.rb, line 72
def parse_identifiers(vulnerability, ref)
  # Extracting id number from reference style CWE-297
  vulnerability['component_versions']['more_details']['cves'][0][ref.downcase].map { |e| e.split("#{ref}-")[1] }
rescue StandardError
  []
end
parse_mapper() click to toggle source
# File lib/heimdall_tools/jfrog_xray_mapper.rb, line 83
def parse_mapper
  csv_data = CSV.read(CWE_NIST_MAPPING_FILE, **{ encoding: 'UTF-8',
                                               headers: true,
                                               header_converters: :symbol,
                                               converters: :all })
  csv_data.map(&:to_hash)
end
to_hdf() click to toggle source
# File lib/heimdall_tools/jfrog_xray_mapper.rb, line 110
def to_hdf
  controls = []
  vulnerability_count = 0
  @project['data'].uniq.each do |vulnerability|
    printf("\rProcessing: %s", $spinner.next)

    vulnerability_count +=1
    item = {}
    item['tags']               = {}
    item['descriptions']       = []
    item['refs']               = NA_ARRAY
    item['source_location']    = NA_HASH
    item['descriptions']       = NA_ARRAY

    # Xray JSONs might note have `id` fields populated.
    # If thats a case MD5 hash is used to collapse vulnerability findings of the same type.
    item['id']                 = vulnerability['id'].empty? ? OpenSSL::Digest::MD5.digest(vulnerability['summary'].to_s).unpack1('H*').to_s : vulnerability['id']
    item['title']              = vulnerability['summary'].to_s
    item['desc']               = format_control_desc(vulnerability)
    item['impact']             = impact(vulnerability['severity'].to_s)
    item['code']               = NA_STRING
    item['results']            = finding(vulnerability)

    item['tags']['nist']       = nist_tag(parse_identifiers(vulnerability, 'CWE'))
    item['tags']['cweid']      = parse_identifiers(vulnerability, 'CWE')

    controls << item
  end

  controls = collapse_duplicates(controls)
  results = HeimdallDataFormat.new(profile_name: 'JFrog Xray Scan',
                                   version: NA_STRING,
                                   title: 'JFrog Xray Scan',
                                   summary: 'Continuous Security and Universal Artifact Analysis',
                                   controls: controls)
  results.to_hdf
end