2626Each processing step can be turned on or off. At least one step must be run. Default
2727is to run both.
2828
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
3030from Custom Fields in the Black Duck project. This allows different groups of projects to
3131use different remeidation settings. If a CVE remediation status should apply globally
3232to all projects, Black Duck's global remediation feature should be used.
3333
34+ The script can also get the CSV filenames from the command line arguments.
35+
3436Here is an example of the CSV data for the CVE list:
3537
3638"CVE-2016-1840","IGNORED","Applies only to Apple OS"
6769import json
6870import csv
6971import traceback
70-
72+ from pprint import pprint
7173from argparse import ArgumentParser
7274from argparse import RawDescriptionHelpFormatter
7375
8183
8284
8385def 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 :
8587 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 }
8790
8891def remediation_is_valid (vuln , remediation_data ):
8992 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+
9296 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
93100 return remediation_data [vulnerability_name ]
94101 else :
95102 return None
@@ -114,12 +121,31 @@ def find_custom_field_value (custom_fields, custom_field_label):
114121 return None
115122 return None
116123
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+
118141 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"' )
120143
121144 for vuln in vulnerable_components ['items' ]:
122145 if vuln ['vulnerabilityWithRemediation' ]['remediationStatus' ] == "NEW" :
146+ remediation_action = None
147+ exclusion_action = None
148+
123149 if (remediation_data ):
124150 remediation_action = remediation_is_valid (vuln , remediation_data )
125151
@@ -137,14 +163,20 @@ def process_vulnerabilities(hub, vulnerable_components, remediation_data=None, e
137163 reason = 'origin-exclusion'
138164
139165 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 ('\" {}\" ,\" {}\" ,\" {}\" ,\" {}\" ,\" {}\" ,\" {}\" ' .
143174 format (vuln ['componentName' ], vuln ['componentVersionName' ],
144- vuln ['componentVersionOriginId' ],
145175 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 "" } ' )
148180
149181def main (argv = None ): # IGNORE:C0111
150182 '''Command line options.'''
@@ -157,7 +189,7 @@ def main(argv=None): # IGNORE:C0111
157189 program_name = os .path .basename (sys .argv [0 ])
158190 program_version = "v%s" % __version__
159191 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 )
161193 program_shortdesc = __import__ ('__main__' ).__doc__ .split ("\n " )[1 ]
162194 program_license = '''%s
163195
@@ -178,8 +210,11 @@ def main(argv=None): # IGNORE:C0111
178210 parser = ArgumentParser (description = program_license , formatter_class = RawDescriptionHelpFormatter )
179211 parser .add_argument ("projectname" , help = "Project nname" )
180212 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" )
183218 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' )
184219 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' )
185220 parser .add_argument ('-V' , '--version' , action = 'version' , version = program_version_message )
@@ -189,9 +224,15 @@ def main(argv=None): # IGNORE:C0111
189224
190225 projectname = args .projectname
191226 projectversion = args .projectversion
227+ local_cve_remediation_file = args .local_remediation_list
228+ local_origin_exclusion_file = args .local_origin_exclusion_list
192229 process_cve_remediation = args .process_cve_remediation_list
193230 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+
195236 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 } "
196237 print (message )
197238
@@ -203,26 +244,37 @@ def main(argv=None): # IGNORE:C0111
203244 hub = HubInstance ()
204245 project = hub .get_project_by_name (projectname )
205246 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 )
207249
208250 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+
211258 remediation_data = load_remediation_input (cve_remediation_file )
212259 else :
213260 remediation_data = None
214261
215262 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 } ' )
218269 exclusion_data = load_remediation_input (exclusion_list_file )
219270 else :
220271 exclusion_data = None
272+
221273
222274 # Retrieve the vulnerabiltites for the project version
223275 vulnerable_components = hub .get_vulnerable_bom_components (version )
224276
225- process_vulnerabilities (hub , vulnerable_components , remediation_data , exclusion_data )
277+ process_vulnerabilities (hub , vulnerable_components , remediation_data , exclusion_data , dry_run )
226278
227279 return 0
228280 except Exception :
0 commit comments