@@ -324,8 +324,8 @@ def check_cves(d, patched_cves):
# Convert CVE_STATUS into ignored CVEs and check validity
cve_ignore = []
for cve in (d.getVarFlags("CVE_STATUS") or {}):
- decoded_status, _, _ = decode_cve_status(d, cve)
- if decoded_status == "Ignored":
+ decoded_status = decode_cve_status(d, cve)
+ if 'mapping' in decoded_status and decoded_status['mapping'] == "Ignored":
cve_ignore.append(cve)
import sqlite3
@@ -507,11 +507,11 @@ def cve_write_data_text(d, patched, unpatched, ignored, cve_data):
write_string += "PACKAGE VERSION: %s%s\n" % (d.getVar("EXTENDPE"), d.getVar("PV"))
write_string += "CVE: %s\n" % cve
write_string += "CVE STATUS: %s\n" % status
- _, detail, description = decode_cve_status(d, cve)
- if detail:
- write_string += "CVE DETAIL: %s\n" % detail
- if description:
- write_string += "CVE DESCRIPTION: %s\n" % description
+ status_details = decode_cve_status(d, cve)
+ if 'detail' in status_details:
+ write_string += "CVE DETAIL: %s\n" % status_details['detail']
+ if 'description' in status_details:
+ write_string += "CVE DESCRIPTION: %s\n" % status_details['description']
write_string += "CVE SUMMARY: %s\n" % cve_data[cve]["summary"]
write_string += "CVSS v2 BASE SCORE: %s\n" % cve_data[cve]["scorev2"]
write_string += "CVSS v3 BASE SCORE: %s\n" % cve_data[cve]["scorev3"]
@@ -637,11 +637,11 @@ def cve_write_data_json(d, patched, unpatched, ignored, cve_data, cve_status):
"status" : status,
"link": issue_link
}
- _, detail, description = decode_cve_status(d, cve)
- if detail:
- cve_item["detail"] = detail
- if description:
- cve_item["description"] = description
+ status_details = decode_cve_status(d, cve)
+ if 'detail' in status_details:
+ cve_item["detail"] = status_details['detail']
+ if 'description' in status_details:
+ cve_item["description"] = status_details['description']
cve_list.append(cve_item)
package_data["issue"] = cve_list
@@ -132,8 +132,8 @@ def get_patched_cves(d):
# Search for additional patched CVEs
for cve in (d.getVarFlags("CVE_STATUS") or {}):
- decoded_status, _, _ = decode_cve_status(d, cve)
- if decoded_status == "Patched":
+ decoded_status = decode_cve_status(d, cve)
+ if 'mapping' in decoded_status and decoded_status['mapping'] == "Patched":
bb.debug(2, "CVE %s is additionally patched" % cve)
patched_cves.add(cve)
@@ -227,19 +227,40 @@ def convert_cve_version(version):
def decode_cve_status(d, cve):
"""
- Convert CVE_STATUS into status, detail and description.
+ Convert CVE_STATUS into status, vendor, product, detail and description.
"""
status = d.getVarFlag("CVE_STATUS", cve)
if not status:
- return ("", "", "")
+ return {}
+
+ status_split = status.split(':', 5)
+ status_out = {}
+ status_out["detail"] = status_split[0]
+ product = "*"
+ vendor = "*"
+ description = ""
+ if len(status_split) >= 4 and status_split[1].strip() == "cpe":
+ # Both vendor and product are mandatory if cpe: present, the syntax is then:
+ # detail: cpe:vendor:product:description
+ vendor = status_split[2].strip()
+ product = status_split[3].strip()
+ description = status_split[4].strip()
+ elif len(status_split) >= 2 and status_split[1].strip() == "cpe":
+ # Malformed CPE
+ bb.warn('Invalid CPE information for CVE_STATUS[%s] = "%s", not setting CPE' % (detail, cve, status))
+ else:
+ # Other case: no CPE, the syntax is then:
+ # detail: description
+ description = status_split[len(status_split)-1].strip() if (len(status_split) > 1) else ""
- status_split = status.split(':', 1)
- detail = status_split[0]
- description = status_split[1].strip() if (len(status_split) > 1) else ""
+ status_out["vendor"] = vendor
+ status_out["product"] = product
+ status_out["description"] = description
- status_mapping = d.getVarFlag("CVE_CHECK_STATUSMAP", detail)
+ status_mapping = d.getVarFlag("CVE_CHECK_STATUSMAP", status_out['detail'])
if status_mapping is None:
bb.warn('Invalid detail "%s" for CVE_STATUS[%s] = "%s", fallback to Unpatched' % (detail, cve, status))
status_mapping = "Unpatched"
+ status_out["mapping"] = status_mapping
- return (status_mapping, detail, description)
+ return status_out
@@ -488,21 +488,22 @@ def create_spdx(d):
cve_by_status = {}
if include_vex != "none":
for cve in d.getVarFlags("CVE_STATUS") or {}:
- status, detail, description = oe.cve_check.decode_cve_status(d, cve)
+ decoded_status = oe.cve_check.decode_cve_status(d, cve)
# If this CVE is fixed upstream, skip it unless all CVEs are
# specified.
- if include_vex != "all" and detail in (
+ if include_vex != "all" and 'detail' in decoded_status and \
+ decoded_status['detail'] in (
"fixed-version",
"cpe-stable-backport",
):
bb.debug(1, "Skipping %s since it is already fixed upstream" % cve)
continue
- cve_by_status.setdefault(status, {})[cve] = (
+ cve_by_status.setdefault(decoded_status['mapping'], {})[cve] = (
build_objset.new_cve_vuln(cve),
- detail,
- description,
+ decoded_status['detail'],
+ decoded_status['description'],
)
cpe_ids = oe.cve_check.get_cpe_ids(d.getVar("CVE_PRODUCT"), d.getVar("CVE_VERSION"))
CVE_STATUS contains assesment of a given CVE, but until now it didn't have include the affected vendor/product. In the case of a global system include, that CVE_STATUS was visible in all recipes. This patch allows encoding of affected product/vendor to each CVE_STATUS assessment, also for groups. We can then filter them later and use only CVEs that correspond to the recipe. This is going to be used in meta/conf/distro/include/cve-extra-exclusions.inc and similar places. Signed-off-by: Marta Rybczynska <marta.rybczynska@syslinbit.com> --- meta/classes/cve-check.bbclass | 24 ++++++++++----------- meta/lib/oe/cve_check.py | 39 ++++++++++++++++++++++++++-------- meta/lib/oe/spdx30_tasks.py | 11 +++++----- 3 files changed, 48 insertions(+), 26 deletions(-)