Description
Buildroot recently added externalReferences with the location of source code and hashes of tarballs to components in the CycloneDX SBOM they generate. This now makes cve-bin-tool report CVEs for totally unrelated pieces of software.
To reproduce
Steps to reproduce the behaviour:
- Create
sbom.cdx.json file with:
{
"bomFormat": "CycloneDX",
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
"specVersion": "1.6",
"components": [
{
"bom-ref": "html",
"type": "library",
"name": "html",
"version": "v1.0",
"licenses": [
{
"license": {
"id": "BSD-3-Clause"
}
}
],
"externalReferences": [
{
"type": "vcs",
"url": "https://example.com/tarball-v1.0.tar.gz",
"comment": "git repository",
"hashes": [
{
"alg": "SHA-256",
"content": "06b35a3596b8417bf66f7bfdd7acd181bcb53fc36dc13216227b5cae154e0219"
}
]
}
]
}
]
}
- Run
cve-bin-tool --sbom cyclonedx --sbom-file sbom.cdx.json
Expected behaviour:
No CVE reported for components with externalReferences that do not contain CPE/pURL.
Actual behaviour:
CVEs from unrelated projects being reported based on package name and version. Here our custom package is named html and matches unknown:html and go:html CVEs while without the externalReferences it does not return anything, as there's no CPE information available.
Version/platform info
commit a33ba906b92b0e9d99bc31d94bcecf2a7a0ba474
Linux laptop-q 7.1.3-200.fc44.x86_64 #1 SMP PREEMPT_DYNAMIC Sat Jul 4 19:20:12 UTC 2026 x86_64 GNU/Linux
Python 3.14.6
Anything else?
I think the issue is that
def parse_ext_ref(self, ext_ref) -> list[tuple[str | None, str | None, str | None]]:
"""
Parse external references in an SBOM to extract module information.
Two passes are made through the external references, giving priority to CPE types,
which will always match the CVE database.
Args:
- ext_ref (List[List[str]]): List of lists representing external references.
Each inner list contains [category, type, locator].
Returns:
- Optional[Tuple[str | None, str | None, str | None]]: A tuple containing the vendor, product, and version
information extracted from the external references, or None if not found.
"""
decoded = {}
results = []
for ref in ext_ref:
ref_type = ref[1]
ref_string = ref[2]
if ref_type == "purl":
# Validation of purl is performed implicitly within the decode_purl function
decoded["purl"] = self.decode_purl(ref_string)
if ref_type == "cpe23Type" and self.is_valid_string("cpe23", ref_string):
decoded["cpe23Type"] = decode_cpe23(ref_string)
if ref_type == "cpe22Type" and self.is_valid_string("cpe22", ref_string):
decoded["cpe22Type"] = decode_cpe22(ref_string)
# No ext-ref matches, return none
if decoded.get("purl") is not None:
LOGGER.debug("Found PURL")
results.append(decoded.get("purl"))
if decoded.get("cpe23Type") is not None:
LOGGER.debug("Found CPE23")
results.append(decoded.get("cpe23Type"))
if decoded.get("cpe22Type") is not None:
LOGGER.debug("Found CPE22")
results.append(decoded.get("cpe22Type"))
if results == []:
LOGGER.debug("Nothing found")
results.append([None, None, None])
return results
[...]
def parse_cyclonedx_spdx(self) -> [(str, str, str)]:
[...]
# If Package URL or CPE record found, use this data in preference to package data
ext_ref = package.get("externalreference")
if ext_ref is not None:
external_references = self.parse_ext_ref(ext_ref=ext_ref)
# Store the data for each external reference
for vendor, package_name, version in external_references:
# For any data not found in CPE or the Package URL get from package data
if not vendor:
pass # Because no vendor was detected then all vendors with this named package
# will be included in the output.
if not package_name:
package_name = package["name"]
if (not version) and (package.get("version") is not None):
version = package["version"]
elif version is None:
LOGGER.debug(f"No version found in {package}")
if version:
# Found at least package and version, save the results
modules.append([vendor, package_name, version])
self.parse_ext_ref(ext_ref=ext_ref) will return [[None, None, None]] in case there's no cpe23type/cpe22type/purl found in the externalReferences and then iterate over it and still add to modules array. I think we should maybe not do anything if no cpe22type/cpe23type/purl is found?
cc @mmind
Description
Buildroot recently added externalReferences with the location of source code and hashes of tarballs to components in the CycloneDX SBOM they generate. This now makes cve-bin-tool report CVEs for totally unrelated pieces of software.
To reproduce
Steps to reproduce the behaviour:
sbom.cdx.jsonfile with:{ "bomFormat": "CycloneDX", "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json", "specVersion": "1.6", "components": [ { "bom-ref": "html", "type": "library", "name": "html", "version": "v1.0", "licenses": [ { "license": { "id": "BSD-3-Clause" } } ], "externalReferences": [ { "type": "vcs", "url": "https://example.com/tarball-v1.0.tar.gz", "comment": "git repository", "hashes": [ { "alg": "SHA-256", "content": "06b35a3596b8417bf66f7bfdd7acd181bcb53fc36dc13216227b5cae154e0219" } ] } ] } ] }cve-bin-tool --sbom cyclonedx --sbom-file sbom.cdx.jsonExpected behaviour:
No CVE reported for components with externalReferences that do not contain CPE/pURL.
Actual behaviour:
CVEs from unrelated projects being reported based on package name and version. Here our custom package is named html and matches
unknown:htmlandgo:htmlCVEs while without the externalReferences it does not return anything, as there's no CPE information available.Version/platform info
Anything else?
I think the issue is that
self.parse_ext_ref(ext_ref=ext_ref)will return[[None, None, None]]in case there's no cpe23type/cpe22type/purl found in the externalReferences and then iterate over it and still add tomodulesarray. I think we should maybe not do anything if no cpe22type/cpe23type/purl is found?cc @mmind