Skip to content

Commit 44a0588

Browse files
author
dnichol
committed
New script that takes a security report and for any rows that contain a BDSA record will call the Black Duck API and add solution and workaround columns to the output CSV file from the BDSA record
1 parent 18133c7 commit 44a0588

File tree

1 file changed

+126
-0
lines changed

1 file changed

+126
-0
lines changed
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
"""
2+
append_bdsa_data_to_security_report.py
3+
4+
Created on March 17, 2021
5+
6+
@author: DNicholls
7+
8+
Script that takes a security report CSV file and goes line by line and where it finds a BDSA vulnerability will call
9+
the Black Duck API to get more information from the BDSA record and adds additional columns for eacg row with this data.
10+
11+
Currently retrieves the solution and workaround text and adds these as 2 columns but can be modified to add more if required.
12+
13+
To run this script, you will need to pass the folder containing the security report, it will find any CSV file matching
14+
security*.csv and read the file line by line. Where a BDSA vulnerability is found it will call the Black Duck API
15+
to get more information on the BDSA record and add columns with the BDSA's solution and workarounds if applicable.
16+
17+
For this script to run, the hub-rest-api-python (blackduck) library and csv library will need to be installed.
18+
19+
"""
20+
21+
import argparse
22+
from blackduck.HubRestApi import HubInstance
23+
import os
24+
import glob
25+
import sys
26+
import logging
27+
import csv
28+
29+
parser = argparse.ArgumentParser("A program to add BDSA details to security reports.")
30+
parser.add_argument("folder")
31+
args = parser.parse_args()
32+
hub = HubInstance()
33+
34+
logging.basicConfig(format='%(asctime)s:%(levelname)s:%(module)s: %(message)s', stream=sys.stderr, level=logging.DEBUG)
35+
logging.getLogger("requests").setLevel(logging.WARNING)
36+
logging.getLogger("urllib3").setLevel(logging.WARNING)
37+
logging.getLogger("blackduck.HubRestApi").setLevel(logging.WARNING)
38+
39+
def checkdirs(folder):
40+
if os.path.isdir(folder) == False:
41+
raise NotADirectoryError("Folder {} is not a folder".format(folder))
42+
43+
def handle_security_reports(folder):
44+
logging.info(f"Looking for security reports in {folder}/security*.csv")
45+
for csvfile in glob.iglob(f"{folder}/security*.csv"):
46+
if '_with_bdsa' not in os.path.basename(csvfile):
47+
logging.debug(f"Handling security report file {csvfile}")
48+
handle_security_report(csvfile)
49+
50+
def handle_security_report(csvfile):
51+
# Create a new file for this report
52+
file_no_ext = os.path.splitext(csvfile)[0]
53+
file_to_create = file_no_ext + "_with_bdsa.csv"
54+
55+
if os.path.isfile(file_to_create):
56+
raise FileExistsError(f"File {file_to_create} already exists so cannot write output file")
57+
58+
# Read the security report line by line
59+
with open(file_to_create, "w") as output_csv_file:
60+
with open(csvfile, 'r') as read_obj:
61+
writer = csv.writer(output_csv_file, delimiter=',', lineterminator='\n')
62+
reader = csv.reader(read_obj)
63+
64+
all = []
65+
row = next(reader)
66+
row.append('Solution')
67+
row.append('Workaround')
68+
all.append(row)
69+
70+
for row in reader:
71+
vuln_id_cell = row[9] # Currently the column index of 'Vulnerability id' column is 9. Change if required.
72+
#logging.debug(f" CELL [{vuln_id_cell}]")
73+
bdsa_id = parse_bdsa_id(vuln_id_cell)
74+
#logging.debug(f"BDSA ID [{bdsa_id}]")
75+
if bdsa_id != None:
76+
bdsa_data = load_bdsa_data(bdsa_id)
77+
#logging.debug(f"BDSA Data Solution [{bdsa_data['solution']}]")
78+
#logging.debug(f"BDSA Data Workaround [{bdsa_data['workaround']}]")
79+
#row.append(row[0])
80+
row.append(bdsa_data['solution'])
81+
row.append(bdsa_data['workaround'])
82+
all.append(row)
83+
else:
84+
# Add the line as is.
85+
logging.debug(f"No BDSA Record")
86+
row.append(row[0])
87+
all.append(row)
88+
89+
logging.info(f"Writing output csv file [{file_to_create}]")
90+
writer.writerows(all)
91+
92+
# Write the report file.
93+
return None
94+
95+
def parse_bdsa_id(record):
96+
# Cell value could be a BDSA id, CVE id or both e.g. BDSA-2020-1128 (CVE-2020-1945)
97+
# This method will return the BDSA id if there is one regardless of the data
98+
if is_bdsa_record(record):
99+
if '(BDSA-' in record:
100+
# CVE-2020-1945 (BDSA-2020-1128)
101+
return record[record.index('(')+len('('):record.index(')')]
102+
elif '(CVE-' in record:
103+
# BDSA-2020-1128 (CVE-2020-1945)
104+
return record[0:record.index('(') - 1]
105+
elif '(' not in record:
106+
# BDSA-2020-1128
107+
return record
108+
109+
else:
110+
return None
111+
112+
113+
def is_bdsa_record(record):
114+
return 'BDSA' in record
115+
116+
def load_bdsa_data(bdsa_id):
117+
logging.debug(f"Retrieving BDSA data for [{bdsa_id}]")
118+
vuln = hub.get_vulnerabilities(bdsa_id)
119+
return vuln
120+
121+
def main():
122+
checkdirs(args.folder)
123+
handle_security_reports(args.folder)
124+
125+
126+
main()

0 commit comments

Comments
 (0)