heimdall_tools 1.3.41 → 1.3.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +51 -2
- data/lib/data/aws-config-mapping.csv +107 -107
- data/lib/data/scoutsuite-nist-mapping.csv +140 -0
- data/lib/heimdall_tools.rb +2 -0
- data/lib/heimdall_tools/aws_config_mapper.rb +35 -17
- data/lib/heimdall_tools/burpsuite_mapper.rb +1 -2
- data/lib/heimdall_tools/cli.rb +23 -11
- data/lib/heimdall_tools/dbprotect_mapper.rb +5 -9
- data/lib/heimdall_tools/fortify_mapper.rb +1 -2
- data/lib/heimdall_tools/help/sarif_mapper.md +12 -0
- data/lib/heimdall_tools/help/scoutsuite_mapper.md +7 -0
- data/lib/heimdall_tools/jfrog_xray_mapper.rb +1 -2
- data/lib/heimdall_tools/nessus_mapper.rb +3 -3
- data/lib/heimdall_tools/netsparker_mapper.rb +9 -13
- data/lib/heimdall_tools/nikto_mapper.rb +1 -2
- data/lib/heimdall_tools/sarif_mapper.rb +198 -0
- data/lib/heimdall_tools/scoutsuite_mapper.rb +180 -0
- data/lib/heimdall_tools/snyk_mapper.rb +1 -2
- data/lib/heimdall_tools/zap_mapper.rb +1 -4
- metadata +10 -5
data/lib/heimdall_tools.rb
CHANGED
@@ -16,4 +16,6 @@ module HeimdallTools
|
|
16
16
|
autoload :DBProtectMapper, 'heimdall_tools/dbprotect_mapper'
|
17
17
|
autoload :AwsConfigMapper, 'heimdall_tools/aws_config_mapper'
|
18
18
|
autoload :NetsparkerMapper, 'heimdall_tools/netsparker_mapper'
|
19
|
+
autoload :SarifMapper, 'heimdall_tools/sarif_mapper'
|
20
|
+
autoload :ScoutSuiteMapper, 'heimdall_tools/scoutsuite_mapper'
|
19
21
|
end
|
@@ -18,11 +18,14 @@ INSUFFICIENT_DATA_MSG = 'Not enough data has been collectd to determine complian
|
|
18
18
|
#
|
19
19
|
module HeimdallTools
|
20
20
|
class AwsConfigMapper
|
21
|
-
def initialize(custom_mapping,
|
22
|
-
@verbose = verbose
|
21
|
+
def initialize(custom_mapping, endpoint = nil)
|
23
22
|
@default_mapping = get_rule_mapping(AWS_CONFIG_MAPPING_FILE)
|
24
23
|
@custom_mapping = custom_mapping.nil? ? {} : get_rule_mapping(custom_mapping)
|
25
|
-
|
24
|
+
if endpoint.nil?
|
25
|
+
@client = Aws::ConfigService::Client.new
|
26
|
+
else
|
27
|
+
@client = Aws::ConfigService::Client.new(endpoint: endpoint)
|
28
|
+
end
|
26
29
|
@issues = get_all_config_rules
|
27
30
|
end
|
28
31
|
|
@@ -34,8 +37,8 @@ module HeimdallTools
|
|
34
37
|
def to_hdf
|
35
38
|
controls = @issues.map do |issue|
|
36
39
|
@item = {}
|
37
|
-
@item['id'] = issue[:
|
38
|
-
@item['title'] = issue[:config_rule_name]
|
40
|
+
@item['id'] = issue[:config_rule_id]
|
41
|
+
@item['title'] = "#{get_account_id(issue[:config_rule_arn])} - #{issue[:config_rule_name]}"
|
39
42
|
@item['desc'] = issue[:description]
|
40
43
|
@item['impact'] = 0.5
|
41
44
|
@item['tags'] = hdf_tags(issue)
|
@@ -51,18 +54,33 @@ module HeimdallTools
|
|
51
54
|
@item
|
52
55
|
end
|
53
56
|
end
|
57
|
+
|
54
58
|
results = HeimdallDataFormat.new(
|
55
59
|
profile_name: 'AWS Config',
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
+
title: 'AWS Config',
|
61
|
+
summary: 'AWS Config',
|
62
|
+
controls: controls,
|
63
|
+
statistics: { aws_config_sdk_version: Aws::ConfigService::GEM_VERSION },
|
60
64
|
)
|
61
65
|
results.to_hdf
|
62
66
|
end
|
63
67
|
|
64
68
|
private
|
65
69
|
|
70
|
+
##
|
71
|
+
# Gets the account ID from a config rule ARN
|
72
|
+
#
|
73
|
+
# https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html
|
74
|
+
# https://docs.aws.amazon.com/general/latest/gr/acct-identifiers.html
|
75
|
+
#
|
76
|
+
# Params:
|
77
|
+
# - arn: The ARN of the config rule
|
78
|
+
#
|
79
|
+
# Returns: The account ID portion of the ARN
|
80
|
+
def get_account_id(arn)
|
81
|
+
/:(\d{12}):config-rule/.match(arn)&.captures&.first || 'no-account-id'
|
82
|
+
end
|
83
|
+
|
66
84
|
##
|
67
85
|
# Read in a config rule -> 800-53 control mapping CSV.
|
68
86
|
#
|
@@ -71,7 +89,7 @@ module HeimdallTools
|
|
71
89
|
#
|
72
90
|
# Returns: A mapped version of the csv in the format { rule_name: row, ... }
|
73
91
|
def get_rule_mapping(path)
|
74
|
-
CSV.read(path, headers: true).map { |row| [row[
|
92
|
+
CSV.read(path, headers: true).map { |row| [row['AwsConfigRuleSourceIdentifier'], row] }.to_h
|
75
93
|
end
|
76
94
|
|
77
95
|
##
|
@@ -238,18 +256,17 @@ module HeimdallTools
|
|
238
256
|
def hdf_tags(config_rule)
|
239
257
|
result = {}
|
240
258
|
|
241
|
-
|
242
|
-
@custom_mapping
|
259
|
+
source_identifier = config_rule.dig(:source, :source_identifier)
|
243
260
|
|
244
261
|
# NIST tag
|
245
262
|
result['nist'] = []
|
246
|
-
default_mapping_match = @default_mapping[
|
263
|
+
default_mapping_match = @default_mapping[source_identifier]
|
247
264
|
|
248
|
-
result['nist'] += default_mapping_match[
|
265
|
+
result['nist'] += default_mapping_match['NIST-ID'].split('|') unless default_mapping_match.nil?
|
249
266
|
|
250
|
-
custom_mapping_match = @custom_mapping[
|
267
|
+
custom_mapping_match = @custom_mapping[source_identifier]
|
251
268
|
|
252
|
-
result['nist'] += custom_mapping_match[
|
269
|
+
result['nist'] += custom_mapping_match['NIST-ID'].split('|').map { |name| "#{name} (user provided)" } unless custom_mapping_match.nil?
|
253
270
|
|
254
271
|
result['nist'] = ['unmapped'] if result['nist'].empty?
|
255
272
|
|
@@ -260,7 +277,8 @@ module HeimdallTools
|
|
260
277
|
# If no input parameters, then provide an empty JSON array to the JSON
|
261
278
|
# parser because passing nil to JSON.parse throws an exception.
|
262
279
|
params = (JSON.parse(config_rule[:input_parameters] || '[]').map { |key, value| "#{key}: #{value}" }).join('<br/>')
|
263
|
-
check_text = config_rule[:config_rule_arn] || ''
|
280
|
+
check_text = "ARN: #{config_rule[:config_rule_arn] || 'N/A'}"
|
281
|
+
check_text += "<br/>Source Identifier: #{config_rule.dig(:source, :source_identifier) || 'N/A'}"
|
264
282
|
check_text += "<br/>#{params}" unless params.empty?
|
265
283
|
check_text
|
266
284
|
end
|
@@ -20,9 +20,8 @@ DEFAULT_NIST_TAG = %w{SA-11 RA-5 Rev_4}.freeze
|
|
20
20
|
|
21
21
|
module HeimdallTools
|
22
22
|
class BurpSuiteMapper
|
23
|
-
def initialize(burps_xml, _name = nil
|
23
|
+
def initialize(burps_xml, _name = nil)
|
24
24
|
@burps_xml = burps_xml
|
25
|
-
@verbose = verbose
|
26
25
|
|
27
26
|
begin
|
28
27
|
@cwe_nist_mapping = parse_mapper
|
data/lib/heimdall_tools/cli.rb
CHANGED
@@ -6,7 +6,6 @@ module HeimdallTools
|
|
6
6
|
long_desc Help.text(:fortify_mapper)
|
7
7
|
option :fvdl, required: true, aliases: '-f'
|
8
8
|
option :output, required: true, aliases: '-o'
|
9
|
-
option :verbose, type: :boolean, aliases: '-V'
|
10
9
|
def fortify_mapper
|
11
10
|
hdf = HeimdallTools::FortifyMapper.new(File.read(options[:fvdl])).to_hdf
|
12
11
|
File.write(options[:output], hdf)
|
@@ -17,7 +16,6 @@ module HeimdallTools
|
|
17
16
|
option :json, required: true, aliases: '-j'
|
18
17
|
option :name, required: true, aliases: '-n'
|
19
18
|
option :output, required: true, aliases: '-o'
|
20
|
-
option :verbose, type: :boolean, aliases: '-V'
|
21
19
|
def zap_mapper
|
22
20
|
hdf = HeimdallTools::ZapMapper.new(File.read(options[:json]), options[:name]).to_hdf
|
23
21
|
File.write(options[:output], hdf)
|
@@ -29,7 +27,6 @@ module HeimdallTools
|
|
29
27
|
option :api_url, required: true, aliases: '-u'
|
30
28
|
option :auth, type: :string, required: false
|
31
29
|
option :output, required: true, aliases: '-o'
|
32
|
-
option :verbose, type: :boolean, aliases: '-V'
|
33
30
|
def sonarqube_mapper
|
34
31
|
hdf = HeimdallTools::SonarQubeMapper.new(options[:name], options[:api_url], options[:auth]).to_hdf
|
35
32
|
File.write(options[:output], hdf)
|
@@ -39,7 +36,6 @@ module HeimdallTools
|
|
39
36
|
long_desc Help.text(:burpsuite_mapper)
|
40
37
|
option :xml, required: true, aliases: '-x'
|
41
38
|
option :output, required: true, aliases: '-o'
|
42
|
-
option :verbose, type: :boolean, aliases: '-V'
|
43
39
|
def burpsuite_mapper
|
44
40
|
hdf = HeimdallTools::BurpSuiteMapper.new(File.read(options[:xml])).to_hdf
|
45
41
|
File.write(options[:output], hdf)
|
@@ -49,7 +45,6 @@ module HeimdallTools
|
|
49
45
|
long_desc Help.text(:nessus_mapper)
|
50
46
|
option :xml, required: true, aliases: '-x'
|
51
47
|
option :output_prefix, required: true, aliases: '-o'
|
52
|
-
option :verbose, type: :boolean, aliases: '-V'
|
53
48
|
def nessus_mapper
|
54
49
|
hdfs = HeimdallTools::NessusMapper.new(File.read(options[:xml])).to_hdf
|
55
50
|
|
@@ -64,7 +59,6 @@ module HeimdallTools
|
|
64
59
|
long_desc Help.text(:snyk_mapper)
|
65
60
|
option :json, required: true, aliases: '-j'
|
66
61
|
option :output_prefix, required: true, aliases: '-o'
|
67
|
-
option :verbose, type: :boolean, aliases: '-V'
|
68
62
|
def snyk_mapper
|
69
63
|
hdfs = HeimdallTools::SnykMapper.new(File.read(options[:json]), options[:name]).to_hdf
|
70
64
|
puts "\r\HDF Generated:\n"
|
@@ -78,7 +72,6 @@ module HeimdallTools
|
|
78
72
|
long_desc Help.text(:nikto_mapper)
|
79
73
|
option :json, required: true, aliases: '-j'
|
80
74
|
option :output, required: true, aliases: '-o'
|
81
|
-
option :verbose, type: :boolean, aliases: '-V'
|
82
75
|
def nikto_mapper
|
83
76
|
hdf = HeimdallTools::NiktoMapper.new(File.read(options[:json])).to_hdf
|
84
77
|
File.write(options[:output], hdf)
|
@@ -90,7 +83,6 @@ module HeimdallTools
|
|
90
83
|
long_desc Help.text(:jfrog_xray_mapper)
|
91
84
|
option :json, required: true, aliases: '-j'
|
92
85
|
option :output, required: true, aliases: '-o'
|
93
|
-
option :verbose, type: :boolean, aliases: '-V'
|
94
86
|
def jfrog_xray_mapper
|
95
87
|
hdf = HeimdallTools::JfrogXrayMapper.new(File.read(options[:json])).to_hdf
|
96
88
|
File.write(options[:output], hdf)
|
@@ -102,7 +94,6 @@ module HeimdallTools
|
|
102
94
|
long_desc Help.text(:dbprotect_mapper)
|
103
95
|
option :xml, required: true, aliases: '-x'
|
104
96
|
option :output, required: true, aliases: '-o'
|
105
|
-
option :verbose, type: :boolean, aliases: '-V'
|
106
97
|
def dbprotect_mapper
|
107
98
|
hdf = HeimdallTools::DBProtectMapper.new(File.read(options[:xml])).to_hdf
|
108
99
|
File.write(options[:output], hdf)
|
@@ -114,7 +105,6 @@ module HeimdallTools
|
|
114
105
|
long_desc Help.text(:aws_config_mapper)
|
115
106
|
# option :custom_mapping, required: false, aliases: '-m'
|
116
107
|
option :output, required: true, aliases: '-o'
|
117
|
-
option :verbose, type: :boolean, aliases: '-V'
|
118
108
|
def aws_config_mapper
|
119
109
|
hdf = HeimdallTools::AwsConfigMapper.new(options[:custom_mapping]).to_hdf
|
120
110
|
File.write(options[:output], hdf)
|
@@ -126,7 +116,6 @@ module HeimdallTools
|
|
126
116
|
long_desc Help.text(:netsparker_mapper)
|
127
117
|
option :xml, required: true, aliases: '-x'
|
128
118
|
option :output, required: true, aliases: '-o'
|
129
|
-
option :verbose, type: :boolean, aliases: '-V'
|
130
119
|
def netsparker_mapper
|
131
120
|
hdf = HeimdallTools::NetsparkerMapper.new(File.read(options[:xml])).to_hdf
|
132
121
|
File.write(options[:output], hdf)
|
@@ -134,6 +123,29 @@ module HeimdallTools
|
|
134
123
|
puts options[:output].to_s
|
135
124
|
end
|
136
125
|
|
126
|
+
desc 'sarif_mapper', 'sarif_mapper translates a SARIF JSON file into HDF format JSON to be viewable in Heimdall'
|
127
|
+
long_desc Help.text(:sarif_mapper)
|
128
|
+
option :json, required: true, aliases: '-j'
|
129
|
+
option :output, required: true, aliases: '-o'
|
130
|
+
option :verbose, type: :boolean, aliases: '-V'
|
131
|
+
def sarif_mapper
|
132
|
+
hdf = HeimdallTools::SarifMapper.new(File.read(options[:json])).to_hdf
|
133
|
+
File.write(options[:output], hdf)
|
134
|
+
puts "\r\HDF Generated:\n"
|
135
|
+
puts options[:output].to_s
|
136
|
+
end
|
137
|
+
|
138
|
+
desc 'scoutsuite_mapper', 'scoutsuite_mapper translates Scout Suite results from Javascript to HDF-formatted JSON so as to be viewable on Heimdall'
|
139
|
+
long_desc Help.text(:scoutsuite_mapper)
|
140
|
+
option :javascript, required: true, banner: 'SCOUTSUITE-RESULTS-JS', aliases: ['-i', '--input', '-j']
|
141
|
+
option :output, required: true, banner: 'HDF-SCAN-RESULTS-JSON', aliases: '-o'
|
142
|
+
def scoutsuite_mapper
|
143
|
+
hdf = HeimdallTools::ScoutSuiteMapper.new(File.read(options[:javascript])).to_hdf
|
144
|
+
File.write(options[:output], hdf)
|
145
|
+
puts "\rHDF Generated:\n"
|
146
|
+
puts options[:output].to_s
|
147
|
+
end
|
148
|
+
|
137
149
|
desc 'version', 'prints version'
|
138
150
|
def version
|
139
151
|
puts VERSION
|
@@ -12,15 +12,11 @@ IMPACT_MAPPING = {
|
|
12
12
|
|
13
13
|
module HeimdallTools
|
14
14
|
class DBProtectMapper
|
15
|
-
def initialize(xml, _name = nil
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
@entries = compile_findings(dataset['dataset'])
|
21
|
-
rescue StandardError => e
|
22
|
-
raise "Invalid DBProtect XML file provided Exception: #{e};\nNote that XML must be of kind `Check Results Details`."
|
23
|
-
end
|
15
|
+
def initialize(xml, _name = nil)
|
16
|
+
dataset = xml_to_hash(xml)
|
17
|
+
@entries = compile_findings(dataset['dataset'])
|
18
|
+
rescue StandardError => e
|
19
|
+
raise "Invalid DBProtect XML file provided Exception: #{e};\nNote that XML must be of kind `Check Results Details`."
|
24
20
|
end
|
25
21
|
|
26
22
|
def to_hdf
|
@@ -0,0 +1,12 @@
|
|
1
|
+
sarif_mapper translates a SARIF JSON file into HDF format JSON to be viewable in Heimdall
|
2
|
+
|
3
|
+
SARIF level to HDF impact Mapping:
|
4
|
+
SARIF level error -> HDF impact 0.7
|
5
|
+
SARIF level warning -> HDF impact 0.5
|
6
|
+
SARIF level note -> HDF impact 0.3
|
7
|
+
SARIF level none -> HDF impact 0.1
|
8
|
+
SARIF level not provided -> HDF impact 0.1 as default
|
9
|
+
|
10
|
+
Examples:
|
11
|
+
|
12
|
+
heimdall_tools sarif_mapper [OPTIONS] -j <sarif-results-json> -o <hdf-scan-results.json>
|
@@ -0,0 +1,7 @@
|
|
1
|
+
scoutsuite_mapper translates Scout Suite results from Javascript to HDF-formatted JSON so as to be viewable on Heimdall
|
2
|
+
|
3
|
+
Note: Currently this mapper only supports AWS.
|
4
|
+
|
5
|
+
Examples:
|
6
|
+
|
7
|
+
heimdall_tools scoutsuite_mapper -i <scoutsuite-results-js> -o <hdf-scan-results-json>
|
@@ -39,9 +39,8 @@ end
|
|
39
39
|
|
40
40
|
module HeimdallTools
|
41
41
|
class NessusMapper
|
42
|
-
def initialize(nessus_xml
|
42
|
+
def initialize(nessus_xml)
|
43
43
|
@nessus_xml = nessus_xml
|
44
|
-
@verbose = verbose
|
45
44
|
read_cci_xml
|
46
45
|
begin
|
47
46
|
@cwe_nist_mapping = parse_mapper
|
@@ -72,7 +71,8 @@ module HeimdallTools
|
|
72
71
|
info = {}
|
73
72
|
|
74
73
|
info['policyName'] = policy['policyName']
|
75
|
-
|
74
|
+
scanner_version = policy['Preferences']['ServerPreferences']['preference'].select { |x| x['name'].eql? 'sc_version' }
|
75
|
+
info['version'] = scanner_version.empty? ? NA_STRING : scanner_version.first['value']
|
76
76
|
info
|
77
77
|
rescue StandardError => e
|
78
78
|
raise "Invalid Nessus XML file provided Exception: #{e}"
|
@@ -21,19 +21,15 @@ DEFAULT_NIST_TAG = %w{SA-11 RA-5}.freeze
|
|
21
21
|
|
22
22
|
module HeimdallTools
|
23
23
|
class NetsparkerMapper
|
24
|
-
def initialize(xml, _name = nil
|
25
|
-
@
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
@scan_info = data['netsparker-enterprise']['target']
|
34
|
-
rescue StandardError => e
|
35
|
-
raise "Invalid Netsparker XML file provided Exception: #{e}"
|
36
|
-
end
|
24
|
+
def initialize(xml, _name = nil)
|
25
|
+
@cwe_nist_mapping = parse_mapper(CWE_NIST_MAPPING_FILE)
|
26
|
+
@owasp_nist_mapping = parse_mapper(OWASP_NIST_MAPPING_FILE)
|
27
|
+
data = xml_to_hash(xml)
|
28
|
+
|
29
|
+
@vulnerabilities = data['netsparker-enterprise']['vulnerabilities']['vulnerability']
|
30
|
+
@scan_info = data['netsparker-enterprise']['target']
|
31
|
+
rescue StandardError => e
|
32
|
+
raise "Invalid Netsparker XML file provided Exception: #{e}"
|
37
33
|
end
|
38
34
|
|
39
35
|
def to_hdf
|
@@ -0,0 +1,198 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'csv'
|
3
|
+
require 'heimdall_tools/hdf'
|
4
|
+
|
5
|
+
RESOURCE_DIR = Pathname.new(__FILE__).join('../../data')
|
6
|
+
|
7
|
+
CWE_NIST_MAPPING_FILE = File.join(RESOURCE_DIR, 'cwe-nist-mapping.csv')
|
8
|
+
|
9
|
+
IMPACT_MAPPING = {
|
10
|
+
error: 0.7,
|
11
|
+
warning: 0.5,
|
12
|
+
note: 0.3,
|
13
|
+
none: 0.0
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
DEFAULT_NIST_TAG = %w{SA-11 RA-5}.freeze
|
17
|
+
|
18
|
+
# Loading spinner sign
|
19
|
+
$spinner = Enumerator.new do |e|
|
20
|
+
loop do
|
21
|
+
e.yield '|'
|
22
|
+
e.yield '/'
|
23
|
+
e.yield '-'
|
24
|
+
e.yield '\\'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
module HeimdallTools
|
29
|
+
class SarifMapper
|
30
|
+
def initialize(sarif_json, _name = nil, verbose = false)
|
31
|
+
@sarif_json = sarif_json
|
32
|
+
@verbose = verbose
|
33
|
+
begin
|
34
|
+
@cwe_nist_mapping = parse_mapper
|
35
|
+
@sarif_log = JSON.parse(@sarif_json)
|
36
|
+
rescue StandardError => e
|
37
|
+
raise "Invalid SARIF JSON file provided\n\nException: #{e}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def extract_scaninfo(sarif_log)
|
42
|
+
info = {}
|
43
|
+
begin
|
44
|
+
info['policy'] = 'SARIF'
|
45
|
+
info['version'] = sarif_log['version']
|
46
|
+
info['projectName'] = 'Static Analysis Results Interchange Format'
|
47
|
+
info['summary'] = NA_STRING
|
48
|
+
info
|
49
|
+
rescue StandardError => e
|
50
|
+
raise "Error extracting project info from SARIF JSON file provided Exception: #{e}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def finding(result)
|
55
|
+
finding = {}
|
56
|
+
finding['status'] = 'failed'
|
57
|
+
finding['code_desc'] = ''
|
58
|
+
if get_location(result)['uri']
|
59
|
+
finding['code_desc'] += " URL : #{get_location(result)['uri']}"
|
60
|
+
end
|
61
|
+
if get_location(result)['start_line']
|
62
|
+
finding['code_desc'] += " LINE : #{get_location(result)['start_line']}"
|
63
|
+
end
|
64
|
+
if get_location(result)['start_column']
|
65
|
+
finding['code_desc'] += " COLUMN : #{get_location(result)['start_column']}"
|
66
|
+
end
|
67
|
+
finding['code_desc'].strip!
|
68
|
+
finding['run_time'] = NA_FLOAT
|
69
|
+
finding['start_time'] = NA_STRING
|
70
|
+
finding
|
71
|
+
end
|
72
|
+
|
73
|
+
def add_nist_tag_from_cwe(cweid, taxonomy_name, tags_node)
|
74
|
+
entries = @cwe_nist_mapping.select { |x| cweid.include?(x[:cweid].to_s) && !x[:nistid].nil? }
|
75
|
+
tags = entries.map { |x| x[:nistid] }
|
76
|
+
result_tags = tags.empty? ? DEFAULT_NIST_TAG : tags.flatten.uniq
|
77
|
+
if result_tags.count.positive?
|
78
|
+
if !tags_node
|
79
|
+
tags_node = {}
|
80
|
+
end
|
81
|
+
if !tags_node.key?(taxonomy_name)
|
82
|
+
tags_node[taxonomy_name] = []
|
83
|
+
end
|
84
|
+
result_tags.each do |t|
|
85
|
+
tags_node[taxonomy_name] |= [t]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
tags_node
|
89
|
+
end
|
90
|
+
|
91
|
+
def get_location(result)
|
92
|
+
location_info = {}
|
93
|
+
location_info['uri'] = result.dig('locations', 0, 'physicalLocation', 'artifactLocation', 'uri')
|
94
|
+
location_info['start_line'] = result.dig('locations', 0, 'physicalLocation', 'region', 'startLine')
|
95
|
+
location_info['start_column'] = result.dig('locations', 0, 'physicalLocation', 'region', 'startColumn')
|
96
|
+
location_info
|
97
|
+
end
|
98
|
+
|
99
|
+
def get_rule_info(run, result, rule_id)
|
100
|
+
finding = {}
|
101
|
+
driver = run.dig('tool', 'driver')
|
102
|
+
finding['driver_name'] = driver['name']
|
103
|
+
finding['driver_version'] = driver['version']
|
104
|
+
rules = driver['rules']
|
105
|
+
if rules
|
106
|
+
rule = rules.find { |x| x['id'].eql?(rule_id) }
|
107
|
+
if rule
|
108
|
+
finding['rule_name'] = rule&.[]('name')
|
109
|
+
finding['rule_short_description'] = rule&.[]('shortDescription')&.[]('text')
|
110
|
+
finding['rule_tags'] = get_tags(rule)
|
111
|
+
finding['rule_name'] = rule&.[]('messageStrings')&.[]('default')&.[]('text') unless finding['rule_name']
|
112
|
+
end
|
113
|
+
end
|
114
|
+
finding['rule_name'] = result&.[]('message')&.[]('text') unless finding['rule_name']
|
115
|
+
finding
|
116
|
+
end
|
117
|
+
|
118
|
+
def get_tags(rule)
|
119
|
+
result = {}
|
120
|
+
Array(rule&.[]('relationships')).each do |relationship|
|
121
|
+
taxonomy_name = relationship['target']['toolComponent']['name'].downcase
|
122
|
+
taxonomy_id = relationship['target']['id']
|
123
|
+
if !result.key?(taxonomy_name)
|
124
|
+
result[taxonomy_name] = []
|
125
|
+
end
|
126
|
+
result[taxonomy_name] |= [taxonomy_id]
|
127
|
+
end
|
128
|
+
result
|
129
|
+
end
|
130
|
+
|
131
|
+
def parse_identifiers(rule_tags, ref)
|
132
|
+
# Extracting id number from reference style CWE-297
|
133
|
+
rule_tags[ref.downcase].map { |e| e.downcase.split("#{ref.downcase}-")[1] }
|
134
|
+
rescue StandardError
|
135
|
+
[]
|
136
|
+
end
|
137
|
+
|
138
|
+
def impact(severity)
|
139
|
+
severity_mapping = IMPACT_MAPPING[severity.to_sym]
|
140
|
+
severity_mapping.nil? ? 0.1 : severity_mapping
|
141
|
+
end
|
142
|
+
|
143
|
+
def parse_mapper
|
144
|
+
csv_data = CSV.read(CWE_NIST_MAPPING_FILE, **{ encoding: 'UTF-8',
|
145
|
+
headers: true,
|
146
|
+
header_converters: :symbol,
|
147
|
+
converters: :all })
|
148
|
+
csv_data.map(&:to_hash)
|
149
|
+
end
|
150
|
+
|
151
|
+
def desc_tags(data, label)
|
152
|
+
{ data: data || NA_STRING, label: label || NA_STRING }
|
153
|
+
end
|
154
|
+
|
155
|
+
def process_item(run, result, controls)
|
156
|
+
printf("\rProcessing: %s", $spinner.next)
|
157
|
+
control = controls.find { |x| x['id'].eql?(result['ruleId']) }
|
158
|
+
|
159
|
+
if control
|
160
|
+
control['results'] << finding(result)
|
161
|
+
else
|
162
|
+
rule_info = get_rule_info(run, result, result['ruleId'])
|
163
|
+
item = {}
|
164
|
+
item['tags'] = rule_info['rule_tags']
|
165
|
+
item['descriptions'] = []
|
166
|
+
item['refs'] = NA_ARRAY
|
167
|
+
item['source_location'] = { ref: get_location(result)['uri'], line: get_location(result)['start_line'] }
|
168
|
+
item['descriptions'] = NA_ARRAY
|
169
|
+
item['title'] = rule_info['rule_name'].to_s
|
170
|
+
item['id'] = result['ruleId'].to_s
|
171
|
+
item['desc'] = rule_info['rule_short_description'].to_s
|
172
|
+
item['impact'] = impact(result['level'].to_s)
|
173
|
+
item['code'] = NA_STRING
|
174
|
+
item['results'] = [finding(result)]
|
175
|
+
item['tags'] = add_nist_tag_from_cwe(parse_identifiers(rule_info['rule_tags'], 'CWE'), 'nist', item['tags'])
|
176
|
+
controls << item
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def to_hdf
|
181
|
+
controls = []
|
182
|
+
@sarif_log['runs'].each do |run|
|
183
|
+
run['results'].each do |result|
|
184
|
+
process_item(run, result, controls)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
scaninfo = extract_scaninfo(@sarif_log)
|
189
|
+
results = HeimdallDataFormat.new(profile_name: scaninfo['policy'],
|
190
|
+
version: scaninfo['version'],
|
191
|
+
title: scaninfo['projectName'],
|
192
|
+
summary: scaninfo['summary'],
|
193
|
+
controls: controls,
|
194
|
+
target_id: scaninfo['projectName'])
|
195
|
+
results.to_hdf
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|