Skip to content
Open
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
3 changes: 2 additions & 1 deletion tests/scans/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
17 changes: 17 additions & 0 deletions tests/scans/scan4.json
Original file line number Diff line number Diff line change
@@ -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": {}
}
}
}
7 changes: 7 additions & 0 deletions tests/test_parse_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
Expand Down
9 changes: 5 additions & 4 deletions trivy_report/print_issues.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__":
Expand Down
13 changes: 9 additions & 4 deletions trivy_report/report_generator.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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__}"
Expand Down
59 changes: 32 additions & 27 deletions trivy_report/report_issues.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__":
Expand Down