Skip to content

Commit

Permalink
When measuring security warnings with Trivy JSON as source, be prepar…
Browse files Browse the repository at this point in the history
…ed for optional fields not being present.

Fixes #10672.
  • Loading branch information
fniessink committed Jan 22, 2025
1 parent e2833e1 commit e0dfd46
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 12 deletions.
13 changes: 6 additions & 7 deletions components/collector/src/source_collectors/trivy/base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Base classes for Trivy JSON collectors."""

from typing import TypedDict
from typing import NotRequired, TypedDict

# The types below are based on https://aquasecurity.github.io/trivy/v0.45/docs/configuration/reporting/#json.
# That documentation says: "VulnerabilityID, PkgName, InstalledVersion, and Severity in Vulnerabilities are always
Expand All @@ -13,28 +13,27 @@ class TrivyJSONVulnerability(TypedDict):
"""Trivy JSON for one vulnerability."""

VulnerabilityID: str
Title: str
Description: str
Title: NotRequired[str]
Description: NotRequired[str]
Severity: str
PkgName: str
InstalledVersion: str
FixedVersion: str
References: list[str]
FixedVersion: NotRequired[str]
References: NotRequired[list[str]]


class TrivyJSONResult(TypedDict):
"""Trivy JSON for one dependency repository."""

Target: str
Vulnerabilities: list[TrivyJSONVulnerability] | None # The examples in the Trivy docs show this key can be null
Vulnerabilities: NotRequired[list[TrivyJSONVulnerability]] # Examples in the Trivy docs show this key can be null


# Trivy JSON reports come in two different forms, following schema version 1 or schema version 2.
# Schema version 1 is not explicitly documented as a schema. The Trivy docs only give an example report.
# See https://aquasecurity.github.io/trivy/v0.55/docs/configuration/reporting/#json.
# Schema version 2 is not explicitly documented as a schema either. The only thing available seems to be a GitHub
# discussion: https://github.com/aquasecurity/trivy/discussions/1050.
# Issue to improve the documentation: https://github.com/aquasecurity/trivy/discussions/7552

TriviJSONSchemaVersion1 = list[TrivyJSONResult]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,19 @@ def _parse_json(self, json: JSON, filename: str) -> Entities:
for vulnerability in result.get("Vulnerabilities") or []:
vulnerability_id = vulnerability["VulnerabilityID"]
package_name = vulnerability["PkgName"]
references = vulnerability.get("References", [])
url = references[0] if references else "" # Assume the 1st link is at least as relevant as the others
entities.append(
Entity(
key=f"{vulnerability_id}@{package_name}@{target}",
vulnerability_id=vulnerability_id,
title=vulnerability["Title"],
description=vulnerability["Description"],
title=vulnerability.get("Title", vulnerability_id),
description=vulnerability.get("Description", ""),
level=vulnerability["Severity"],
package_name=package_name,
installed_version=vulnerability["InstalledVersion"],
fixed_version=vulnerability.get("FixedVersion", ""),
url=vulnerability["References"][0], # Assume the 1st link is at least as relevant as the others
url=url,
),
)
return entities
6 changes: 6 additions & 0 deletions components/collector/tests/source_collectors/trivy/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ def vulnerabilities_json(self, schema_version: int = 2):
"Severity": "LOW",
"References": ["https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-5432"],
},
{
"VulnerabilityID": "CVE-2025-6298",
"PkgName": "This vulnerability has no optional fields",
"InstalledVersion": "3.4.1",
"Severity": "LOW",
},
],
},
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,25 @@ def expected_entities(self):
"fixed_version": "",
"url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-5432",
},
{
"key": "CVE-2025-6298@This vulnerability has no optional fields@trivy-ci-test (alpine 3_7_1)",
"vulnerability_id": "CVE-2025-6298",
"title": "CVE-2025-6298",
"description": "",
"package_name": "This vulnerability has no optional fields",
"installed_version": "3.4.1",
"level": "LOW",
"fixed_version": "",
"url": "",
},
]

async def test_warnings(self):
"""Test the number of security warnings."""
for schema_version in self.SCHEMA_VERSIONS:
with self.subTest(schema_version=schema_version):
response = await self.collect(get_request_json_return_value=self.vulnerabilities_json(schema_version))
self.assert_measurement(response, value="3", entities=self.expected_entities())
self.assert_measurement(response, value="4", entities=self.expected_entities())

async def test_warning_levels(self):
"""Test the number of security warnings when specifying a level."""
Expand All @@ -75,4 +86,4 @@ async def test_fix_not_available(self):
for schema_version in self.SCHEMA_VERSIONS:
with self.subTest(schema_version=schema_version):
response = await self.collect(get_request_json_return_value=self.vulnerabilities_json(schema_version))
self.assert_measurement(response, value="2", entities=self.expected_entities()[1:])
self.assert_measurement(response, value="3", entities=self.expected_entities()[1:])
1 change: 1 addition & 0 deletions docs/src/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ If your currently installed *Quality-time* version is not the latest version, pl

### Fixed

- When measuring security warnings with Trivy JSON as source, be prepared for optional fields not being present. Fixes [#10672](https://github.com/ICTU/quality-time/issues/10672).
- Docker compose has been integrated into Docker as a subcommand for a while, but the developer documentation did not reflect that. Change `docker-compose` to `docker compose` in the documentation. Fixes [#10684](https://github.com/ICTU/quality-time/issues/10684).

### Changed
Expand Down

0 comments on commit e0dfd46

Please sign in to comment.