class HeimdallTools::ScoutSuiteMapper

currently only tested against an AWS based result, but ScoutSuite supports many other cloud providers such as Azure

Public Class Methods

new(scoutsuite_js) click to toggle source
# File lib/heimdall_tools/scoutsuite_mapper.rb, line 39
def initialize(scoutsuite_js)
  begin
    @scoutsuite_nist_mapping = parse_mapper
  rescue StandardError => e
    raise "Invalid Scout Suite to NIST mapping file:\nException: #{e}"
  end

  begin
    @scoutsuite_json = scoutsuite_js.lines[1] # first line is `scoutsuite_results =\n` and second line is json
    @report = JSON.parse(@scoutsuite_json)
  rescue StandardError => e
    raise "Invalid Scout Suite JavaScript file provided:\nException: #{e}"
  end
end

Public Instance Methods

compliance(arr) click to toggle source
# File lib/heimdall_tools/scoutsuite_mapper.rb, line 123
def compliance(arr)
  str = 'Compliant with '
  arr.map do |val|
    info = "#{val['name']}, reference #{val['reference']}, version #{val['version']}"
    str + info
  end.join("\n")
end
create_attribute(name, value, required = nil, sensitive = nil, type = nil) click to toggle source
# File lib/heimdall_tools/scoutsuite_mapper.rb, line 59
def create_attribute(name, value, required = nil, sensitive = nil, type = nil)
  { name: name, options: { value: value, required: required, sensitive: sensitive, type: type }.compact }
end
desc_tags(data, label) click to toggle source
# File lib/heimdall_tools/scoutsuite_mapper.rb, line 102
def desc_tags(data, label)
  { data: data || NA_STRING, label: label || NA_STRING }
end
extract_scaninfo(report) click to toggle source
# File lib/heimdall_tools/scoutsuite_mapper.rb, line 63
def extract_scaninfo(report)
  info = {}
  begin
    info['name'] = 'Scout Suite Multi-Cloud Security Auditing Tool'
    info['version'] = report['last_run']['version']
    info['title'] = "Scout Suite Report using #{report['last_run']['ruleset_name']} ruleset on #{report['provider_name']} with account #{report['account_id']}"
    info['target_id'] = "#{report['last_run']['ruleset_name']} ruleset:#{report['provider_name']}:#{report['account_id']}"
    info['summary'] = report['last_run']['ruleset_about']
    info['attributes'] = [
      create_attribute('account_id', report['account_id'], true, false, INSPEC_INPUTS_MAPPING[:string]),
      create_attribute('environment', report['environment']),
      create_attribute('ruleset', report['ruleset_name']),
      # think at least these run_parameters are aws only
      create_attribute('run_parameters_excluded_regions', report['last_run']['run_parameters']['excluded_regions'].join(', ')),
      create_attribute('run_parameters_regions', report['last_run']['run_parameters']['regions'].join(', ')),
      create_attribute('run_parameters_services', report['last_run']['run_parameters']['services'].join(', ')),
      create_attribute('run_parameters_skipped_services', report['last_run']['run_parameters']['skipped_services'].join(', ')),
      create_attribute('time', report['last_run']['time']),
      create_attribute('partition', report['partition']), # think this is aws only
      create_attribute('provider_code', report['provider_code']),
      create_attribute('provider_name', report['provider_name']),
    ]

    info
  rescue StandardError => e
    raise "Error extracting report info from Scout Suite JS->JSON file:\nException: #{e}"
  end
end
findings(details) click to toggle source
# File lib/heimdall_tools/scoutsuite_mapper.rb, line 106
def findings(details)
  finding = {}
  if (details['checked_items']).zero?
    finding['status'] = 'skipped'
    finding['skip_message'] = 'Skipped because no items were checked'
  elsif (details['flagged_items']).zero?
    finding['status'] = 'passed'
    finding['message'] = "0 flagged items out of #{details['checked_items']} checked items"
  else # there are checked items and things were flagged
    finding['status'] = 'failed'
    finding['message'] = "#{details['flagged_items']} flagged items out of #{details['checked_items']} checked items:\n#{details['items'].join("\n")}"
  end
  finding['code_desc'] = details['description']
  finding['start_time'] = @report['last_run']['time']
  [finding]
end
impact(severity) click to toggle source
# File lib/heimdall_tools/scoutsuite_mapper.rb, line 98
def impact(severity)
  IMPACT_MAPPING[severity.to_sym]
end
nist_tag(rule) click to toggle source
# File lib/heimdall_tools/scoutsuite_mapper.rb, line 92
def nist_tag(rule)
  entries = @scoutsuite_nist_mapping.select { |x| rule.eql?(x[:rule].to_s) && !x[:nistid].nil? }
  tags = entries.map { |x| x[:nistid].split('|') }
  tags.empty? ? DEFAULT_NIST_TAG : tags.flatten.uniq
end
parse_mapper() click to toggle source
# File lib/heimdall_tools/scoutsuite_mapper.rb, line 54
def parse_mapper
  csv_data = CSV.read(SCOUTSUITE_NIST_MAPPING_FILE, { encoding: 'UTF-8', headers: true, header_converters: :symbol })
  csv_data.map(&:to_hash)
end
to_hdf() click to toggle source
# File lib/heimdall_tools/scoutsuite_mapper.rb, line 131
def to_hdf
  controls = []
  @report['services'].each_key do |service|
    @report['services'][service]['findings'].each_key do |finding|
      printf("\rProcessing: %s", $spinner.next)

      finding_id = finding
      finding_details = @report['services'][service]['findings'][finding]

      item = {}
      item['id']                 = finding_id
      item['title']              = finding_details['description']

      item['tags']               = { nist: nist_tag(finding_id) }

      item['impact']             = impact(finding_details['level'])

      item['desc']               = finding_details['rationale']

      item['descriptions']       = []
      item['descriptions']       << desc_tags(finding_details['remediation'], 'fix') unless finding_details['remediation'].nil?
      item['descriptions']       << desc_tags(finding_details['service'], 'service')
      item['descriptions']       << desc_tags(finding_details['path'], 'path')
      item['descriptions']       << desc_tags(finding_details['id_suffix'], 'id_suffix')

      item['refs']               = []
      item['refs']               += finding_details['references'].map { |link| { url: link } } unless finding_details['references'].nil? || finding_details['references'].empty?
      item['refs']               << { ref: compliance(finding_details['compliance']) } unless finding_details['compliance'].nil?

      item['source_location']    = NA_HASH
      item['code']               = NA_STRING

      item['results']            = findings(finding_details)

      controls << item
    end
  end

  scaninfo = extract_scaninfo(@report)
  results = HeimdallDataFormat.new(profile_name: scaninfo['name'],
                                   version: scaninfo['version'],
                                   title: scaninfo['title'],
                                   summary: scaninfo['summary'],
                                   controls: controls,
                                   target_id: scaninfo['target_id'],
                                   attributes: scaninfo['attributes'])
  results.to_hdf
end