heimdall_tools 1.3.40 → 1.3.41
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/lib/heimdall_tools/aws_config_mapper.rb +26 -26
- data/lib/heimdall_tools/burpsuite_mapper.rb +7 -11
- data/lib/heimdall_tools/cli.rb +9 -10
- data/lib/heimdall_tools/command.rb +0 -2
- data/lib/heimdall_tools/dbprotect_mapper.rb +9 -18
- data/lib/heimdall_tools/fortify_mapper.rb +1 -2
- data/lib/heimdall_tools/hdf.rb +4 -5
- data/lib/heimdall_tools/jfrog_xray_mapper.rb +26 -27
- data/lib/heimdall_tools/nessus_mapper.rb +39 -46
- data/lib/heimdall_tools/netsparker_mapper.rb +13 -16
- data/lib/heimdall_tools/nikto_mapper.rb +27 -27
- data/lib/heimdall_tools/snyk_mapper.rb +20 -22
- data/lib/heimdall_tools/sonarqube_mapper.rb +23 -21
- data/lib/heimdall_tools/zap_mapper.rb +3 -4
- data/lib/utilities/xml_to_hash.rb +6 -6
- metadata +38 -24
@@ -6,7 +6,7 @@ require 'nokogiri'
|
|
6
6
|
|
7
7
|
RESOURCE_DIR = Pathname.new(__FILE__).join('../../data')
|
8
8
|
|
9
|
-
NESSUS_PLUGINS_NIST_MAPPING_FILE =
|
9
|
+
NESSUS_PLUGINS_NIST_MAPPING_FILE = File.join(RESOURCE_DIR, 'nessus-plugins-nist-mapping.csv')
|
10
10
|
U_CCI_LIST = File.join(RESOURCE_DIR, 'U_CCI_List.xml')
|
11
11
|
|
12
12
|
IMPACT_MAPPING = {
|
@@ -14,16 +14,16 @@ IMPACT_MAPPING = {
|
|
14
14
|
Low: 0.3,
|
15
15
|
Medium: 0.5,
|
16
16
|
High: 0.7,
|
17
|
-
Critical: 0.9
|
17
|
+
Critical: 0.9
|
18
18
|
}.freeze
|
19
19
|
|
20
|
-
DEFAULT_NIST_TAG = [
|
20
|
+
DEFAULT_NIST_TAG = ['unmapped'].freeze
|
21
21
|
|
22
22
|
# Nessus results file 800-53 refs does not contain Nist rev version. Using this default
|
23
23
|
# version in that case
|
24
24
|
DEFAULT_NIST_REV = 'Rev_4'.freeze
|
25
25
|
|
26
|
-
NA_PLUGIN_OUTPUT =
|
26
|
+
NA_PLUGIN_OUTPUT = 'This Nessus Plugin does not provide output message.'.freeze
|
27
27
|
|
28
28
|
# rubocop:disable Metrics/AbcSize
|
29
29
|
|
@@ -51,19 +51,16 @@ module HeimdallTools
|
|
51
51
|
rescue StandardError => e
|
52
52
|
raise "Invalid Nessus XML file provided Exception: #{e}"
|
53
53
|
end
|
54
|
-
|
55
54
|
end
|
56
55
|
|
57
56
|
def extract_report
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
raise "Invalid Nessus XML file provided Exception: #{e}"
|
66
|
-
end
|
57
|
+
# When there are multiple hosts in the nessus report ReportHost field is an array
|
58
|
+
# When there is only one host in the nessus report ReportHost field is a hash
|
59
|
+
# Array() converts ReportHost to array in case there is only one host
|
60
|
+
reports = @data['NessusClientData_v2']['Report']['ReportHost']
|
61
|
+
reports.is_a?(Array) ? reports : [reports]
|
62
|
+
rescue StandardError => e
|
63
|
+
raise "Invalid Nessus XML file provided Exception: #{e}"
|
67
64
|
end
|
68
65
|
|
69
66
|
def parse_refs(refs, key)
|
@@ -71,24 +68,20 @@ module HeimdallTools
|
|
71
68
|
end
|
72
69
|
|
73
70
|
def extract_scaninfo
|
74
|
-
|
75
|
-
|
76
|
-
info = {}
|
71
|
+
policy = @data['NessusClientData_v2']['Policy']
|
72
|
+
info = {}
|
77
73
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
end
|
74
|
+
info['policyName'] = policy['policyName']
|
75
|
+
info['version'] = policy['Preferences']['ServerPreferences']['preference'].select { |x| x['name'].eql? 'sc_version' }.first['value']
|
76
|
+
info
|
77
|
+
rescue StandardError => e
|
78
|
+
raise "Invalid Nessus XML file provided Exception: #{e}"
|
84
79
|
end
|
85
80
|
|
86
81
|
def extract_timestamp(report)
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
raise "Invalid Nessus XML file provided Exception: #{e}"
|
91
|
-
end
|
82
|
+
report['HostProperties']['tag'].select { |x| x['name'].eql? 'HOST_START' }.first['text']
|
83
|
+
rescue StandardError => e
|
84
|
+
raise "Invalid Nessus XML file provided Exception: #{e}"
|
92
85
|
end
|
93
86
|
|
94
87
|
def format_desc(issue)
|
@@ -129,7 +122,7 @@ module HeimdallTools
|
|
129
122
|
|
130
123
|
def cci_nist_tag(cci_refs)
|
131
124
|
nist_tags = []
|
132
|
-
cci_refs.each do |
|
125
|
+
cci_refs.each do |cci_ref|
|
133
126
|
item_node = @cci_xml.xpath("//cci_list/cci_items/cci_item[@id='#{cci_ref}']")[0] unless @cci_xml.nil?
|
134
127
|
unless item_node.nil?
|
135
128
|
nist_ref = item_node.xpath('./references/reference[not(@version <= preceding-sibling::reference/@version) and not(@version <=following-sibling::reference/@version)]/@index').text
|
@@ -140,7 +133,7 @@ module HeimdallTools
|
|
140
133
|
end
|
141
134
|
|
142
135
|
def plugin_nist_tag(pluginfamily, pluginid)
|
143
|
-
entries = @cwe_nist_mapping.select { |x| (x[:pluginfamily].eql?(pluginfamily) && (x[:pluginid].eql?('*') || x[:pluginid].eql?(pluginid.to_i))
|
136
|
+
entries = @cwe_nist_mapping.select { |x| (x[:pluginfamily].eql?(pluginfamily) && (x[:pluginid].eql?('*') || x[:pluginid].eql?(pluginid.to_i))) && !x[:nistid].nil? }
|
144
137
|
tags = entries.map { |x| [x[:nistid].split('|'), "Rev_#{x[:rev]}"] }
|
145
138
|
tags.empty? ? DEFAULT_NIST_TAG : tags.flatten.uniq
|
146
139
|
end
|
@@ -148,15 +141,15 @@ module HeimdallTools
|
|
148
141
|
def impact(severity)
|
149
142
|
# Map CAT levels and Plugin severity to HDF impact levels
|
150
143
|
case severity
|
151
|
-
when
|
144
|
+
when '0'
|
152
145
|
IMPACT_MAPPING[:Info]
|
153
|
-
when
|
146
|
+
when '1', 'III'
|
154
147
|
IMPACT_MAPPING[:Low]
|
155
|
-
when
|
148
|
+
when '2', 'II'
|
156
149
|
IMPACT_MAPPING[:Medium]
|
157
|
-
when
|
150
|
+
when '3', 'I'
|
158
151
|
IMPACT_MAPPING[:High]
|
159
|
-
when
|
152
|
+
when '4'
|
160
153
|
IMPACT_MAPPING[:Critical]
|
161
154
|
else
|
162
155
|
-1
|
@@ -172,17 +165,17 @@ module HeimdallTools
|
|
172
165
|
end
|
173
166
|
|
174
167
|
def desc_tags(data, label)
|
175
|
-
{
|
168
|
+
{ data: data || NA_STRING, label: label || NA_STRING }
|
176
169
|
end
|
177
170
|
|
178
171
|
# Nessus report could have multiple issue entries for multiple findings of same issue type.
|
179
|
-
# The meta data is identical across entries
|
172
|
+
# The meta data is identical across entries
|
180
173
|
# method collapse_duplicates return unique controls with applicable findings collapsed into it.
|
181
174
|
def collapse_duplicates(controls)
|
182
175
|
unique_controls = []
|
183
176
|
|
184
177
|
controls.map { |x| x['id'] }.uniq.each do |id|
|
185
|
-
collapsed_results = controls.select { |x| x['id'].eql?(id) }.map {|x| x['results']}
|
178
|
+
collapsed_results = controls.select { |x| x['id'].eql?(id) }.map { |x| x['results'] }
|
186
179
|
unique_control = controls.find { |x| x['id'].eql?(id) }
|
187
180
|
unique_control['results'] = collapsed_results.flatten
|
188
181
|
unique_controls << unique_control
|
@@ -192,9 +185,9 @@ module HeimdallTools
|
|
192
185
|
|
193
186
|
def to_hdf
|
194
187
|
host_results = {}
|
195
|
-
@reports.each do |
|
188
|
+
@reports.each do |report|
|
196
189
|
controls = []
|
197
|
-
report['ReportItem'].each do |
|
190
|
+
report['ReportItem'].each do |item|
|
198
191
|
printf("\rProcessing: %s", $spinner.next)
|
199
192
|
@item = {}
|
200
193
|
@item['tags'] = {}
|
@@ -207,7 +200,7 @@ module HeimdallTools
|
|
207
200
|
# Current version covers STIG based 'Policy Compliance' results
|
208
201
|
# TODO Cover cases for 'Policy Compliance' results based on CIS
|
209
202
|
if item['compliance-reference']
|
210
|
-
@item['id'] = parse_refs(item['compliance-reference'],'Vuln-ID').join.to_s
|
203
|
+
@item['id'] = parse_refs(item['compliance-reference'], 'Vuln-ID').join.to_s
|
211
204
|
else
|
212
205
|
@item['id'] = item['pluginID'].to_s
|
213
206
|
end
|
@@ -222,17 +215,17 @@ module HeimdallTools
|
|
222
215
|
@item['desc'] = format_desc(item).to_s
|
223
216
|
end
|
224
217
|
if item['compliance-reference']
|
225
|
-
@item['impact'] = impact(parse_refs(item['compliance-reference'],'CAT').join.to_s)
|
218
|
+
@item['impact'] = impact(parse_refs(item['compliance-reference'], 'CAT').join.to_s)
|
226
219
|
else
|
227
|
-
@item['impact'] = impact(item['severity'])
|
220
|
+
@item['impact'] = impact(item['severity'])
|
228
221
|
end
|
229
222
|
if item['compliance-reference']
|
230
|
-
@item['tags']['nist'] = cci_nist_tag(parse_refs(item['compliance-reference'],'CCI'))
|
223
|
+
@item['tags']['nist'] = cci_nist_tag(parse_refs(item['compliance-reference'], 'CCI'))
|
231
224
|
else
|
232
|
-
@item['tags']['nist'] = plugin_nist_tag(item['pluginFamily'],item['pluginID'])
|
225
|
+
@item['tags']['nist'] = plugin_nist_tag(item['pluginFamily'], item['pluginID'])
|
233
226
|
end
|
234
227
|
if item['compliance-solution']
|
235
|
-
@item['descriptions'] <<
|
228
|
+
@item['descriptions'] << desc_tags(item['compliance-solution'], 'check')
|
236
229
|
end
|
237
230
|
|
238
231
|
@item['code'] = ''
|
@@ -17,13 +17,11 @@ IMPACT_MAPPING = {
|
|
17
17
|
Information: 0.0
|
18
18
|
}.freeze
|
19
19
|
|
20
|
-
DEFAULT_NIST_TAG =
|
21
|
-
|
22
|
-
# rubocop:disable Metrics/AbcSize
|
20
|
+
DEFAULT_NIST_TAG = %w{SA-11 RA-5}.freeze
|
23
21
|
|
24
22
|
module HeimdallTools
|
25
23
|
class NetsparkerMapper
|
26
|
-
def initialize(xml,
|
24
|
+
def initialize(xml, _name = nil, verbose = false)
|
27
25
|
@verbose = verbose
|
28
26
|
|
29
27
|
begin
|
@@ -33,7 +31,6 @@ module HeimdallTools
|
|
33
31
|
|
34
32
|
@vulnerabilities = data['netsparker-enterprise']['vulnerabilities']['vulnerability']
|
35
33
|
@scan_info = data['netsparker-enterprise']['target']
|
36
|
-
|
37
34
|
rescue StandardError => e
|
38
35
|
raise "Invalid Netsparker XML file provided Exception: #{e}"
|
39
36
|
end
|
@@ -63,7 +60,7 @@ module HeimdallTools
|
|
63
60
|
controls = collapse_duplicates(controls)
|
64
61
|
results = HeimdallDataFormat.new(profile_name: 'Netsparker Enterprise Scan',
|
65
62
|
title: "Netsparker Enterprise Scan ID: #{@scan_info['scan-id']} URL: #{@scan_info['url']}",
|
66
|
-
summary:
|
63
|
+
summary: 'Netsparker Enterprise Scan',
|
67
64
|
target_id: @scan_info['url'],
|
68
65
|
controls: controls)
|
69
66
|
results.to_hdf
|
@@ -79,25 +76,25 @@ module HeimdallTools
|
|
79
76
|
finding = {}
|
80
77
|
finding['status'] = 'failed'
|
81
78
|
finding['code_desc'] = []
|
82
|
-
finding['code_desc'] << "http-request : #{parse_html(vulnerability['http-request']['content'])
|
79
|
+
finding['code_desc'] << "http-request : #{parse_html(vulnerability['http-request']['content'])}"
|
83
80
|
finding['code_desc'] << "method : #{vulnerability['http-request']['method']}"
|
84
81
|
finding['code_desc'] = finding['code_desc'].join("\n")
|
85
82
|
|
86
83
|
finding['message'] = []
|
87
|
-
finding['message'] << "http-response : #{parse_html(vulnerability['http-response']['content'])
|
84
|
+
finding['message'] << "http-response : #{parse_html(vulnerability['http-response']['content'])}"
|
88
85
|
finding['message'] << "duration : #{vulnerability['http-response']['duration']}"
|
89
86
|
finding['message'] << "status-code : #{vulnerability['http-response']['status-code']}"
|
90
87
|
finding['message'] = finding['message'].join("\n")
|
91
88
|
finding['run_time'] = NA_FLOAT
|
92
89
|
|
93
|
-
finding['start_time'] =
|
90
|
+
finding['start_time'] = @scan_info['initiated']
|
94
91
|
[finding]
|
95
92
|
end
|
96
93
|
|
97
94
|
def format_control_desc(vulnerability)
|
98
95
|
text = []
|
99
|
-
text <<
|
100
|
-
text << "Exploitation-skills: #{parse_html(vulnerability['exploitation-skills'])}"
|
96
|
+
text << parse_html(vulnerability['description']).to_s unless vulnerability['description'].nil?
|
97
|
+
text << "Exploitation-skills: #{parse_html(vulnerability['exploitation-skills'])}" unless vulnerability['exploitation-skills'].nil?
|
101
98
|
text << "Extra-information: #{vulnerability['extra-information']}" unless vulnerability['extra-information'].nil?
|
102
99
|
text << "Classification: #{vulnerability['classification']}" unless vulnerability['classification'].nil?
|
103
100
|
text << "Impact: #{parse_html(vulnerability['impact'])}" unless vulnerability['impact'].nil?
|
@@ -106,14 +103,14 @@ module HeimdallTools
|
|
106
103
|
text << "Certainty: #{vulnerability['certainty']}" unless vulnerability['certainty'].nil?
|
107
104
|
text << "Type: #{vulnerability['type']}" unless vulnerability['type'].nil?
|
108
105
|
text << "Confirmed: #{vulnerability['confirmed']}" unless vulnerability['confirmed'].nil?
|
109
|
-
text.join(
|
106
|
+
text.join('<br>')
|
110
107
|
end
|
111
108
|
|
112
109
|
def format_check_text(vulnerability)
|
113
110
|
text = []
|
114
111
|
text << "Exploitation-skills: #{parse_html(vulnerability['exploitation-skills'])}" unless vulnerability['exploitation-skills'].nil?
|
115
112
|
text << "Proof-of-concept: #{parse_html(vulnerability['proof-of-concept'])}" unless vulnerability['proof-of-concept'].nil?
|
116
|
-
text.join(
|
113
|
+
text.join('<br>')
|
117
114
|
end
|
118
115
|
|
119
116
|
def format_fix_text(vulnerability)
|
@@ -121,7 +118,7 @@ module HeimdallTools
|
|
121
118
|
text << "Remedial-actions: #{parse_html(vulnerability['remedial-actions'])}" unless vulnerability['remedial-actions'].nil?
|
122
119
|
text << "Remedial-procedure: #{parse_html(vulnerability['remedial-procedure'])}" unless vulnerability['remedial-procedure'].nil?
|
123
120
|
text << "Remedy-references: #{parse_html(vulnerability['remedy-references'])}" unless vulnerability['remedy-references'].nil?
|
124
|
-
text.join(
|
121
|
+
text.join('<br>')
|
125
122
|
end
|
126
123
|
|
127
124
|
def nist_tag(classification)
|
@@ -146,7 +143,7 @@ module HeimdallTools
|
|
146
143
|
end
|
147
144
|
|
148
145
|
def desc_tags(data, label)
|
149
|
-
{
|
146
|
+
{ data: data || NA_STRING, label: label || NA_STRING }
|
150
147
|
end
|
151
148
|
|
152
149
|
# Netsparker report could have multiple issue entries for multiple findings of same issue type.
|
@@ -156,7 +153,7 @@ module HeimdallTools
|
|
156
153
|
unique_controls = []
|
157
154
|
|
158
155
|
controls.map { |x| x['id'] }.uniq.each do |id|
|
159
|
-
collapsed_results = controls.select { |x| x['id'].eql?(id) }.map {|x| x['results']}
|
156
|
+
collapsed_results = controls.select { |x| x['id'].eql?(id) }.map { |x| x['results'] }
|
160
157
|
unique_control = controls.find { |x| x['id'].eql?(id) }
|
161
158
|
unique_control['results'] = collapsed_results.flatten
|
162
159
|
unique_controls << unique_control
|
@@ -9,10 +9,10 @@ NIKTO_NIST_MAPPING_FILE = File.join(RESOURCE_DIR, 'nikto-nist-mapping.csv')
|
|
9
9
|
IMPACT_MAPPING = {
|
10
10
|
high: 0.7,
|
11
11
|
medium: 0.5,
|
12
|
-
low: 0.3
|
12
|
+
low: 0.3
|
13
13
|
}.freeze
|
14
14
|
|
15
|
-
DEFAULT_NIST_TAG =
|
15
|
+
DEFAULT_NIST_TAG = %w{SA-11 RA-5}.freeze
|
16
16
|
|
17
17
|
# Loading spinner sign
|
18
18
|
$spinner = Enumerator.new do |e|
|
@@ -26,7 +26,7 @@ end
|
|
26
26
|
|
27
27
|
module HeimdallTools
|
28
28
|
class NiktoMapper
|
29
|
-
def initialize(nikto_json,
|
29
|
+
def initialize(nikto_json, _name = nil, verbose = false)
|
30
30
|
@nikto_json = nikto_json
|
31
31
|
@verbose = verbose
|
32
32
|
|
@@ -36,9 +36,9 @@ module HeimdallTools
|
|
36
36
|
raise "Invalid Nikto to NIST mapping file: Exception: #{e}"
|
37
37
|
end
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
# TODO: Support Multi-target scan results
|
40
|
+
# Nikto multi-target scans generate invalid format JSONs
|
41
|
+
# Possible workaround to use https://stackoverflow.com/a/58209963/1670307
|
42
42
|
|
43
43
|
begin
|
44
44
|
@project = JSON.parse(nikto_json)
|
@@ -64,7 +64,7 @@ module HeimdallTools
|
|
64
64
|
def finding(vulnerability)
|
65
65
|
finding = {}
|
66
66
|
finding['status'] = 'failed'
|
67
|
-
finding['code_desc'] = "URL : #{vulnerability['url']
|
67
|
+
finding['code_desc'] = "URL : #{vulnerability['url']} Method: #{vulnerability['method']}"
|
68
68
|
finding['run_time'] = NA_FLOAT
|
69
69
|
finding['start_time'] = NA_STRING
|
70
70
|
[finding]
|
@@ -83,32 +83,32 @@ module HeimdallTools
|
|
83
83
|
def parse_mapper
|
84
84
|
csv_data = CSV.read(NIKTO_NIST_MAPPING_FILE, **{ encoding: 'UTF-8',
|
85
85
|
headers: true,
|
86
|
-
header_converters: :symbol})
|
86
|
+
header_converters: :symbol })
|
87
87
|
csv_data.map(&:to_hash)
|
88
88
|
end
|
89
89
|
|
90
90
|
def desc_tags(data, label)
|
91
|
-
{
|
91
|
+
{ data: data || NA_STRING, label: label || NA_STRING }
|
92
92
|
end
|
93
93
|
|
94
|
-
# Nikto report could have multiple vulnerability entries for multiple findings of same issue type.
|
95
|
-
# The meta data is identical across entries
|
96
|
-
# method collapse_duplicates return unique controls with applicable findings collapsed into it.
|
97
|
-
def collapse_duplicates(controls)
|
98
|
-
unique_controls = []
|
99
|
-
|
100
|
-
controls.map { |x| x['id'] }.uniq.each do |id|
|
101
|
-
collapsed_results = controls.select { |x| x['id'].eql?(id) }.map {|x| x['results']}
|
102
|
-
unique_control = controls.find { |x| x['id'].eql?(id) }
|
103
|
-
unique_control['results'] = collapsed_results.flatten
|
104
|
-
unique_controls << unique_control
|
105
|
-
end
|
106
|
-
unique_controls
|
107
|
-
end
|
94
|
+
# Nikto report could have multiple vulnerability entries for multiple findings of same issue type.
|
95
|
+
# The meta data is identical across entries
|
96
|
+
# method collapse_duplicates return unique controls with applicable findings collapsed into it.
|
97
|
+
def collapse_duplicates(controls)
|
98
|
+
unique_controls = []
|
99
|
+
|
100
|
+
controls.map { |x| x['id'] }.uniq.each do |id|
|
101
|
+
collapsed_results = controls.select { |x| x['id'].eql?(id) }.map { |x| x['results'] }
|
102
|
+
unique_control = controls.find { |x| x['id'].eql?(id) }
|
103
|
+
unique_control['results'] = collapsed_results.flatten
|
104
|
+
unique_controls << unique_control
|
105
|
+
end
|
106
|
+
unique_controls
|
107
|
+
end
|
108
108
|
|
109
109
|
def to_hdf
|
110
110
|
controls = []
|
111
|
-
@project['vulnerabilities'].each do |
|
111
|
+
@project['vulnerabilities'].each do |vulnerability|
|
112
112
|
printf("\rProcessing: %s", $spinner.next)
|
113
113
|
|
114
114
|
item = {}
|
@@ -125,11 +125,11 @@ module HeimdallTools
|
|
125
125
|
# Duplicating vulnerability msg field
|
126
126
|
item['desc'] = vulnerability['msg'].to_s
|
127
127
|
|
128
|
-
# Nitko does not provide finding severity; hard-coding severity to medium
|
129
|
-
item['impact'] = impact('medium')
|
128
|
+
# Nitko does not provide finding severity; hard-coding severity to medium
|
129
|
+
item['impact'] = impact('medium')
|
130
130
|
item['code'] = NA_STRING
|
131
131
|
item['results'] = finding(vulnerability)
|
132
|
-
item['tags']['nist'] = nist_tag(
|
132
|
+
item['tags']['nist'] = nist_tag(vulnerability['id'].to_s)
|
133
133
|
item['tags']['ösvdb'] = vulnerability['OSVDB']
|
134
134
|
|
135
135
|
controls << item
|
@@ -10,12 +10,12 @@ CWE_NIST_MAPPING_FILE = File.join(RESOURCE_DIR, 'cwe-nist-mapping.csv')
|
|
10
10
|
IMPACT_MAPPING = {
|
11
11
|
high: 0.7,
|
12
12
|
medium: 0.5,
|
13
|
-
low: 0.3
|
13
|
+
low: 0.3
|
14
14
|
}.freeze
|
15
15
|
|
16
16
|
SNYK_VERSION_REGEX = 'v(\d+.)(\d+.)(\d+)'.freeze
|
17
17
|
|
18
|
-
DEFAULT_NIST_TAG =
|
18
|
+
DEFAULT_NIST_TAG = %w{SA-11 RA-5}.freeze
|
19
19
|
|
20
20
|
# Loading spinner sign
|
21
21
|
$spinner = Enumerator.new do |e|
|
@@ -29,7 +29,7 @@ end
|
|
29
29
|
|
30
30
|
module HeimdallTools
|
31
31
|
class SnykMapper
|
32
|
-
def initialize(synk_json,
|
32
|
+
def initialize(synk_json, _name = nil, verbose = false)
|
33
33
|
@synk_json = synk_json
|
34
34
|
@verbose = verbose
|
35
35
|
|
@@ -38,10 +38,9 @@ module HeimdallTools
|
|
38
38
|
@projects = JSON.parse(synk_json)
|
39
39
|
|
40
40
|
# Cover single and multi-project scan use cases.
|
41
|
-
unless @projects.
|
42
|
-
@projects = [
|
41
|
+
unless @projects.is_a?(Array)
|
42
|
+
@projects = [@projects]
|
43
43
|
end
|
44
|
-
|
45
44
|
rescue StandardError => e
|
46
45
|
raise "Invalid Snyk JSON file provided Exception: #{e}"
|
47
46
|
end
|
@@ -52,7 +51,7 @@ module HeimdallTools
|
|
52
51
|
begin
|
53
52
|
info['policy'] = project['policy']
|
54
53
|
reg = Regexp.new(SNYK_VERSION_REGEX, Regexp::IGNORECASE)
|
55
|
-
info['version'] = info['policy'].scan(reg).join
|
54
|
+
info['version'] = info['policy'].scan(reg).join
|
56
55
|
info['projectName'] = project['projectName']
|
57
56
|
info['summary'] = project['summary']
|
58
57
|
|
@@ -65,7 +64,7 @@ module HeimdallTools
|
|
65
64
|
def finding(vulnerability)
|
66
65
|
finding = {}
|
67
66
|
finding['status'] = 'failed'
|
68
|
-
finding['code_desc'] = "From : [ #{vulnerability['from'].join(
|
67
|
+
finding['code_desc'] = "From : [ #{vulnerability['from'].join(' , ')} ]"
|
69
68
|
finding['run_time'] = NA_FLOAT
|
70
69
|
|
71
70
|
# Snyk results does not profile scan timestamp; using current time to satisfy HDF format
|
@@ -81,9 +80,9 @@ module HeimdallTools
|
|
81
80
|
|
82
81
|
def parse_identifiers(vulnerability, ref)
|
83
82
|
# Extracting id number from reference style CWE-297
|
84
|
-
vulnerability['identifiers'][ref].map { |e| e.split("#{ref}-")[1]
|
85
|
-
|
86
|
-
|
83
|
+
vulnerability['identifiers'][ref].map { |e| e.split("#{ref}-")[1] }
|
84
|
+
rescue StandardError
|
85
|
+
[]
|
87
86
|
end
|
88
87
|
|
89
88
|
def impact(severity)
|
@@ -99,17 +98,17 @@ module HeimdallTools
|
|
99
98
|
end
|
100
99
|
|
101
100
|
def desc_tags(data, label)
|
102
|
-
{
|
101
|
+
{ data: data || NA_STRING, label: label || NA_STRING }
|
103
102
|
end
|
104
103
|
|
105
104
|
# Snyk report could have multiple vulnerability entries for multiple findings of same issue type.
|
106
|
-
# The meta data is identical across entries
|
105
|
+
# The meta data is identical across entries
|
107
106
|
# method collapse_duplicates return unique controls with applicable findings collapsed into it.
|
108
107
|
def collapse_duplicates(controls)
|
109
108
|
unique_controls = []
|
110
109
|
|
111
110
|
controls.map { |x| x['id'] }.uniq.each do |id|
|
112
|
-
collapsed_results = controls.select { |x| x['id'].eql?(id) }.map {|x| x['results']}
|
111
|
+
collapsed_results = controls.select { |x| x['id'].eql?(id) }.map { |x| x['results'] }
|
113
112
|
unique_control = controls.find { |x| x['id'].eql?(id) }
|
114
113
|
unique_control['results'] = collapsed_results.flatten
|
115
114
|
unique_controls << unique_control
|
@@ -117,12 +116,11 @@ module HeimdallTools
|
|
117
116
|
unique_controls
|
118
117
|
end
|
119
118
|
|
120
|
-
|
121
119
|
def to_hdf
|
122
120
|
project_results = {}
|
123
|
-
@projects.each do |
|
121
|
+
@projects.each do |project|
|
124
122
|
controls = []
|
125
|
-
project['vulnerabilities'].each do |
|
123
|
+
project['vulnerabilities'].each do |vulnerability|
|
126
124
|
printf("\rProcessing: %s", $spinner.next)
|
127
125
|
|
128
126
|
item = {}
|
@@ -135,13 +133,13 @@ module HeimdallTools
|
|
135
133
|
item['title'] = vulnerability['title'].to_s
|
136
134
|
item['id'] = vulnerability['id'].to_s
|
137
135
|
item['desc'] = vulnerability['description'].to_s
|
138
|
-
item['impact'] = impact(vulnerability['severity'])
|
136
|
+
item['impact'] = impact(vulnerability['severity'])
|
139
137
|
item['code'] = ''
|
140
138
|
item['results'] = finding(vulnerability)
|
141
|
-
item['tags']['nist'] = nist_tag(
|
142
|
-
item['tags']['cweid'] = parse_identifiers(
|
143
|
-
item['tags']['cveid'] = parse_identifiers(
|
144
|
-
item['tags']['ghsaid'] = parse_identifiers(
|
139
|
+
item['tags']['nist'] = nist_tag(parse_identifiers(vulnerability, 'CWE'))
|
140
|
+
item['tags']['cweid'] = parse_identifiers(vulnerability, 'CWE')
|
141
|
+
item['tags']['cveid'] = parse_identifiers(vulnerability, 'CVE')
|
142
|
+
item['tags']['ghsaid'] = parse_identifiers(vulnerability, 'GHSA')
|
145
143
|
|
146
144
|
controls << item
|
147
145
|
end
|