mirror of
https://gitlab.com/buildroot.org/buildroot.git
synced 2026-10-01 06:21:56 -09:00
support/scripts/cve-check: fix vulnerabilities with different analysis
Before this commit, only one entry per vulnerability ID was added to the
output. In CycloneDX, if you need to provide different analyses for
different affected components with the same vulnerability ID, you must
create multiple entries with the same ID.
When running `cve-check` with the `--include-resolved` argument, the
analysis of some vulnerabilities would get overwritten, which led to
undefined analysis results.
This is especially true when running the analysis on multiple components
with the same name but different versions. For instance, if the input
SBOM includes both the `gnupg` and `gnupg2` packages, CVE-2025-68973
could be included. This CVE might be exploitable for the `gnupg` package
but resolved for `gnupg2`. Therefore, a single analysis entry cannot
cover both cases.
This commit fixes the logic for adding vulnerabilities to the output
SBOM. A vulnerability is now added as a new entry if:
1. A vulnerability with the same ID doesn't exist yet.
2. The affect of the new vulnerability is not the same as the one
already present.
For the CVE-2025-68973 example this would result in the following
output:
```json
[
{
"id": "CVE-2025-68973",
"analysis": {
"state": "exploitable"
}
"affects": [
{"ref": "gnupg"}
]
},
{
"id": "CVE-2025-68973",
"analysis": {
"state": "resolved"
}
"affects": [
{"ref": "gnupg2"}
]
}
]
```
45 vulnerabilities were concerned by this bug over the Buildroot tree.
Co-Authored-By: Tim Soubry <tim.soubry@mind.be>
Signed-off-by: Thomas Perale <thomas.perale@mind.be>
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
(cherry picked from commit d4ff747a2b)
Signed-off-by: Thomas Perale <thomas.perale@mind.be>
This commit is contained in:
@@ -175,55 +175,54 @@ def nvd_cve_to_cdx_vulnerability(nvd_cve):
|
|||||||
|
|
||||||
def vuln_append_or_update_affects_if_exists(vulnerabilities, vulnerability):
|
def vuln_append_or_update_affects_if_exists(vulnerabilities, vulnerability):
|
||||||
"""
|
"""
|
||||||
Append 'vulnerability' passed as argument to the 'vulnerabilities' argument
|
Updates a matching 'vulnerability' from the 'vulnerabilities' list or
|
||||||
if an entry with the same 'id' doesn't exist yet.
|
appends it as a new entry.
|
||||||
If the vulnerability already exists, the input reference is added to the
|
|
||||||
'affects' list of the existing entry.
|
A vulnerability is considered 'matching' if it shares the same 'id' AND
|
||||||
|
either:
|
||||||
|
|
||||||
|
1. An identical 'affects' entry.
|
||||||
|
2. An identical 'analysis.state'.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
vulnerabilities (list): The vulnerabilities array reference retrieved
|
vulnerabilities (list): The vulnerabilities array reference retrieved
|
||||||
from the input CycloneDX SBOM
|
from the input CycloneDX SBOM
|
||||||
vulnerability (dict): Vulnerability to add to the 'vulnerabilities' list.
|
vulnerability (dict): Vulnerability to add to the 'vulnerabilities' list.
|
||||||
"""
|
"""
|
||||||
# Search if a vulnerability with the same identifier already exists in the
|
new_analysis = vulnerability.get("analysis", {}).get("state")
|
||||||
# SBOM vulnerability list.
|
new_ref = next((a.get("ref") for a in vulnerability.get("affects", [])), None)
|
||||||
matching_vuln = next(
|
|
||||||
(vuln for vuln in vulnerabilities if vuln.get("id") == vulnerability["id"]),
|
|
||||||
None
|
|
||||||
)
|
|
||||||
|
|
||||||
# bom-ref to the component is passed to the affects of the vulnerability
|
# All vulnerabilities with same ID
|
||||||
# passed as argument
|
matching_vulns = [v for v in vulnerabilities if v.get("id") == vulnerability.get("id")]
|
||||||
bom_ref = next((a["ref"] for a in vulnerability.get("affects", [])), None)
|
|
||||||
|
|
||||||
if matching_vuln is not None:
|
for curr_vuln in matching_vulns:
|
||||||
# Remove the affect to not use it while updating matching vuln.
|
curr_vuln_analysis = curr_vuln.get("analysis", {}).get("state")
|
||||||
if "affects" in vulnerability:
|
curr_vuln_refs = [a.get("ref") for a in curr_vuln.get("affects", [])]
|
||||||
del vulnerability["affects"]
|
|
||||||
|
|
||||||
if matching_vuln.get("analysis") is not None and "analysis" in vulnerability:
|
is_same_ref = new_ref in curr_vuln_refs
|
||||||
# We don't update vulnerabilities that already have an
|
is_same_analysis = curr_vuln_analysis == new_analysis
|
||||||
# 'analysis'.
|
|
||||||
# Buildroot ignored vulnerabilities will already have
|
if not (is_same_ref or is_same_analysis):
|
||||||
# an analysis and need to remain as such.
|
continue
|
||||||
|
|
||||||
|
if is_same_ref:
|
||||||
|
# If same vulnerability id and same affect ref, keep the previous
|
||||||
|
# analysis. This is the case where a vulnerability was ignored from
|
||||||
|
# the generated SBOM.
|
||||||
del vulnerability["analysis"]
|
del vulnerability["analysis"]
|
||||||
|
del vulnerability["affects"]
|
||||||
|
else:
|
||||||
|
# The same analysis, add a new affect
|
||||||
|
# reference.
|
||||||
|
if new_ref is not None:
|
||||||
|
curr_vuln.setdefault("affects", []).append({"ref": new_ref})
|
||||||
|
del vulnerability["affects"]
|
||||||
|
|
||||||
affects = matching_vuln.setdefault("affects", [])
|
curr_vuln.update(vulnerability)
|
||||||
|
return
|
||||||
|
|
||||||
if bom_ref is not None:
|
# No same ID w/ same analysis or same ref.
|
||||||
ref = next((a["ref"] for a in affects if a["ref"] == bom_ref), None)
|
vulnerabilities.append(vulnerability)
|
||||||
if ref is None:
|
|
||||||
# Add a 'ref' (bom reference) to the component if not
|
|
||||||
# already present in the 'affects' list.
|
|
||||||
affects.append({
|
|
||||||
"ref": bom_ref
|
|
||||||
})
|
|
||||||
|
|
||||||
# Update the metadata of the vulnerability with the one
|
|
||||||
# downloaded from the database.
|
|
||||||
matching_vuln.update(vulnerability)
|
|
||||||
else:
|
|
||||||
vulnerabilities.append(vulnerability)
|
|
||||||
|
|
||||||
|
|
||||||
def check_package_cve_affects(cve: cvecheck.CVE, cpe_product_pkgs, sbom, opt: Options):
|
def check_package_cve_affects(cve: cvecheck.CVE, cpe_product_pkgs, sbom, opt: Options):
|
||||||
@@ -297,8 +296,7 @@ def enrich_vulnerabilities(nvd_path: Path, sbom):
|
|||||||
print(f"Warning: '{vuln_id}' doesn't exist in NVD database.", file=sys.stderr)
|
print(f"Warning: '{vuln_id}' doesn't exist in NVD database.", file=sys.stderr)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
vulnerability = nvd_cve_to_cdx_vulnerability(cve.nvd_cve)
|
vuln.update(nvd_cve_to_cdx_vulnerability(cve.nvd_cve))
|
||||||
vuln_append_or_update_affects_if_exists(vulnerabilities, vulnerability)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|||||||
Reference in New Issue
Block a user