Skip to content

feat: add detailed flag (#781) #1588

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Apr 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ Usage:
-V, --version show program's version number and exit
--disable-version-check
skips checking for a new version
--detailed display detailed report
--disable-validation-check
skips checking xml files against schema
--offline operate in offline mode
Expand Down
4 changes: 4 additions & 0 deletions cve_bin_tool/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,9 @@ def main(argv=None):
help="operate in offline mode",
default=False,
)
parser.add_argument(
"--detailed", action="store_true", help="display detailed report", default=False
)

merge_report_group = parser.add_argument_group(
"Merge Report", "Arguments related to Intermediate and Merged Reports"
Expand Down Expand Up @@ -630,6 +633,7 @@ def main(argv=None):
append=args["append"],
merge_report=merged_reports,
affected_versions=args["affected_versions"],
detailed=args["detailed"],
vex_filename=args["vex"],
)

Expand Down
21 changes: 14 additions & 7 deletions cve_bin_tool/output_engine/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@
)


def output_json(all_cve_data: Dict[ProductInfo, CVEData], outfile: IO):
def output_json(
all_cve_data: Dict[ProductInfo, CVEData], outfile: IO, detailed: bool = False
):
"""Output a JSON of CVEs"""
formatted_output = format_output(all_cve_data)
formatted_output = format_output(all_cve_data, detailed)
json.dump(formatted_output, outfile, indent=" ")


Expand All @@ -55,10 +57,11 @@ def save_intermediate(
json.dump(inter_output, f, indent=" ")


def output_csv(all_cve_data: Dict[ProductInfo, CVEData], outfile):
def output_csv(
all_cve_data: Dict[ProductInfo, CVEData], outfile, detailed: bool = False
):
"""Output a CSV of CVEs"""

formatted_output = format_output(all_cve_data)
formatted_output = format_output(all_cve_data, detailed)

# Trim any leading -, =, + or @ to avoid excel macros
for cve_entry in formatted_output:
Expand All @@ -81,6 +84,8 @@ def output_csv(all_cve_data: Dict[ProductInfo, CVEData], outfile):
"comments",
],
)
if detailed:
writer.fieldnames.append("description")
writer.writeheader()
writer.writerows(formatted_output)

Expand Down Expand Up @@ -329,6 +334,7 @@ def __init__(
merge_report: Union[None, List[str]] = None,
affected_versions: int = 0,
all_cve_version_info=None,
detailed: bool = False,
vex_filename: str = "",
):
self.logger = logger or LOGGER.getChild(self.__class__.__name__)
Expand All @@ -346,6 +352,7 @@ def __init__(
self.merge_report = merge_report
self.affected_versions = affected_versions
self.all_cve_data = all_cve_data
self.detailed = detailed
self.vex_filename = vex_filename

def output_cves(self, outfile, output_type="console"):
Expand All @@ -354,9 +361,9 @@ def output_cves(self, outfile, output_type="console"):
to other formats like CSV or JSON
"""
if output_type == "json":
output_json(self.all_cve_data, outfile)
output_json(self.all_cve_data, outfile, self.detailed)
elif output_type == "csv":
output_csv(self.all_cve_data, outfile)
output_csv(self.all_cve_data, outfile, self.detailed)
elif output_type == "pdf":
output_pdf(
self.all_cve_data,
Expand Down
35 changes: 19 additions & 16 deletions cve_bin_tool/output_engine/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ def generate_filename(extension: str, prefix: str = "output") -> str:
return filename


def format_output(all_cve_data: Dict[ProductInfo, CVEData]) -> List[Dict[str, str]]:
def format_output(
all_cve_data: Dict[ProductInfo, CVEData], detailed: bool = False
) -> List[Dict[str, str]]:
"""
summary: format output in the list of dictionary format.

Expand All @@ -76,21 +78,22 @@ def format_output(all_cve_data: Dict[ProductInfo, CVEData]) -> List[Dict[str, st
formatted_output = []
for product_info, cve_data in all_cve_data.items():
for cve in cve_data["cves"]:
formatted_output.append(
{
"vendor": product_info.vendor,
"product": product_info.product,
"version": product_info.version,
"cve_number": cve.cve_number,
"severity": cve.severity,
"score": str(cve.score),
"cvss_version": str(cve.cvss_version),
"cvss_vector": cve.cvss_vector,
"paths": ", ".join(cve_data["paths"]),
"remarks": cve.remarks.name,
"comments": cve.comments,
}
)
details = {
"vendor": product_info.vendor,
"product": product_info.product,
"version": product_info.version,
"cve_number": cve.cve_number,
"severity": cve.severity,
"score": str(cve.score),
"cvss_version": str(cve.cvss_version),
"cvss_vector": cve.cvss_vector,
"paths": ", ".join(cve_data["paths"]),
"remarks": cve.remarks.name,
"comments": cve.comments,
}
if detailed:
details["description"] = cve.description
formatted_output.append(details)

return formatted_output

Expand Down
67 changes: 67 additions & 0 deletions test/test_output_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,66 @@
class TestOutputEngine(unittest.TestCase):
"""Test the OutputEngine class functions"""

MOCK_DETAILED_OUTPUT = {
ProductInfo("vendor0", "product0", "1.0"): CVEData(
cves=[
CVE(
"CVE-1234-1234",
"MEDIUM",
score=4.2,
cvss_version=2,
cvss_vector="C:H",
description="description0",
),
],
paths={""},
),
ProductInfo("vendor0", "product0", "2.8.6"): CVEData(
cves=[
CVE(
"CVE-1234-1234",
"LOW",
score=2.5,
cvss_version=3,
cvss_vector="CVSS3.0/C:H/I:L/A:M",
description="description1",
)
],
paths={""},
),
}

FORMATTED_DETAILED_OUTPUT = [
{
"vendor": "vendor0",
"product": "product0",
"version": "1.0",
"cve_number": "CVE-1234-1234",
"severity": "MEDIUM",
"score": "4.2",
"cvss_version": "2",
"cvss_vector": "C:H",
"paths": "",
"remarks": "NewFound",
"comments": "",
"description": "description0",
},
{
"vendor": "vendor0",
"product": "product0",
"version": "2.8.6",
"cve_number": "CVE-1234-1234",
"severity": "LOW",
"score": "2.5",
"cvss_version": "3",
"cvss_vector": "CVSS3.0/C:H/I:L/A:M",
"paths": "",
"remarks": "NewFound",
"comments": "",
"description": "description1",
},
]

MOCK_OUTPUT = {
ProductInfo("vendor0", "product0", "1.0"): CVEData(
cves=[
Expand Down Expand Up @@ -598,6 +658,13 @@ def test_formatted_output(self):
"""Test reformatting products"""
self.assertEqual(format_output(self.MOCK_OUTPUT), self.FORMATTED_OUTPUT)

def test_formatted_detailed_output(self):
"""Test detailed flag output"""
self.assertEqual(
format_output(self.MOCK_DETAILED_OUTPUT, detailed=True),
self.FORMATTED_DETAILED_OUTPUT,
)

def test_output_json(self):
"""Test formatting output as JSON"""
output_json(self.MOCK_OUTPUT, self.mock_file)
Expand Down