heimdall_tools 1.3.37 → 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/README.md +18 -0
- data/lib/heimdall_tools.rb +1 -0
- data/lib/heimdall_tools/aws_config_mapper.rb +26 -26
- data/lib/heimdall_tools/burpsuite_mapper.rb +8 -12
- data/lib/heimdall_tools/cli.rb +19 -8
- 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/help/netsparker_mapper.md +7 -0
- data/lib/heimdall_tools/jfrog_xray_mapper.rb +34 -27
- data/lib/heimdall_tools/nessus_mapper.rb +39 -46
- data/lib/heimdall_tools/netsparker_mapper.rb +164 -0
- data/lib/heimdall_tools/nikto_mapper.rb +28 -28
- data/lib/heimdall_tools/snyk_mapper.rb +21 -23
- data/lib/heimdall_tools/sonarqube_mapper.rb +23 -21
- data/lib/heimdall_tools/zap_mapper.rb +4 -5
- data/lib/utilities/xml_to_hash.rb +6 -6
- metadata +40 -24
@@ -0,0 +1,7 @@
|
|
1
|
+
netsparker_mapper translates an Netsparker XML results file into HDF format json to be viewable in Heimdall
|
2
|
+
|
3
|
+
The current iteration only works with Netsparker Enterprise Vulnerabilities Scan.
|
4
|
+
|
5
|
+
Examples:
|
6
|
+
|
7
|
+
heimdall_tools netsparker_mapper -x netsparker_results.xml -o netsparker_hdf.json
|
@@ -10,10 +10,10 @@ 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
|
-
DEFAULT_NIST_TAG =
|
16
|
+
DEFAULT_NIST_TAG = %w{SA-11 RA-5}.freeze
|
17
17
|
|
18
18
|
# Loading spinner sign
|
19
19
|
$spinner = Enumerator.new do |e|
|
@@ -27,14 +27,13 @@ end
|
|
27
27
|
|
28
28
|
module HeimdallTools
|
29
29
|
class JfrogXrayMapper
|
30
|
-
def initialize(xray_json,
|
30
|
+
def initialize(xray_json, _name = nil, verbose = false)
|
31
31
|
@xray_json = xray_json
|
32
32
|
@verbose = verbose
|
33
33
|
|
34
34
|
begin
|
35
35
|
@cwe_nist_mapping = parse_mapper
|
36
36
|
@project = JSON.parse(xray_json)
|
37
|
-
|
38
37
|
rescue StandardError => e
|
39
38
|
raise "Invalid JFrog Xray JSON file provided Exception: #{e}"
|
40
39
|
end
|
@@ -44,11 +43,11 @@ module HeimdallTools
|
|
44
43
|
finding = {}
|
45
44
|
finding['status'] = 'failed'
|
46
45
|
finding['code_desc'] = []
|
47
|
-
finding['code_desc'] << "source_comp_id : #{vulnerability['source_comp_id']
|
48
|
-
finding['code_desc'] << "vulnerable_versions : #{vulnerability['component_versions']['vulnerable_versions']
|
49
|
-
finding['code_desc'] << "fixed_versions : #{vulnerability['component_versions']['fixed_versions']
|
50
|
-
finding['code_desc'] << "issue_type : #{vulnerability['issue_type']
|
51
|
-
finding['code_desc'] << "provider : #{vulnerability['provider']
|
46
|
+
finding['code_desc'] << "source_comp_id : #{vulnerability['source_comp_id']}"
|
47
|
+
finding['code_desc'] << "vulnerable_versions : #{vulnerability['component_versions']['vulnerable_versions']}"
|
48
|
+
finding['code_desc'] << "fixed_versions : #{vulnerability['component_versions']['fixed_versions']}"
|
49
|
+
finding['code_desc'] << "issue_type : #{vulnerability['issue_type']}"
|
50
|
+
finding['code_desc'] << "provider : #{vulnerability['provider']}"
|
52
51
|
finding['code_desc'] = finding['code_desc'].join("\n")
|
53
52
|
finding['run_time'] = NA_FLOAT
|
54
53
|
|
@@ -57,17 +56,25 @@ module HeimdallTools
|
|
57
56
|
[finding]
|
58
57
|
end
|
59
58
|
|
59
|
+
def format_control_desc(vulnerability)
|
60
|
+
text = []
|
61
|
+
info = vulnerability['component_versions']['more_details']
|
62
|
+
text << info['description'].to_s
|
63
|
+
text << "cves: #{info['cves']}" unless info['cves'].nil?
|
64
|
+
text.join('<br>')
|
65
|
+
end
|
66
|
+
|
60
67
|
def nist_tag(cweid)
|
61
|
-
entries = @cwe_nist_mapping.select { |x| cweid.include?
|
68
|
+
entries = @cwe_nist_mapping.select { |x| cweid.include?(x[:cweid].to_s) && !x[:nistid].nil? }
|
62
69
|
tags = entries.map { |x| x[:nistid] }
|
63
70
|
tags.empty? ? DEFAULT_NIST_TAG : tags.flatten.uniq
|
64
71
|
end
|
65
72
|
|
66
73
|
def parse_identifiers(vulnerability, ref)
|
67
74
|
# Extracting id number from reference style CWE-297
|
68
|
-
vulnerability['component_versions']['more_details']['cves'][0][ref.downcase].map { |e| e.split("#{ref}-")[1]
|
69
|
-
|
70
|
-
|
75
|
+
vulnerability['component_versions']['more_details']['cves'][0][ref.downcase].map { |e| e.split("#{ref}-")[1] }
|
76
|
+
rescue StandardError
|
77
|
+
[]
|
71
78
|
end
|
72
79
|
|
73
80
|
def impact(severity)
|
@@ -83,17 +90,17 @@ module HeimdallTools
|
|
83
90
|
end
|
84
91
|
|
85
92
|
def desc_tags(data, label)
|
86
|
-
{
|
93
|
+
{ data: data || NA_STRING, label: label || NA_STRING }
|
87
94
|
end
|
88
95
|
|
89
96
|
# Xray report could have multiple vulnerability entries for multiple findings of same issue type.
|
90
|
-
# The meta data is identical across entries
|
97
|
+
# The meta data is identical across entries
|
91
98
|
# method collapse_duplicates return unique controls with applicable findings collapsed into it.
|
92
99
|
def collapse_duplicates(controls)
|
93
100
|
unique_controls = []
|
94
101
|
|
95
102
|
controls.map { |x| x['id'] }.uniq.each do |id|
|
96
|
-
collapsed_results = controls.select { |x| x['id'].eql?(id) }.map {|x| x['results']}
|
103
|
+
collapsed_results = controls.select { |x| x['id'].eql?(id) }.map { |x| x['results'] }
|
97
104
|
unique_control = controls.find { |x| x['id'].eql?(id) }
|
98
105
|
unique_control['results'] = collapsed_results.flatten
|
99
106
|
unique_controls << unique_control
|
@@ -104,9 +111,9 @@ module HeimdallTools
|
|
104
111
|
def to_hdf
|
105
112
|
controls = []
|
106
113
|
vulnerability_count = 0
|
107
|
-
@project['data'].uniq.each do |
|
114
|
+
@project['data'].uniq.each do |vulnerability|
|
108
115
|
printf("\rProcessing: %s", $spinner.next)
|
109
|
-
|
116
|
+
|
110
117
|
vulnerability_count +=1
|
111
118
|
item = {}
|
112
119
|
item['tags'] = {}
|
@@ -115,26 +122,26 @@ module HeimdallTools
|
|
115
122
|
item['source_location'] = NA_HASH
|
116
123
|
item['descriptions'] = NA_ARRAY
|
117
124
|
|
118
|
-
# Xray JSONs might note have `id` fields populated.
|
125
|
+
# Xray JSONs might note have `id` fields populated.
|
119
126
|
# If thats a case MD5 hash is used to collapse vulnerability findings of the same type.
|
120
|
-
item['id'] = vulnerability['id'].empty? ? OpenSSL::Digest::MD5.digest(vulnerability['summary'].to_s).
|
127
|
+
item['id'] = vulnerability['id'].empty? ? OpenSSL::Digest::MD5.digest(vulnerability['summary'].to_s).unpack1('H*').to_s : vulnerability['id']
|
121
128
|
item['title'] = vulnerability['summary'].to_s
|
122
|
-
item['desc'] = vulnerability
|
123
|
-
item['impact'] = impact(vulnerability['severity'].to_s)
|
129
|
+
item['desc'] = format_control_desc(vulnerability)
|
130
|
+
item['impact'] = impact(vulnerability['severity'].to_s)
|
124
131
|
item['code'] = NA_STRING
|
125
132
|
item['results'] = finding(vulnerability)
|
126
133
|
|
127
|
-
item['tags']['nist'] = nist_tag(
|
128
|
-
item['tags']['cweid'] = parse_identifiers(
|
134
|
+
item['tags']['nist'] = nist_tag(parse_identifiers(vulnerability, 'CWE'))
|
135
|
+
item['tags']['cweid'] = parse_identifiers(vulnerability, 'CWE')
|
129
136
|
|
130
137
|
controls << item
|
131
138
|
end
|
132
139
|
|
133
140
|
controls = collapse_duplicates(controls)
|
134
|
-
results = HeimdallDataFormat.new(profile_name:
|
141
|
+
results = HeimdallDataFormat.new(profile_name: 'JFrog Xray Scan',
|
135
142
|
version: NA_STRING,
|
136
|
-
title:
|
137
|
-
summary:
|
143
|
+
title: 'JFrog Xray Scan',
|
144
|
+
summary: 'Continuous Security and Universal Artifact Analysis',
|
138
145
|
controls: controls)
|
139
146
|
results.to_hdf
|
140
147
|
end
|
@@ -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'] = ''
|
@@ -0,0 +1,164 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'csv'
|
3
|
+
require 'heimdall_tools/hdf'
|
4
|
+
require 'utilities/xml_to_hash'
|
5
|
+
|
6
|
+
RESOURCE_DIR = Pathname.new(__FILE__).join('../../data')
|
7
|
+
|
8
|
+
CWE_NIST_MAPPING_FILE = File.join(RESOURCE_DIR, 'cwe-nist-mapping.csv')
|
9
|
+
OWASP_NIST_MAPPING_FILE = File.join(RESOURCE_DIR, 'owasp-nist-mapping.csv')
|
10
|
+
|
11
|
+
IMPACT_MAPPING = {
|
12
|
+
Critical: 1.0,
|
13
|
+
High: 0.7,
|
14
|
+
Medium: 0.5,
|
15
|
+
Low: 0.3,
|
16
|
+
Best_Practice: 0.0,
|
17
|
+
Information: 0.0
|
18
|
+
}.freeze
|
19
|
+
|
20
|
+
DEFAULT_NIST_TAG = %w{SA-11 RA-5}.freeze
|
21
|
+
|
22
|
+
module HeimdallTools
|
23
|
+
class NetsparkerMapper
|
24
|
+
def initialize(xml, _name = nil, verbose = false)
|
25
|
+
@verbose = verbose
|
26
|
+
|
27
|
+
begin
|
28
|
+
@cwe_nist_mapping = parse_mapper(CWE_NIST_MAPPING_FILE)
|
29
|
+
@owasp_nist_mapping = parse_mapper(OWASP_NIST_MAPPING_FILE)
|
30
|
+
data = xml_to_hash(xml)
|
31
|
+
|
32
|
+
@vulnerabilities = data['netsparker-enterprise']['vulnerabilities']['vulnerability']
|
33
|
+
@scan_info = data['netsparker-enterprise']['target']
|
34
|
+
rescue StandardError => e
|
35
|
+
raise "Invalid Netsparker XML file provided Exception: #{e}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_hdf
|
40
|
+
controls = []
|
41
|
+
@vulnerabilities.each do |vulnerability|
|
42
|
+
@item = {}
|
43
|
+
@item['id'] = vulnerability['LookupId'].to_s
|
44
|
+
@item['title'] = vulnerability['name'].to_s
|
45
|
+
@item['desc'] = format_control_desc(vulnerability)
|
46
|
+
@item['impact'] = impact(vulnerability['severity'])
|
47
|
+
@item['tags'] = {}
|
48
|
+
@item['descriptions'] = []
|
49
|
+
|
50
|
+
@item['descriptions'] << desc_tags(format_check_text(vulnerability), 'check')
|
51
|
+
@item['descriptions'] << desc_tags(format_fix_text(vulnerability), 'fix')
|
52
|
+
@item['refs'] = NA_ARRAY
|
53
|
+
@item['source_location'] = NA_HASH
|
54
|
+
@item['tags']['nist'] = nist_tag(vulnerability['classification'])
|
55
|
+
@item['code'] = ''
|
56
|
+
@item['results'] = finding(vulnerability)
|
57
|
+
|
58
|
+
controls << @item
|
59
|
+
end
|
60
|
+
controls = collapse_duplicates(controls)
|
61
|
+
results = HeimdallDataFormat.new(profile_name: 'Netsparker Enterprise Scan',
|
62
|
+
title: "Netsparker Enterprise Scan ID: #{@scan_info['scan-id']} URL: #{@scan_info['url']}",
|
63
|
+
summary: 'Netsparker Enterprise Scan',
|
64
|
+
target_id: @scan_info['url'],
|
65
|
+
controls: controls)
|
66
|
+
results.to_hdf
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def parse_html(block)
|
72
|
+
block['#cdata-section'].to_s.strip unless block.nil?
|
73
|
+
end
|
74
|
+
|
75
|
+
def finding(vulnerability)
|
76
|
+
finding = {}
|
77
|
+
finding['status'] = 'failed'
|
78
|
+
finding['code_desc'] = []
|
79
|
+
finding['code_desc'] << "http-request : #{parse_html(vulnerability['http-request']['content'])}"
|
80
|
+
finding['code_desc'] << "method : #{vulnerability['http-request']['method']}"
|
81
|
+
finding['code_desc'] = finding['code_desc'].join("\n")
|
82
|
+
|
83
|
+
finding['message'] = []
|
84
|
+
finding['message'] << "http-response : #{parse_html(vulnerability['http-response']['content'])}"
|
85
|
+
finding['message'] << "duration : #{vulnerability['http-response']['duration']}"
|
86
|
+
finding['message'] << "status-code : #{vulnerability['http-response']['status-code']}"
|
87
|
+
finding['message'] = finding['message'].join("\n")
|
88
|
+
finding['run_time'] = NA_FLOAT
|
89
|
+
|
90
|
+
finding['start_time'] = @scan_info['initiated']
|
91
|
+
[finding]
|
92
|
+
end
|
93
|
+
|
94
|
+
def format_control_desc(vulnerability)
|
95
|
+
text = []
|
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?
|
98
|
+
text << "Extra-information: #{vulnerability['extra-information']}" unless vulnerability['extra-information'].nil?
|
99
|
+
text << "Classification: #{vulnerability['classification']}" unless vulnerability['classification'].nil?
|
100
|
+
text << "Impact: #{parse_html(vulnerability['impact'])}" unless vulnerability['impact'].nil?
|
101
|
+
text << "FirstSeenDate: #{vulnerability['FirstSeenDate']}" unless vulnerability['FirstSeenDate'].nil?
|
102
|
+
text << "LastSeenDate: #{vulnerability['LastSeenDate']}" unless vulnerability['LastSeenDate'].nil?
|
103
|
+
text << "Certainty: #{vulnerability['certainty']}" unless vulnerability['certainty'].nil?
|
104
|
+
text << "Type: #{vulnerability['type']}" unless vulnerability['type'].nil?
|
105
|
+
text << "Confirmed: #{vulnerability['confirmed']}" unless vulnerability['confirmed'].nil?
|
106
|
+
text.join('<br>')
|
107
|
+
end
|
108
|
+
|
109
|
+
def format_check_text(vulnerability)
|
110
|
+
text = []
|
111
|
+
text << "Exploitation-skills: #{parse_html(vulnerability['exploitation-skills'])}" unless vulnerability['exploitation-skills'].nil?
|
112
|
+
text << "Proof-of-concept: #{parse_html(vulnerability['proof-of-concept'])}" unless vulnerability['proof-of-concept'].nil?
|
113
|
+
text.join('<br>')
|
114
|
+
end
|
115
|
+
|
116
|
+
def format_fix_text(vulnerability)
|
117
|
+
text = []
|
118
|
+
text << "Remedial-actions: #{parse_html(vulnerability['remedial-actions'])}" unless vulnerability['remedial-actions'].nil?
|
119
|
+
text << "Remedial-procedure: #{parse_html(vulnerability['remedial-procedure'])}" unless vulnerability['remedial-procedure'].nil?
|
120
|
+
text << "Remedy-references: #{parse_html(vulnerability['remedy-references'])}" unless vulnerability['remedy-references'].nil?
|
121
|
+
text.join('<br>')
|
122
|
+
end
|
123
|
+
|
124
|
+
def nist_tag(classification)
|
125
|
+
tags = []
|
126
|
+
entries = @cwe_nist_mapping.select { |x| classification['cwe'].include?(x[:cweid].to_s) && !x[:nistid].nil? }
|
127
|
+
tags << entries.map { |x| x[:nistid] }
|
128
|
+
entries = @owasp_nist_mapping.select { |x| classification['owasp'].include?(x[:owaspid].to_s) && !x[:nistid].nil? }
|
129
|
+
tags << entries.map { |x| x[:nistid] }
|
130
|
+
tags.flatten.empty? ? DEFAULT_NIST_TAG : tags.flatten.uniq
|
131
|
+
end
|
132
|
+
|
133
|
+
def impact(severity)
|
134
|
+
IMPACT_MAPPING[severity.to_sym]
|
135
|
+
end
|
136
|
+
|
137
|
+
def parse_mapper(mapping_file)
|
138
|
+
csv_data = CSV.read(mapping_file, { encoding: 'UTF-8',
|
139
|
+
headers: true,
|
140
|
+
header_converters: :symbol,
|
141
|
+
converters: :all })
|
142
|
+
csv_data.map(&:to_hash)
|
143
|
+
end
|
144
|
+
|
145
|
+
def desc_tags(data, label)
|
146
|
+
{ data: data || NA_STRING, label: label || NA_STRING }
|
147
|
+
end
|
148
|
+
|
149
|
+
# Netsparker report could have multiple issue entries for multiple findings of same issue type.
|
150
|
+
# The meta data is identical across entries
|
151
|
+
# method collapse_duplicates return unique controls with applicable findings collapsed into it.
|
152
|
+
def collapse_duplicates(controls)
|
153
|
+
unique_controls = []
|
154
|
+
|
155
|
+
controls.map { |x| x['id'] }.uniq.each do |id|
|
156
|
+
collapsed_results = controls.select { |x| x['id'].eql?(id) }.map { |x| x['results'] }
|
157
|
+
unique_control = controls.find { |x| x['id'].eql?(id) }
|
158
|
+
unique_control['results'] = collapsed_results.flatten
|
159
|
+
unique_controls << unique_control
|
160
|
+
end
|
161
|
+
unique_controls
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|