Skip to content
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

When measuring security warnings with Trivy JSON as source, be prepar… #10698

Merged
Merged
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
When measuring security warnings with Trivy JSON as source, be prepar…
…ed for optional fields not being present.

Fixes #10672.
fniessink committed Jan 22, 2025
commit 454af711bc5d036175b88bdb2c15073372c1d7db
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
@@ -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]

Original file line number Diff line number Diff line change
@@ -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
@@ -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",
},
],
},
]
Original file line number Diff line number Diff line change
@@ -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."""
@@ -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
@@ -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