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.
@@ -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 = ["SA-11", "RA-5"].freeze
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, name=nil, verbose = false)
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'].to_s }"
48
- finding['code_desc'] << "vulnerable_versions : #{vulnerability['component_versions']['vulnerable_versions'].to_s }"
49
- finding['code_desc'] << "fixed_versions : #{vulnerability['component_versions']['fixed_versions'].to_s }"
50
- finding['code_desc'] << "issue_type : #{vulnerability['issue_type'].to_s }"
51
- finding['code_desc'] << "provider : #{vulnerability['provider'].to_s }"
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? x[:cweid].to_s }
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
- rescue
70
- return []
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
- { "data": data || NA_STRING, "label": label || NA_STRING }
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 | vulnerability |
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).unpack("H*")[0].to_s : vulnerability['id']
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['component_versions']['more_details']['description'].to_s
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( parse_identifiers( vulnerability, 'CWE') )
128
- item['tags']['cweid'] = parse_identifiers( vulnerability, 'CWE')
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: "JFrog Xray Scan",
141
+ results = HeimdallDataFormat.new(profile_name: 'JFrog Xray Scan',
135
142
  version: NA_STRING,
136
- title: "JFrog Xray Scan",
137
- summary: "Continuous Security and Universal Artifact Analysis",
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 = File.join(RESOURCE_DIR, 'nessus-plugins-nist-mapping.csv')
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 = ["unmapped"].freeze
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 = "This Nessus Plugin does not provide output message.".freeze
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
- begin
59
- # When there are multiple hosts in the nessus report ReportHost field is an array
60
- # When there is only one host in the nessus report ReportHost field is a hash
61
- # Array() converts ReportHost to array in case there is only one host
62
- reports = @data['NessusClientData_v2']['Report']['ReportHost']
63
- reports.kind_of?(Array) ? reports : [reports]
64
- rescue StandardError => e
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
- begin
75
- policy = @data['NessusClientData_v2']['Policy']
76
- info = {}
71
+ policy = @data['NessusClientData_v2']['Policy']
72
+ info = {}
77
73
 
78
- info['policyName'] = policy['policyName']
79
- info['version'] = policy['Preferences']['ServerPreferences']['preference'].select {|x| x['name'].eql? 'sc_version'}.first['value']
80
- info
81
- rescue StandardError => e
82
- raise "Invalid Nessus XML file provided Exception: #{e}"
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
- begin
88
- timestamp = report['HostProperties']['tag'].select {|x| x['name'].eql? 'HOST_START'}.first['text']
89
- rescue StandardError => e
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 | cci_ref |
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 "0"
144
+ when '0'
152
145
  IMPACT_MAPPING[:Info]
153
- when "1","III"
146
+ when '1', 'III'
154
147
  IMPACT_MAPPING[:Low]
155
- when "2","II"
148
+ when '2', 'II'
156
149
  IMPACT_MAPPING[:Medium]
157
- when "3","I"
150
+ when '3', 'I'
158
151
  IMPACT_MAPPING[:High]
159
- when "4"
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
- { "data": data || NA_STRING, "label": label || NA_STRING }
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 | report|
188
+ @reports.each do |report|
196
189
  controls = []
197
- report['ReportItem'].each do | item |
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'] << desc_tags(item['compliance-solution'], 'check')
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