Skip to content

Commit fd1cebc

Browse files
A program that creates a csv containing vulnerability remediation audit info for a single project or all versions within a project
1 parent e2e569a commit fd1cebc

File tree

1 file changed

+167
-0
lines changed

1 file changed

+167
-0
lines changed
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import argparse
2+
import csv
3+
import glob
4+
import logging
5+
import os
6+
import shutil
7+
import sys
8+
import time
9+
import timeit
10+
11+
import pandas
12+
from pandas.errors import EmptyDataError
13+
14+
from blackduck.HubRestApi import HubInstance
15+
16+
parser = argparse.ArgumentParser(
17+
"A program that creates a csv containing vulnerability remediation audit info for a specified project version")
18+
parser.add_argument("project")
19+
parser.add_argument('-V', '--version', default="" )
20+
parser.add_argument('-r', '--refresh', action='store_true',
21+
help='delete existing reports in the results directory and regenerate')
22+
parser.add_argument('-v', '--verbose', action='store_true', default=False, help='turn on DEBUG logging')
23+
24+
args = parser.parse_args()
25+
26+
27+
def set_logging_level(log_level):
28+
logging.basicConfig(stream=sys.stderr, level=log_level)
29+
30+
31+
if args.verbose:
32+
set_logging_level(logging.DEBUG)
33+
else:
34+
set_logging_level(logging.INFO)
35+
36+
projname = args.project
37+
timestamp = time.strftime('%m_%d_%Y_%H_%M')
38+
file_out = (projname + '_' + "vulnerability_remediation_report-" + timestamp)
39+
file_out = (file_out + ".csv")
40+
hub = HubInstance()
41+
rootDir = os.getcwd()
42+
43+
44+
def do_refresh(dir_name):
45+
temp_dir = os.path.join(rootDir, dir_name)
46+
print("tempDir=%s" % temp_dir)
47+
for fileName in os.listdir(temp_dir):
48+
print("Removing stale files %s" % fileName)
49+
os.remove(os.path.join(temp_dir, fileName))
50+
51+
52+
def check_dirs():
53+
os.chdir(rootDir)
54+
if not os.path.isdir('./temp'):
55+
os.makedirs('./temp')
56+
print('made temp directory')
57+
elif len(os.listdir('./temp')) != 0:
58+
do_refresh('temp')
59+
else:
60+
print('temp directory already exists')
61+
62+
if not os.path.isdir('./vulnerability_remediation_results'):
63+
os.makedirs('./vulnerability_remediation_results')
64+
print('made vulnerability_remediation_results directory')
65+
elif args.refresh and len(os.listdir('./vulnerability_remediation_results')) != 0:
66+
print('refreshing vulnerability_remediation_results')
67+
do_refresh('vulnerability_remediation')
68+
else:
69+
print('vulnerability_remediation directory already exists')
70+
71+
72+
# The resultant report will contain these columns for each vulnerability
73+
def get_header():
74+
return ["Project Name", "Project Version Name", "Component Name", "Component Version",
75+
"Vulnerability ID", "Remediation Status", "Created By",
76+
"Updated By", "Remediation Comment", "Created At", "Updated At"]
77+
78+
79+
def genreport():
80+
project = hub.get_project_by_name(args.project)
81+
vulnerable_bom_components_all_versions = dict()
82+
if args.version:
83+
version = hub.get_version_by_name(project, args.version)
84+
project_version_key = "{}, {}".format(project['name'], version['versionName'])
85+
version_dict = hub.get_vulnerable_bom_components(version)
86+
vulnerable_bom_components_all_versions.update(
87+
{project_version_key: {"total_count": version_dict['totalCount'],
88+
"vulnerable_components": version_dict['items']}})
89+
print("Vulnerability count returned for {} {} = {} ".format(project['name'], version['versionName'],
90+
version_dict['totalCount']))
91+
else:
92+
versions = hub.get_project_versions(project)['items']
93+
for v in versions:
94+
project_version_key = "{}, {}".format(project['name'], v['versionName'])
95+
version_dict = hub.get_vulnerable_bom_components(v)
96+
vulnerable_bom_components_all_versions.update(
97+
{project_version_key: {"total_count": version_dict['totalCount'],
98+
"vulnerable_components": version_dict['items']}})
99+
print("Vulnerability count returned for {} {} = {} ".format(project['name'], v['versionName'],
100+
version_dict['totalCount']))
101+
102+
curdir = os.getcwd()
103+
tempdir = os.path.join(curdir, 'temp')
104+
os.chdir(tempdir)
105+
with open(file_out, 'a', newline='') as f:
106+
writer = csv.writer(f)
107+
first_file = True
108+
109+
for version in vulnerable_bom_components_all_versions:
110+
for bom_component in vulnerable_bom_components_all_versions.get(version).get('vulnerable_components'):
111+
# retrieve the remediation audit trail for a vulnerability
112+
project_name = version.split(',')[0]
113+
project_version_name = version.split(',')[1]
114+
component_name = bom_component['componentName']
115+
component_version_name = bom_component['componentVersionName']
116+
117+
if first_file:
118+
header = get_header()
119+
writer.writerow(header)
120+
first_file = False
121+
try:
122+
dates = list(
123+
map(lambda d: d.split('T')[0], [bom_component['vulnerabilityWithRemediation'].get(y) for y in
124+
['remediationCreatedAt',
125+
'remediationUpdatedAt']]))
126+
row_list = [project_name, project_version_name, component_name, component_version_name] + \
127+
[bom_component['vulnerabilityWithRemediation'].get(x) for x in
128+
['vulnerabilityName',
129+
'remediationStatus',
130+
'remediationCreatedBy',
131+
'remediationUpdatedBy',
132+
'remediationComment']] + dates
133+
134+
except (KeyError, IndexError) as err:
135+
logging.debug("Issue pulling vulnerability remediation for:{}, {} not present".format(
136+
bom_component['vulnerabilityWithRemediation']['vulnerabilityName'], err))
137+
else:
138+
writer.writerow(row_list)
139+
140+
141+
def concat():
142+
curdir = os.getcwd()
143+
os.chdir(curdir)
144+
all_csvs = glob.glob(os.path.join(curdir, '*.csv'))
145+
all_data_frames = []
146+
for a_csv in all_csvs:
147+
try:
148+
data_frame = pandas.read_csv(a_csv, index_col=None)
149+
except EmptyDataError:
150+
data_frame = pandas.DataFrame()
151+
152+
all_data_frames.append(data_frame)
153+
data_frame_concat = pandas.concat(all_data_frames, axis=0, ignore_index=True)
154+
data_frame_concat.to_csv(file_out, index=False)
155+
shutil.move(file_out, '../vulnerability_remediation_results/')
156+
shutil.rmtree('../temp', ignore_errors=True)
157+
158+
159+
def main():
160+
check_dirs()
161+
start = timeit.default_timer()
162+
genreport()
163+
print("Time spent generating this report: {} seconds".format(int(timeit.default_timer() - start)))
164+
concat()
165+
166+
167+
main()

0 commit comments

Comments
 (0)