Skip to content

Commit f2652c2

Browse files
authored
Unified csv & console output (#410)
* Added OutputEngine to group together cve output options * Updated cli.py to use it, and to have flags for output type and output filename * fixes #409
1 parent 774a3c6 commit f2652c2

File tree

2 files changed

+108
-14
lines changed

2 files changed

+108
-14
lines changed

cve_bin_tool/OutputEngine.py

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import csv
2+
import os
3+
4+
from datetime import datetime
5+
from .log import LOGGER
6+
7+
8+
class OutputEngine(object):
9+
def __init__(self, filename=None, modules=None, logger=None):
10+
if logger is None:
11+
logger = LOGGER.getChild(self.__class__.__name__)
12+
self.filename = filename
13+
self.modules = modules
14+
self.logger = logger
15+
16+
def generate_filename(self, extention=None):
17+
""" Generates a random filename"""
18+
if not extention == None:
19+
now = datetime.now().strftime("%Y-%m-%d.%H:%m:%S")
20+
self.filename = f"output.cve-bin-tool.{now}.{extention}"
21+
22+
def output_cves(self, outfile, output=None):
23+
24+
""" Output a list of CVEs
25+
format is modules[checker_name][version] = dict{id: severity}
26+
"""
27+
# if the output is csv we must open a file
28+
if output == "csv":
29+
outfile = open(f"{outfile}", "w")
30+
31+
writer = csv.writer(outfile)
32+
writer.writerow(["Module Name", "Version", "CVE Number", "Severity"])
33+
34+
writer = csv.writer(outfile)
35+
for modulename, versions in self.modules.items():
36+
for version, cve_list in versions.items():
37+
for cve_number, cve_severity in cve_list.items():
38+
row = [modulename, version, cve_number, cve_severity]
39+
writer.writerow(row)
40+
41+
# We must also close the file
42+
if output == "csv":
43+
outfile.close()
44+
45+
def output_csv(self):
46+
47+
""" Generate a CSV file for list of CVE """
48+
49+
# Check if we need to generate a filename
50+
if self.filename == None:
51+
self.generate_filename("csv")
52+
else:
53+
self.filename = f"{self.filename}.csv"
54+
# check if the filename already exists
55+
file_list = os.listdir(os.getcwd())
56+
if self.filename in file_list:
57+
self.logger.warning(
58+
f"Failed to write at '{self.filename}'. File already exists"
59+
)
60+
self.logger.info(
61+
"Generating a new filename with Default Naming Convention"
62+
)
63+
self.generate_filename("csv")
64+
65+
# try opening that file
66+
try:
67+
with open(self.filename, "w") as f:
68+
f.write("testing")
69+
os.remove(self.filename)
70+
except Exception as E:
71+
self.logger.warning(E)
72+
self.logger.info("Switching Back to Default Naming Convention")
73+
self.generate_filename("csv")
74+
75+
# Log the filename generated
76+
self.logger.info(f"CSV output stored at {os.getcwd()}/{self.filename}")
77+
78+
# call to output_cves
79+
self.output_cves(self.filename, "csv")

cve_bin_tool/cli.py

+29-14
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
from .extractor import Extractor
3030
from .strings import Strings
3131
from .file import is_binary
32+
from .OutputEngine import OutputEngine
3233

3334
from .cvedb import CVEDB
3435
from .log import LOGGER
@@ -291,18 +292,6 @@ def scan_files_unpack(unpacked):
291292
return scan_files(*unpacked)
292293

293294

294-
def output_cves(outfile, modules):
295-
""" Output a list of CVEs
296-
format is modules[checker_name][version] = dict{id: severity}
297-
"""
298-
writer = csv.writer(outfile)
299-
for modulename, versions in modules.items():
300-
for version, cve_list in versions.items():
301-
for cve_number, cve_severity in cve_list.items():
302-
row = [modulename, version, cve_number, cve_severity]
303-
writer.writerow(row)
304-
305-
306295
def main(argv=None, outfile=sys.stdout):
307296
""" Scan a binary file for certain open source libraries that may have CVEs """
308297
if argv is None:
@@ -338,6 +327,21 @@ def main(argv=None, outfile=sys.stdout):
338327
action=LogAction,
339328
choices=["debug", "info", "warning", "error", "critical"],
340329
)
330+
output_group.add_argument(
331+
"-o",
332+
"--output",
333+
action="store",
334+
choices=["csv", "json", "console"],
335+
default="console",
336+
help="update output format (default: console)",
337+
)
338+
output_group.add_argument(
339+
"-f",
340+
"--filename",
341+
action="store",
342+
default=None,
343+
help="provide output file name",
344+
)
341345
parser.add_argument(
342346
"-v", "--version", action="version", version=f"{get_version_string()}",
343347
)
@@ -478,8 +482,19 @@ def main(argv=None, outfile=sys.stdout):
478482
)
479483
)
480484
LOGGER.info(f"Known CVEs in {affected_string}:")
481-
if LOGGER.getEffectiveLevel() != logging.CRITICAL:
482-
output_cves(outfile, scanner.all_cves)
485+
486+
# Creates a Object for OutputEngine
487+
output = OutputEngine(modules=scanner.all_cves, filename=args.filename)
488+
489+
if (
490+
LOGGER.getEffectiveLevel() != logging.CRITICAL
491+
and args.output == "console"
492+
):
493+
output.output_cves(outfile)
494+
495+
# If the args are passed for csv we will generate a CSV output
496+
if args.output == "csv":
497+
output.output_csv()
483498

484499
# Use the number of files with known cves as error code
485500
# as requested by folk planning to automate use of this script.

0 commit comments

Comments
 (0)