diff --git a/tests/scans/readme.md b/tests/scans/readme.md index 679d370..d8fc4d8 100644 --- a/tests/scans/readme.md +++ b/tests/scans/readme.md @@ -2,4 +2,5 @@ - `scan1.json` - Example with vulnerabilities for 3 packages. - `scan2.json` - Example with single vulnerability for one package. -- `scan2.json` - Example with multiple vulnerabilities for one package. +- `scan3.json` - Example with multiple vulnerabilities for one package. +- `scan4.json` - Example with nothing for trivy to scan. diff --git a/tests/scans/scan4.json b/tests/scans/scan4.json new file mode 100644 index 0000000..f376f5c --- /dev/null +++ b/tests/scans/scan4.json @@ -0,0 +1,17 @@ +{ + "SchemaVersion": 2, + "ArtifactName": ".", + "ArtifactType": "filesystem", + "Metadata": { + "ImageConfig": { + "architecture": "", + "created": "0001-01-01T00:00:00Z", + "os": "", + "rootfs": { + "type": "", + "diff_ids": null + }, + "config": {} + } + } +} diff --git a/tests/test_parse_report.py b/tests/test_parse_report.py index fa2f743..4dc723d 100644 --- a/tests/test_parse_report.py +++ b/tests/test_parse_report.py @@ -27,6 +27,13 @@ def test_parse_report(report_filename: str, report_count: int): for report in reports: assert isinstance(report, Report) +def test_parse_report_no_results(): + # Test that it is able to parse Trivy reports with no Results list + data: ReportDict = json.load(open("tests/scans/scan4.json", "rb")) + assert isinstance(data, dict) + + parsed_result = parse_results(data, existing_issues=[]) + assert parsed_result is None def test_parse_report1(): data: ReportDict = json.load(open("tests/scans/scan1.json", "rb")) diff --git a/trivy_report/print_issues.py b/trivy_report/print_issues.py index 2206dd4..0a1f280 100755 --- a/trivy_report/print_issues.py +++ b/trivy_report/print_issues.py @@ -43,11 +43,12 @@ def main(): reports = parse_results(data, existing_issues=existing_issues) except TypeError as e: abort(f"Failed to parse Trivy JSON report: {e}") - issues = generate_issues(reports) + if reports is not None: + issues = generate_issues(reports) - for issue in issues: - print(issue.title) - print(issue.body, end="\0") + for issue in issues: + print(issue.title) + print(issue.body, end="\0") if __name__ == "__main__": diff --git a/trivy_report/report_generator.py b/trivy_report/report_generator.py index 0096e55..2ca1c30 100644 --- a/trivy_report/report_generator.py +++ b/trivy_report/report_generator.py @@ -1,6 +1,6 @@ import collections from dataclasses import dataclass -from typing import Iterator, List, Optional, OrderedDict, TypedDict +from typing import Iterator, List, Optional, OrderedDict, Tuple, TypedDict # Types for dictionaries found in JSON data @@ -74,14 +74,19 @@ class Issue: body: str -def parse_results(data: ReportDict, existing_issues: List[str]) -> Iterator[Report]: +def parse_results(data: ReportDict, existing_issues: List[str]) -> Tuple[Iterator[Report], None]: """ - Parses Trivy result structure and creates a report per package/version that was found. + Parses Trivy result structure and creates a report per package/version that + was found. Return None if no Results found, ie. nothing to parse. :param data: The report data that was parsed from JSON file. :param existing_issues: List of GitHub issues, used to exclude already reported issues. """ - results = data["Results"] + try: + results = data["Results"] + except KeyError as e: + return None + if not isinstance(results, list): raise TypeError( f"The JSON entry .Results is not a list, got: {type(results).__name__}" diff --git a/trivy_report/report_issues.py b/trivy_report/report_issues.py index eab356e..4b26648 100755 --- a/trivy_report/report_issues.py +++ b/trivy_report/report_issues.py @@ -76,35 +76,40 @@ def main(): reports = parse_results(data, existing_issues=existing_issues) except TypeError as e: abort(f"Failed to parse Trivy JSON report: {e}") - issues = generate_issues(reports) - for issue in issues: - print(f"Creating GitHub issue `{issue.title}`") - print( - f'gh --repo "{github_repo}" issue create --title "{issue.title}" --body ... --label "{input_label}" ' - + " ".join(extra_args) - ) - proc = subprocess.Popen( - [ - "gh", - "--repo", - github_repo, - "issue", - "create", - "--title", - issue.title, - "--body", - issue.body, - "--label", - input_label, - ] - + extra_args - ) - proc.communicate() - if proc.returncode != 0: - abort("Failed to create issue with `gh` cli") + if reports is None: + print("No reports to create issues for") else: - print("No new vulnerabilities found") + issues = generate_issues(reports) + + for issue in issues: + print(f"Creating GitHub issue `{issue.title}`") + print( + f'gh --repo "{github_repo}" issue create --title "{issue.title}" --body ... --label "{input_label}" ' + + " ".join(extra_args) + ) + proc = subprocess.Popen( + [ + "gh", + "--repo", + github_repo, + "issue", + "create", + "--title", + issue.title, + "--body", + issue.body, + "--label", + input_label, + ] + + extra_args + ) + proc.communicate() + if proc.returncode != 0: + abort("Failed to create issue with `gh` cli") + else: + print("No new vulnerabilities found") + if __name__ == "__main__":