Class: HeimdallTools::ScoutSuiteMapper

Inherits:
Object
  • Object
show all
Defined in:
lib/heimdall_tools/scoutsuite_mapper.rb

Overview

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

Instance Method Summary collapse

Constructor Details

#initialize(scoutsuite_js) ⇒ ScoutSuiteMapper

Returns a new instance of ScoutSuiteMapper.



39
40
41
42
43
44
45
46
47
48
49
50
51
52
# 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

Instance Method Details

#compliance(arr) ⇒ Object



123
124
125
126
127
128
129
# 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) ⇒ Object



59
60
61
# 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) ⇒ Object



102
103
104
# 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) ⇒ Object



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# 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) ⇒ Object



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# 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) ⇒ Object



98
99
100
# File 'lib/heimdall_tools/scoutsuite_mapper.rb', line 98

def impact(severity)
  IMPACT_MAPPING[severity.to_sym]
end

#nist_tag(rule) ⇒ Object



92
93
94
95
96
# 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_mapperObject



54
55
56
57
# 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_hdfObject



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# 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