Skip to content

Commit ae25596

Browse files
author
Glenn Snyder
committed
minor tweaks
1 parent a9a3828 commit ae25596

File tree

1 file changed

+140
-0
lines changed

1 file changed

+140
-0
lines changed
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
#!/usr/bin/env python
2+
3+
'''
4+
Created on Mar 29, 2019
5+
6+
@author: gsnyder
7+
8+
Retrieve components marked as commercial and having CVE's
9+
10+
Warning: This program is single-threaded to minimize the load on the system so it can take
11+
a very long time to run.
12+
13+
'''
14+
15+
import argparse
16+
import csv
17+
from datetime import datetime
18+
import json
19+
import logging
20+
import sys
21+
22+
from blackduck.HubRestApi import HubInstance
23+
24+
parser = argparse.ArgumentParser("Find components marked as commercial, and with vulnerabilities, in the Black Duck KB and write them to an Excel file, one row per vulnerability")
25+
parser.add_argument("-f", "--file", default="has_commercial_components.csv", help="The output file name (default: has_commercial_components.csv) to use when capturing all the components marked commercial from the Black Duck KB that have vulnerabilities")
26+
parser.add_argument("-l", "--limit", type=int, default=100, help="The number of components to return with each call to the REST API (default: 100)")
27+
parser.add_argument("-t", "--total", type=int, default=99999, help="The total number of components to retrieve")
28+
args = parser.parse_args()
29+
30+
logging.basicConfig(format='%(asctime)s:%(levelname)s:%(message)s', stream=sys.stderr, level=logging.DEBUG)
31+
logging.getLogger("requests").setLevel(logging.WARNING)
32+
logging.getLogger("urllib3").setLevel(logging.WARNING)
33+
34+
hub = HubInstance()
35+
36+
components_url = hub.get_apibase() + "/search/components"
37+
38+
offset = 0
39+
total_hits = 0
40+
41+
# loop to page through the results from the KB until there are none left
42+
while total_hits < args.total:
43+
logging.debug("Retrieving components with has_commercial=true AND has_cves=true from offset {}, limit {}".format(
44+
offset, args.limit))
45+
find_commercial_url = components_url + "?q=has_commercial:true&filter=has_cves:true&limit={}&offset={}".format(
46+
args.limit, offset)
47+
48+
logging.debug("Executing GET on {}".format(find_commercial_url))
49+
results = hub.execute_get(find_commercial_url).json().get('items', [])
50+
51+
if results:
52+
offset += args.limit
53+
hits = results[0]['hits']
54+
total_hits += len(hits)
55+
logging.debug("Found {} hits, total hits now {}".format(len(hits), total_hits))
56+
57+
rows = []
58+
for hit in hits:
59+
number_versions = int(hit['fields']['release_count'][0])
60+
component_name = hit['fields']['name'][0]
61+
component_url = hit['component']
62+
component_description = hit['fields']['description'][0]
63+
if number_versions < 1000:
64+
component = hub.execute_get(hit['component']).json()
65+
versions_url = hub.get_link(component, "versions")
66+
versions = hub.execute_get(versions_url).json().get('items', [])
67+
logging.debug("Found {} versions for component {}".format(len(versions), component['name']))
68+
for version in versions:
69+
version_name = version['versionName']
70+
version_url =version['_meta']['href']
71+
vuln_url = hub.get_link(version, "vulnerabilities")
72+
vulns = hub.execute_get(vuln_url).json().get('items', [])
73+
logging.debug("Found {} vulnerabilities for version {}".format(
74+
len(vulns), version_name))
75+
for vuln in vulns:
76+
logging.debug("Adding {}".format(vuln['name']))
77+
row_data = {
78+
"Component Name": component_name,
79+
"Component URL": component_url,
80+
"Description": component_description,
81+
"Version": version_name,
82+
"Version URL": version_url,
83+
"Vuln": vuln['name'],
84+
"Vulnerability URL": vuln['_meta']['href'],
85+
"Vuln Description": vuln['description'],
86+
"Vuln Severity": vuln['severity'],
87+
"Vuln CWE URL": hub.get_link(vuln, "cwes"),
88+
"Vuln Published Date": vuln['publishedDate'],
89+
"Vuln Updated Date": vuln['updatedDate'],
90+
}
91+
92+
#
93+
# Expand CVSS2 and CVSS3 data into separate columns so they can be used (in Excel)
94+
# to filter, sort, etc
95+
#
96+
cvss2 = {}
97+
if 'temporalMetrics' in vuln['cvss2']:
98+
# expand temporal metrics
99+
cvss2_temporal_metrics = {"cvss2_temporal_"+k:v for (k,v) in vuln['cvss2']['temporalMetrics'].items()}
100+
cvss2.update(cvss2_temporal_metrics)
101+
# remove the redundant info
102+
del vuln['cvss2']['temporalMetrics']
103+
cvss2.update({"cvss2_"+k:str(v) for (k,v) in vuln['cvss2'].items()})
104+
row_data.update(cvss2)
105+
106+
cvss3 = {}
107+
if 'cvss3' in vuln:
108+
if 'temporalMetrics' in vuln['cvss3']:
109+
# expand temporal metrics
110+
cvss3_temporal_metrics = {"cvss3_temporal_"+k:v for (k,v) in vuln['cvss3']['temporalMetrics'].items()}
111+
cvss3.update(cvss3_temporal_metrics)
112+
# remove the redundant info
113+
del vuln['cvss3']['temporalMetrics']
114+
cvss3 = {"cvss3_"+k:str(v) for (k,v) in vuln['cvss3'].items()}
115+
row_data.update(cvss3)
116+
rows.append(row_data)
117+
118+
if len(hits) < args.limit:
119+
# at the end?
120+
logging.debug("Looks like we are at the end, breaking loop")
121+
break
122+
else:
123+
logging.debug("No results, exiting loop")
124+
break
125+
126+
logging.debug("Saving {} hits to has_commercial_components.csv".format(total_hits))
127+
all_columns = set()
128+
for row in rows:
129+
all_columns = all_columns.union(row.keys())
130+
131+
# Relying on spelling of keys/column names to put them into a 'nice' order
132+
# when they are written out to CSV using DictWriter
133+
all_columns = sorted(all_columns)
134+
135+
with open(args.file, "w", newline="") as csvfile:
136+
writer = csv.DictWriter(csvfile, fieldnames=all_columns)
137+
writer.writeheader()
138+
for row in rows:
139+
writer.writerow(row)
140+

0 commit comments

Comments
 (0)