Skip to content

Commit fe098f4

Browse files
authored
Support schema version 2 of the Trivy JSON format. (#9798)
Closes #9711.
1 parent 37bc11f commit fe098f4

File tree

4 files changed

+106
-75
lines changed

4 files changed

+106
-75
lines changed

components/collector/.vulture_ignore_list.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@
118118
PipDependencies # unused class (src/source_collectors/pip/dependencies.py:10)
119119
PyupioSafetySecurityWarnings # unused class (src/source_collectors/pyupio_safety/security_warnings.py:12)
120120
QualityTimeMetrics # unused class (src/source_collectors/quality_time/metrics.py:18)
121-
QualityTimeMissingMetrics # unused class (src/source_collectors/quality_time/missing_metrics.py:11)
122121
QualityTimeSourceUpToDateness # unused class (src/source_collectors/quality_time/source_up_to_dateness.py:12)
123122
QualityTimeSourceVersion # unused class (src/source_collectors/quality_time/source_version.py:9)
124123
RobotFrameworkSourceUpToDateness # unused class (src/source_collectors/robot_framework/source_up_to_dateness.py:13)
@@ -153,7 +152,9 @@
153152
References # unused variable (src/source_collectors/trivy/security_warnings.py:26)
154153
Target # unused variable (src/source_collectors/trivy/security_warnings.py:32)
155154
Vulnerabilities # unused variable (src/source_collectors/trivy/security_warnings.py:33)
156-
TrivyJSONSecurityWarnings # unused class (src/source_collectors/trivy/security_warnings.py:39)
155+
SchemaVersion # unused variable (src/source_collectors/trivy/security_warnings.py:42)
156+
Results # unused variable (src/source_collectors/trivy/security_warnings.py:43)
157+
TrivyJSONSecurityWarnings # unused class (src/source_collectors/trivy/security_warnings.py:49)
157158
totalCount # unused variable (tests/source_collectors/github/test_merge_requests.py:16)
158159
baseRefName # unused variable (tests/source_collectors/github/test_merge_requests.py:24)
159160
createdAt # unused variable (tests/source_collectors/github/test_merge_requests.py:27)

components/collector/src/source_collectors/trivy/security_warnings.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,31 @@ class TrivyJSONVulnerability(TypedDict):
2626
References: list[str]
2727

2828

29-
class TrivyJSONDependencyRepository(TypedDict):
29+
class TrivyJSONResult(TypedDict):
3030
"""Trivy JSON for one dependency repository."""
3131

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

3535

36-
TrivyJSON = list[TrivyJSONDependencyRepository]
36+
# Trivy JSON reports come in two different forms, following schema version 1 or schema version 2.
37+
# Schema version 1 is not explicitly documented as a schema. The Trivy docs only give an example report.
38+
# See https://aquasecurity.github.io/trivy/v0.55/docs/configuration/reporting/#json.
39+
# Schema version 2 is not explicitly documented as a schema either. The only thing available seems to be a GitHub
40+
# discussion: https://github.com/aquasecurity/trivy/discussions/1050.
41+
# Issue to improve the documentation: https://github.com/aquasecurity/trivy/discussions/7552
42+
43+
TriviJSONSchemaVersion1 = list[TrivyJSONResult]
44+
45+
46+
class TrivyJSONSchemaVersion2(TypedDict):
47+
"""Trivy JSON conform schema version 2."""
48+
49+
SchemaVersion: int
50+
Results: list[TrivyJSONResult]
51+
52+
53+
TrivyJSON = TriviJSONSchemaVersion1 | TrivyJSONSchemaVersion2
3754

3855

3956
class TrivyJSONSecurityWarnings(SecurityWarningsSourceCollector, JSONFileSourceCollector):
@@ -46,9 +63,11 @@ class TrivyJSONSecurityWarnings(SecurityWarningsSourceCollector, JSONFileSourceC
4663
def _parse_json(self, json: JSON, filename: str) -> Entities:
4764
"""Override to parse the vulnerabilities from the Trivy JSON."""
4865
entities = Entities()
49-
for dependency_repository in cast(TrivyJSON, json):
50-
target = dependency_repository["Target"]
51-
for vulnerability in dependency_repository.get("Vulnerabilities") or []:
66+
trivy_json = cast(TrivyJSON, json)
67+
results = trivy_json["Results"] if isinstance(trivy_json, dict) else trivy_json
68+
for result in results:
69+
target = result["Target"]
70+
for vulnerability in result.get("Vulnerabilities") or []:
5271
vulnerability_id = vulnerability["VulnerabilityID"]
5372
package_name = vulnerability["PkgName"]
5473
entities.append(
Lines changed: 78 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
"""Unit tests for the Trivy JSON security warnings collector."""
22

3-
from typing import ClassVar
4-
5-
from source_collectors.trivy.security_warnings import TrivyJSON
6-
73
from tests.source_collectors.source_collector_test_case import SourceCollectorTestCase
84

95

@@ -12,74 +8,88 @@ class TrivyJSONSecurityWarningsTest(SourceCollectorTestCase):
128

139
SOURCE_TYPE = "trivy_json"
1410
METRIC_TYPE = "security_warnings"
15-
VULNERABILITIES_JSON: ClassVar[TrivyJSON] = [
16-
{
17-
"Target": "php-app/composer.lock",
18-
"Vulnerabilities": None,
19-
},
20-
{
21-
"Target": "trivy-ci-test (alpine 3.7.1)",
22-
"Vulnerabilities": [
23-
{
24-
"VulnerabilityID": "CVE-2018-16840",
25-
"PkgName": "curl",
26-
"InstalledVersion": "7.61.0-r0",
27-
"FixedVersion": "7.61.1-r1",
28-
"Title": 'curl: Use-after-free when closing "easy" handle in Curl_close()',
29-
"Description": "A heap use-after-free flaw was found in curl versions from 7.59.0 through ...",
30-
"Severity": "HIGH",
31-
"References": [
32-
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-16840",
33-
],
34-
},
35-
{
36-
"VulnerabilityID": "CVE-2019-3822",
37-
"PkgName": "curl",
38-
"InstalledVersion": "7.61.1-r0",
39-
"FixedVersion": "7.61.2-r2",
40-
"Title": "curl: NTLMv2 type-3 header stack buffer overflow",
41-
"Description": "libcurl versions from 7.36.0 to before 7.64.0 are vulnerable to ...",
42-
"Severity": "MEDIUM",
43-
"References": [
44-
"https://curl.haxx.se/docs/CVE-2019-3822.html",
45-
"https://lists.apache.org/thread.html",
46-
],
47-
},
48-
],
49-
},
50-
]
51-
EXPECTED_ENTITIES: ClassVar[list[dict[str, str]]] = [
52-
{
53-
"key": "CVE-2018-16840@curl@trivy-ci-test (alpine 3_7_1)",
54-
"vulnerability_id": "CVE-2018-16840",
55-
"title": 'curl: Use-after-free when closing "easy" handle in Curl_close()',
56-
"description": "A heap use-after-free flaw was found in curl versions from 7.59.0 through ...",
57-
"level": "HIGH",
58-
"package_name": "curl",
59-
"installed_version": "7.61.0-r0",
60-
"fixed_version": "7.61.1-r1",
61-
"url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-16840",
62-
},
63-
{
64-
"key": "CVE-2019-3822@curl@trivy-ci-test (alpine 3_7_1)",
65-
"vulnerability_id": "CVE-2019-3822",
66-
"title": "curl: NTLMv2 type-3 header stack buffer overflow",
67-
"description": "libcurl versions from 7.36.0 to before 7.64.0 are vulnerable to ...",
68-
"level": "MEDIUM",
69-
"package_name": "curl",
70-
"installed_version": "7.61.1-r0",
71-
"fixed_version": "7.61.2-r2",
72-
"url": "https://curl.haxx.se/docs/CVE-2019-3822.html",
73-
},
74-
]
11+
SCHEMA_VERSIONS = (1, 2)
12+
13+
def vulnerabilities_json(self, schema_version: int = 1):
14+
"""Return the Trivy Vulnerabilities JSON."""
15+
results = [
16+
{
17+
"Target": "php-app/composer.lock",
18+
"Vulnerabilities": None,
19+
},
20+
{
21+
"Target": "trivy-ci-test (alpine 3.7.1)",
22+
"Vulnerabilities": [
23+
{
24+
"VulnerabilityID": "CVE-2018-16840",
25+
"PkgName": "curl",
26+
"InstalledVersion": "7.61.0-r0",
27+
"FixedVersion": "7.61.1-r1",
28+
"Title": 'curl: Use-after-free when closing "easy" handle in Curl_close()',
29+
"Description": "A heap use-after-free flaw was found in curl versions from 7.59.0 through ...",
30+
"Severity": "HIGH",
31+
"References": [
32+
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-16840",
33+
],
34+
},
35+
{
36+
"VulnerabilityID": "CVE-2019-3822",
37+
"PkgName": "curl",
38+
"InstalledVersion": "7.61.1-r0",
39+
"FixedVersion": "7.61.2-r2",
40+
"Title": "curl: NTLMv2 type-3 header stack buffer overflow",
41+
"Description": "libcurl versions from 7.36.0 to before 7.64.0 are vulnerable to ...",
42+
"Severity": "MEDIUM",
43+
"References": [
44+
"https://curl.haxx.se/docs/CVE-2019-3822.html",
45+
"https://lists.apache.org/thread.html",
46+
],
47+
},
48+
],
49+
},
50+
]
51+
if schema_version == 1:
52+
return results
53+
return {"SchemaVersion": 2, "Results": results}
54+
55+
def expected_entities(self):
56+
"""Return the expected entities."""
57+
return [
58+
{
59+
"key": "CVE-2018-16840@curl@trivy-ci-test (alpine 3_7_1)",
60+
"vulnerability_id": "CVE-2018-16840",
61+
"title": 'curl: Use-after-free when closing "easy" handle in Curl_close()',
62+
"description": "A heap use-after-free flaw was found in curl versions from 7.59.0 through ...",
63+
"level": "HIGH",
64+
"package_name": "curl",
65+
"installed_version": "7.61.0-r0",
66+
"fixed_version": "7.61.1-r1",
67+
"url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-16840",
68+
},
69+
{
70+
"key": "CVE-2019-3822@curl@trivy-ci-test (alpine 3_7_1)",
71+
"vulnerability_id": "CVE-2019-3822",
72+
"title": "curl: NTLMv2 type-3 header stack buffer overflow",
73+
"description": "libcurl versions from 7.36.0 to before 7.64.0 are vulnerable to ...",
74+
"level": "MEDIUM",
75+
"package_name": "curl",
76+
"installed_version": "7.61.1-r0",
77+
"fixed_version": "7.61.2-r2",
78+
"url": "https://curl.haxx.se/docs/CVE-2019-3822.html",
79+
},
80+
]
7581

7682
async def test_warnings(self):
7783
"""Test the number of security warnings."""
78-
response = await self.collect(get_request_json_return_value=self.VULNERABILITIES_JSON)
79-
self.assert_measurement(response, value="2", entities=self.EXPECTED_ENTITIES)
84+
for schema_version in self.SCHEMA_VERSIONS:
85+
with self.subTest(schema_version=schema_version):
86+
response = await self.collect(get_request_json_return_value=self.vulnerabilities_json(schema_version))
87+
self.assert_measurement(response, value="2", entities=self.expected_entities())
8088

8189
async def test_warning_levels(self):
8290
"""Test the number of security warnings when specifying a level."""
8391
self.set_source_parameter("levels", ["high", "critical"])
84-
response = await self.collect(get_request_json_return_value=self.VULNERABILITIES_JSON)
85-
self.assert_measurement(response, value="1", entities=[self.EXPECTED_ENTITIES[0]])
92+
for schema_version in self.SCHEMA_VERSIONS:
93+
with self.subTest(schema_version=schema_version):
94+
response = await self.collect(get_request_json_return_value=self.vulnerabilities_json(schema_version))
95+
self.assert_measurement(response, value="1", entities=[self.expected_entities()[0]])

docs/src/changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ If your currently installed *Quality-time* version is not v5.15.0, please first
2727
- Allow for configuring Jenkins as source for the metric 'CI-pipeline duration' (GitLab CI was already supported, Azure DevOps will follow later). Partially implements [#6423](https://github.com/ICTU/quality-time/issues/6423).
2828
- Show the number of ignored measurement entities (entities marked as "False positive, "Won't fix" or "Will be fixed") in the measurement value popup. Closes [#7626](https://github.com/ICTU/quality-time/issues/7626).
2929
- Add GitHub as possible source for the 'merge requests' metric. Patch contributed by Tobias Termeczky (the/experts). Closes [#9323](https://github.com/ICTU/quality-time/issues/9323).
30+
- Support schema version 2 of the Trivy JSON format. Closes [#9711](https://github.com/ICTU/quality-time/issues/9711).
3031

3132
### Changed
3233

0 commit comments

Comments
 (0)