26
26
Each processing step can be turned on or off. At least one step must be run. Default
27
27
is to run both.
28
28
29
- The script get's it CVE and orign lists from CSV files. The CSV filenames are loaded
29
+ The script can get's its CVE and orign lists from CSV files. The CSV filenames are loaded
30
30
from Custom Fields in the Black Duck project. This allows different groups of projects to
31
31
use different remeidation settings. If a CVE remediation status should apply globally
32
32
to all projects, Black Duck's global remediation feature should be used.
33
33
34
+ The script can also get the CSV filenames from the command line arguments.
35
+
34
36
Here is an example of the CSV data for the CVE list:
35
37
36
38
"CVE-2016-1840","IGNORED","Applies only to Apple OS"
67
69
import json
68
70
import csv
69
71
import traceback
70
-
72
+ from pprint import pprint
71
73
from argparse import ArgumentParser
72
74
from argparse import RawDescriptionHelpFormatter
73
75
81
83
82
84
83
85
def load_remediation_input (remediation_file ):
84
- with open (remediation_file , mode = 'r' ) as infile :
86
+ with open (remediation_file , mode = 'r' , encoding = "utf-8" ) as infile :
85
87
reader = csv .reader (infile )
86
- return {rows [0 ]:[rows [1 ],rows [2 ]] for rows in reader }
88
+ #return {rows[0]:[rows[1],rows[2]] for rows in reader}
89
+ return {rows [0 ]:rows [1 :] for rows in reader }
87
90
88
91
def remediation_is_valid (vuln , remediation_data ):
89
92
vulnerability_name = vuln ['vulnerabilityWithRemediation' ]['vulnerabilityName' ]
90
- # remediation_status = vuln['vulnerabilityWithRemediation']['remediationStatus']
91
- # remediation_comment = vuln['vulnerabilityWithRemediation'].get('remediationComment','')
93
+ remediation_status = vuln ['vulnerabilityWithRemediation' ]['remediationStatus' ]
94
+ remediation_comment = vuln ['vulnerabilityWithRemediation' ].get ('remediationComment' ,'' )
95
+
92
96
if vulnerability_name in remediation_data .keys ():
97
+ remediation = remediation_data [vulnerability_name ]
98
+ if (remediation_status == remediation [0 ] and remediation_comment == remediation [1 ]):
99
+ return None
93
100
return remediation_data [vulnerability_name ]
94
101
else :
95
102
return None
@@ -114,12 +121,31 @@ def find_custom_field_value (custom_fields, custom_field_label):
114
121
return None
115
122
return None
116
123
117
- def process_vulnerabilities (hub , vulnerable_components , remediation_data = None , exclusion_data = None ):
124
+
125
+
126
+ def set_vulnerablity_remediation (hub , vuln , remediation_status , remediation_comment ):
127
+ url = vuln ['_meta' ]['href' ]
128
+ update = {}
129
+ update ['remediationStatus' ] = remediation_status
130
+ update ['comment' ] = remediation_comment
131
+ response = hub .execute_put (url , data = update )
132
+ return response
133
+
134
+ def process_vulnerabilities (hub , vulnerable_components , remediation_data = None , exclusion_data = None , dry_run = False ):
135
+
136
+ if (dry_run ):
137
+ print (f"Opening dry run output file: { dry_run } " )
138
+ csv_file = open (dry_run , mode = 'w' , newline = '' , encoding = 'utf-8' )
139
+ csv_writer = csv .writer (csv_file , delimiter = ',' , quotechar = '"' , quoting = csv .QUOTE_MINIMAL )
140
+
118
141
count = 0
119
- print ('"Component Name","Component Version","Component OriginID"," CVE","Reason","Remeidation Status","HTTP response code"' )
142
+ print ('"Component Name","Component Version","CVE","Reason","Remeidation Status","HTTP response code"' )
120
143
121
144
for vuln in vulnerable_components ['items' ]:
122
145
if vuln ['vulnerabilityWithRemediation' ]['remediationStatus' ] == "NEW" :
146
+ remediation_action = None
147
+ exclusion_action = None
148
+
123
149
if (remediation_data ):
124
150
remediation_action = remediation_is_valid (vuln , remediation_data )
125
151
@@ -137,14 +163,20 @@ def process_vulnerabilities(hub, vulnerable_components, remediation_data=None, e
137
163
reason = 'origin-exclusion'
138
164
139
165
if (remediation_action ):
140
- resp = hub .set_vulnerablity_remediation (vuln , remediation_action [0 ],remediation_action [1 ])
141
- count += 1
142
- print ('\" {}\" ,\" {}\" ,\" {}\" ,\" {}\" ,\" {}\" ,\" {}\" ,\" {}\" ' .
166
+ if (dry_run ):
167
+ remediation_action .insert (0 , vuln ['vulnerabilityWithRemediation' ]['vulnerabilityName' ])
168
+ csv_writer .writerow (remediation_action )
169
+ else :
170
+ resp = set_vulnerablity_remediation (hub , vuln , remediation_action [0 ],remediation_action [1 ])
171
+ count += 1
172
+
173
+ print ('\" {}\" ,\" {}\" ,\" {}\" ,\" {}\" ,\" {}\" ,\" {}\" ' .
143
174
format (vuln ['componentName' ], vuln ['componentVersionName' ],
144
- vuln ['componentVersionOriginId' ],
145
175
vuln ['vulnerabilityWithRemediation' ]['vulnerabilityName' ],
146
- reason , remediation_action [0 ], resp .status_code ))
147
- print (f'Remediated { count } vulnerabilities.' )
176
+ reason , remediation_action [0 ], resp .status_code if not dry_run else "" ))
177
+
178
+
179
+ print (f'Remediated { count } vulnerabilities. { "(dry run)" if dry_run else "" } ' )
148
180
149
181
def main (argv = None ): # IGNORE:C0111
150
182
'''Command line options.'''
@@ -157,7 +189,7 @@ def main(argv=None): # IGNORE:C0111
157
189
program_name = os .path .basename (sys .argv [0 ])
158
190
program_version = "v%s" % __version__
159
191
program_build_date = str (__updated__ )
160
- program_version_message = '%%(prog) s %s (%s)' % (program_version , program_build_date )
192
+ program_version_message = '%s %s (%s)' % (program_name , program_version , program_build_date )
161
193
program_shortdesc = __import__ ('__main__' ).__doc__ .split ("\n " )[1 ]
162
194
program_license = '''%s
163
195
@@ -178,8 +210,11 @@ def main(argv=None): # IGNORE:C0111
178
210
parser = ArgumentParser (description = program_license , formatter_class = RawDescriptionHelpFormatter )
179
211
parser .add_argument ("projectname" , help = "Project nname" )
180
212
parser .add_argument ("projectversion" , help = "Project vesrsion" )
181
- parser .add_argument ("--no-process-cve-remediation-list" , dest = 'process_cve_remediation_list' , action = 'store_false' , help = "Disbable processing CVE-Remediation-list" )
182
- parser .add_argument ("--no-process-origin-exclusion-list" , dest = 'process_origin_exclusion_list' , action = 'store_false' , help = "Disable processing Origin-Exclusion-List" )
213
+ parser .add_argument ("--dry-run" , dest = "dry_run" , nargs = '?' , const = "dry_run.csv" , help = "dry run remediations and output to file" )
214
+ parser .add_argument ("--remediation-list" , dest = "local_remediation_list" , default = None , help = "Filename of cve remediation list csv file" )
215
+ parser .add_argument ("--origin-exclusion-list" , dest = "local_origin_exclusion_list" , default = None , help = "Filename of origin exclusion list csv file" )
216
+ parser .add_argument ("--no-process-cve-remediation-list" , dest = 'process_cve_remediation_list' , action = 'store_false' , help = "Disable processing CVE-Remediation-list" )
217
+ parser .add_argument ("--no-process-origin-exclusion-list" , dest = 'process_origin_exclusion_list' , default = None , action = 'store_false' , help = "Disable processing Origin-Exclusion-List" )
183
218
parser .add_argument ("--cve-remediation-list-custom-field-label" , default = 'CVE Remediation List' , help = 'Label of Custom Field on Black Duck that contains remeidation list file name' )
184
219
parser .add_argument ("--origin-exclusion-list-custom-field-label" , default = 'Origin Exclusion List' , help = 'Label of Custom Field on Black Duck that containts origin exclusion list file name' )
185
220
parser .add_argument ('-V' , '--version' , action = 'version' , version = program_version_message )
@@ -189,9 +224,15 @@ def main(argv=None): # IGNORE:C0111
189
224
190
225
projectname = args .projectname
191
226
projectversion = args .projectversion
227
+ local_cve_remediation_file = args .local_remediation_list
228
+ local_origin_exclusion_file = args .local_origin_exclusion_list
192
229
process_cve_remediation = args .process_cve_remediation_list
193
230
process_origin_exclulsion = args .process_origin_exclusion_list
194
-
231
+ #dry_run = args.dry_run
232
+ #dry_run_output = args.dry_run_output
233
+ dry_run = args .dry_run
234
+ print (args .dry_run )
235
+
195
236
message = f"{ program_version_message } \n \n Project: { projectname } \n Version: { projectversion } \n Process origin exclusion list: { process_origin_exclulsion } \n Process CVE remediation list: { process_cve_remediation } "
196
237
print (message )
197
238
@@ -203,26 +244,37 @@ def main(argv=None): # IGNORE:C0111
203
244
hub = HubInstance ()
204
245
project = hub .get_project_by_name (projectname )
205
246
version = hub .get_project_version_by_name (projectname , projectversion )
206
- custom_fields = hub .get_project_custom_fields (project )
247
+
248
+ custom_fields = hub .get_cf_values (project )
207
249
208
250
if (process_cve_remediation ):
209
- cve_remediation_file = find_custom_field_value (custom_fields , args .cve_remediation_list_custom_field_label )
210
- print (f' Opening: { args .cve_remediation_list_custom_field_label } :{ cve_remediation_file } ' )
251
+ if (local_cve_remediation_file ):
252
+ cve_remediation_file = local_cve_remediation_file
253
+ print (f' Opening CVE remediation file: { cve_remediation_file } ' )
254
+ else :
255
+ cve_remediation_file = find_custom_field_value (custom_fields , args .cve_remediation_list_custom_field_label )
256
+ print (f' Opening: { args .cve_remediation_list_custom_field_label } :{ cve_remediation_file } ' )
257
+
211
258
remediation_data = load_remediation_input (cve_remediation_file )
212
259
else :
213
260
remediation_data = None
214
261
215
262
if (process_origin_exclulsion ):
216
- exclusion_list_file = find_custom_field_value (custom_fields , args .origin_exclusion_list_custom_field_label )
217
- print (f' Opening: { args .origin_exclusion_list_custom_field_label } :{ exclusion_list_file } ' )
263
+ if local_origin_exclusion_file :
264
+ exclusion_list_file = local_origin_exclusion_file
265
+ print (f' Opening origin exclusion list: { exclusion_list_file } ' )
266
+ else :
267
+ exclusion_list_file = find_custom_field_value (custom_fields , args .origin_exclusion_list_custom_field_label )
268
+ print (f' Opening: { args .origin_exclusion_list_custom_field_label } :{ exclusion_list_file } ' )
218
269
exclusion_data = load_remediation_input (exclusion_list_file )
219
270
else :
220
271
exclusion_data = None
272
+
221
273
222
274
# Retrieve the vulnerabiltites for the project version
223
275
vulnerable_components = hub .get_vulnerable_bom_components (version )
224
276
225
- process_vulnerabilities (hub , vulnerable_components , remediation_data , exclusion_data )
277
+ process_vulnerabilities (hub , vulnerable_components , remediation_data , exclusion_data , dry_run )
226
278
227
279
return 0
228
280
except Exception :
0 commit comments