From patchwork Fri Nov 21 09:54:09 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Benjamin ROBIN X-Patchwork-Id: 75146 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 605EACFA765 for ; Fri, 21 Nov 2025 09:54:25 +0000 (UTC) Received: from smtpout-03.galae.net (smtpout-03.galae.net [185.246.85.4]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.8128.1763718863830456257 for ; Fri, 21 Nov 2025 01:54:25 -0800 Authentication-Results: mx.groups.io; dkim=fail reason="dkim: body hash did not verify" header.i=@bootlin.com header.s=dkim header.b=aM9HRrNH; spf=pass (domain: bootlin.com, ip: 185.246.85.4, mailfrom: benjamin.robin@bootlin.com) Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-03.galae.net (Postfix) with ESMTPS id B2B5A4E41831; Fri, 21 Nov 2025 09:54:21 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 7E31C60719; Fri, 21 Nov 2025 09:54:21 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 6DE4F10371E72; Fri, 21 Nov 2025 10:54:20 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1763718860; h=from:subject:date:message-id:to:cc:mime-version:content-type: content-transfer-encoding:in-reply-to:references; bh=aNFpnBgx3JJViawSSGTaVre9fnpGJum+lDJ7dZAPmmQ=; b=aM9HRrNHX3wFQ0On8H/b/qXgef5EHIR4tXsUwTLES6AYqZKBd7ST+UrjpdmKTJs5rowoa1 H2QexNGz1kYkFbyphLtyeCKRi2K/h3sU3YbuzUZjUh3FIBAcQaV6Blg2Jrycu2NRHqAiMZ VpiY9bUYzY/sUmE5drQAzmeBZ0aLpv9Eunink+jGsaH/VYb8ufpK3emxxbV/rha6AaoJRm B5S4HCC1wqHJICaJy7b3It0HCvQd22Vsbtt+xd0rc5XEk5OuhVG8dOO+/UXo40hY3l3FQ2 9M/BxI45kP3IAcpgxHZ9GV/6GtDxNR0MHDPNwOHeyTJpPdfF6rvcUUzHXU/EiQ== From: "Benjamin Robin (Schneider Electric)" To: openembedded-core@lists.openembedded.org Cc: "Benjamin Robin (Schneider Electric)" , thomas.petazzoni@bootlin.com, mathieu.dubois-briand@bootlin.com, miquel.raynal@bootlin.com, antonin.godard@bootlin.com, kamel.bouhara@bootlin.com, pascal.eberhard@se.com, jpewhacker@gmail.com Subject: [scarthgap v2 1/5] spdx30: provide all CVE_STATUS, not only Patched status Date: Fri, 21 Nov 2025 10:54:09 +0100 Message-ID: <20251121095415.288301-2-benjamin.robin@bootlin.com> X-Mailer: git-send-email 2.51.2 In-Reply-To: <20251121095415.288301-1-benjamin.robin@bootlin.com> References: <20251121095415.288301-1-benjamin.robin@bootlin.com> MIME-Version: 1.0 X-Last-TLS-Session-Version: TLSv1.3 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Fri, 21 Nov 2025 09:54:25 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/226658 In scarthgap, the `oe.cve_check.get_patched_cves()` method only returns CVEs with a "Patched" status. We want to retrieve all annotations, including those with an "Ignored" status. Therefore, to avoid modifying the current API, we integrate the logic for retrieving all CVE_STATUS values ​​directly into `spdx30_task`. Signed-off-by: Benjamin Robin (Schneider Electric) --- meta/lib/oe/spdx30_tasks.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py index 8115088ab8ef..e6f2beb06f4e 100644 --- a/meta/lib/oe/spdx30_tasks.py +++ b/meta/lib/oe/spdx30_tasks.py @@ -452,6 +452,22 @@ def set_purposes(d, element, *var_names, force_purposes=[]): ] +def _get_cves_info(d): + patched_cves = oe.cve_check.get_patched_cves(d) + for cve_id in (d.getVarFlags("CVE_STATUS") or {}): + mapping, detail, description = oe.cve_check.decode_cve_status(d, cve_id) + if not mapping or not detail: + bb.warn(f"Skipping {cve_id} — missing or unknown CVE status") + continue + yield cve_id, mapping, detail, description + patched_cves.discard(cve_id) + + # decode_cve_status is decoding CVE_STATUS, so patch files need to be hardcoded + for cve_id in patched_cves: + # fix-file-included is not available in scarthgap + yield cve_id, "Patched", "backported-patch", None + + def create_spdx(d): def set_var_field(var, obj, name, package=None): val = None @@ -501,20 +517,7 @@ def create_spdx(d): # Add CVEs cve_by_status = {} if include_vex != "none": - patched_cves = oe.cve_check.get_patched_cves(d) - for cve_id in patched_cves: - # decode_cve_status is decoding CVE_STATUS, so patch files need to be hardcoded - if cve_id in (d.getVarFlags("CVE_STATUS") or {}): - mapping, detail, description = oe.cve_check.decode_cve_status(d, cve_id) - else: - mapping = "Patched" - detail = "backported-patch" # fix-file-included is not available in scarthgap - description = None - - if not mapping or not detail: - bb.warn(f"Skipping {cve_id} — missing or unknown CVE status") - continue - + for cve_id, mapping, detail, description in _get_cves_info(d): # If this CVE is fixed upstream, skip it unless all CVEs are # specified. if ( From patchwork Fri Nov 21 09:54:10 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Benjamin ROBIN X-Patchwork-Id: 75148 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 415D3CFA766 for ; Fri, 21 Nov 2025 09:54:35 +0000 (UTC) Received: from smtpout-04.galae.net (smtpout-04.galae.net [185.171.202.116]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.8129.1763718864699962199 for ; Fri, 21 Nov 2025 01:54:25 -0800 Authentication-Results: mx.groups.io; dkim=fail reason="dkim: body hash did not verify" header.i=@bootlin.com header.s=dkim header.b=V1NDbj/O; spf=pass (domain: bootlin.com, ip: 185.171.202.116, mailfrom: benjamin.robin@bootlin.com) Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-04.galae.net (Postfix) with ESMTPS id C868DC1019A; Fri, 21 Nov 2025 09:54:00 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 2F1EF60719; Fri, 21 Nov 2025 09:54:23 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 3F1E210371E6A; Fri, 21 Nov 2025 10:54:21 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1763718862; h=from:subject:date:message-id:to:cc:mime-version:content-type: content-transfer-encoding:in-reply-to:references; bh=HCKCARQmHoiZT82rO2fEkwRgoDp4LzjOHPO2vhPo/lA=; b=V1NDbj/OMjQ0KDQhoTZ/MIzPf/2dMUyC4s8QZcNr6/Bq6fX2b9mRRfx81xCXDsQbpu1WL3 2f2ZWaHUTbDhrmqJEu+jxpm2cSY0SvVaat2idoUnTT1UrH8GKih9lKeQ2CsBgM28Hi/oOI hRVZ+dcoIwBawGwVj7sPf2Nh1t1T6xszRPXg7Db1m84sPbWC1Iqcsu01fg4BRSoKeykXcz WlJ4Ty9oDdSd1qbWD/Z9yaDLX9lMJzL+Mfu+y9k8cIe2yx1PbwECLDoFpzjzNpNFHorEBw 5vuF6373XVErhrdaFeJsvNTw0Y0ySGfRwF2O9uMQNTN/RebqA3TR8R2Gktfq7g== From: "Benjamin Robin (Schneider Electric)" To: openembedded-core@lists.openembedded.org Cc: "Benjamin Robin (Schneider Electric)" , thomas.petazzoni@bootlin.com, mathieu.dubois-briand@bootlin.com, miquel.raynal@bootlin.com, antonin.godard@bootlin.com, kamel.bouhara@bootlin.com, pascal.eberhard@se.com, jpewhacker@gmail.com, Marta Rybczynska , Samantha Jalabert , Richard Purdie Subject: [scarthgap v2 2/5] vex.bbclass: add a new class Date: Fri, 21 Nov 2025 10:54:10 +0100 Message-ID: <20251121095415.288301-3-benjamin.robin@bootlin.com> X-Mailer: git-send-email 2.51.2 In-Reply-To: <20251121095415.288301-1-benjamin.robin@bootlin.com> References: <20251121095415.288301-1-benjamin.robin@bootlin.com> MIME-Version: 1.0 X-Last-TLS-Session-Version: TLSv1.3 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Fri, 21 Nov 2025 09:54:35 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/226659 The "vex" class generates the minimum information that is necessary for VEX generation by an external CVE checking tool. It is a drop-in replacement of "cve-check". It uses the same variables from recipes to make the migration and backporting easier. The goal of this class is to allow generation of the CVE list of an image or distribution on-demand, including the latest information from vulnerability databases. Vulnerability data changes every day, so a status generated at build becomes out-of-date very soon. Research done for this work shows that the current VEX formats (CSAF and OpenVEX) do not provide enough information to generate such rolling information. Instead, we extract the needed data from recipe annotations (package names, CPEs, versions, CVE patches applied...) and store for later use in the format that is an extension of the CVE-check JSON output format. This output can be then used (separately or with SPDX of the same build) by an external tool to generate the vulnerability annotation and VEX statements in standard formats. When back-porting this feature, the do_generate_vex() had to be modified to use the "old" get_patched_cves() API. Signed-off-by: Marta Rybczynska Signed-off-by: Samantha Jalabert Signed-off-by: Richard Purdie (cherry picked from commit 6352ad93a72e67d6dfa82e870222518a97c426fa) Signed-off-by: Benjamin Robin (Schneider Electric) --- meta/classes/vex.bbclass | 327 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 327 insertions(+) create mode 100644 meta/classes/vex.bbclass diff --git a/meta/classes/vex.bbclass b/meta/classes/vex.bbclass new file mode 100644 index 000000000000..73dd9338a1d5 --- /dev/null +++ b/meta/classes/vex.bbclass @@ -0,0 +1,327 @@ +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: MIT +# + +# This class is used to generate metadata needed by external +# tools to check for vulnerabilities, for example CVEs. +# +# In order to use this class just inherit the class in the +# local.conf file and it will add the generate_vex task for +# every recipe. If an image is build it will generate a report +# in DEPLOY_DIR_IMAGE for all the packages used, it will also +# generate a file for all recipes used in the build. +# +# Variables use CVE_CHECK prefix to keep compatibility with +# the cve-check class +# +# Example: +# bitbake -c generate_vex openssl +# bitbake core-image-sato +# bitbake -k -c generate_vex universe +# +# The product name that the CVE database uses defaults to BPN, but may need to +# be overriden per recipe (for example tiff.bb sets CVE_PRODUCT=libtiff). +CVE_PRODUCT ??= "${BPN}" +CVE_VERSION ??= "${PV}" + +CVE_CHECK_SUMMARY_DIR ?= "${LOG_DIR}/cve" + +CVE_CHECK_SUMMARY_FILE_NAME_JSON = "cve-summary.json" +CVE_CHECK_SUMMARY_INDEX_PATH = "${CVE_CHECK_SUMMARY_DIR}/cve-summary-index.txt" + +CVE_CHECK_DIR ??= "${DEPLOY_DIR}/cve" +CVE_CHECK_RECIPE_FILE_JSON ?= "${CVE_CHECK_DIR}/${PN}_cve.json" +CVE_CHECK_MANIFEST_JSON ?= "${IMGDEPLOYDIR}/${IMAGE_NAME}.json" + +# Skip CVE Check for packages (PN) +CVE_CHECK_SKIP_RECIPE ?= "" + +# Replace NVD DB check status for a given CVE. Each of CVE has to be mentioned +# separately with optional detail and description for this status. +# +# CVE_STATUS[CVE-1234-0001] = "not-applicable-platform: Issue only applies on Windows" +# CVE_STATUS[CVE-1234-0002] = "fixed-version: Fixed externally" +# +# Settings the same status and reason for multiple CVEs is possible +# via CVE_STATUS_GROUPS variable. +# +# CVE_STATUS_GROUPS = "CVE_STATUS_WIN CVE_STATUS_PATCHED" +# +# CVE_STATUS_WIN = "CVE-1234-0001 CVE-1234-0003" +# CVE_STATUS_WIN[status] = "not-applicable-platform: Issue only applies on Windows" +# CVE_STATUS_PATCHED = "CVE-1234-0002 CVE-1234-0004" +# CVE_STATUS_PATCHED[status] = "fixed-version: Fixed externally" +# +# All possible CVE statuses could be found in cve-check-map.conf +# CVE_CHECK_STATUSMAP[not-applicable-platform] = "Ignored" +# CVE_CHECK_STATUSMAP[fixed-version] = "Patched" +# +# CVE_CHECK_IGNORE is deprecated and CVE_STATUS has to be used instead. +# Keep CVE_CHECK_IGNORE until other layers migrate to new variables +CVE_CHECK_IGNORE ?= "" + +# Layers to be excluded +CVE_CHECK_LAYER_EXCLUDELIST ??= "" + +# Layers to be included +CVE_CHECK_LAYER_INCLUDELIST ??= "" + + +# set to "alphabetical" for version using single alphabetical character as increment release +CVE_VERSION_SUFFIX ??= "" + +python () { + if bb.data.inherits_class("cve-check", d): + raise bb.parse.SkipRecipe("Skipping recipe: found incompatible combination of cve-check and vex enabled at the same time.") + + # Fallback all CVEs from CVE_CHECK_IGNORE to CVE_STATUS + cve_check_ignore = d.getVar("CVE_CHECK_IGNORE") + if cve_check_ignore: + bb.warn("CVE_CHECK_IGNORE is deprecated in favor of CVE_STATUS") + for cve in (d.getVar("CVE_CHECK_IGNORE") or "").split(): + d.setVarFlag("CVE_STATUS", cve, "ignored") + + # Process CVE_STATUS_GROUPS to set multiple statuses and optional detail or description at once + for cve_status_group in (d.getVar("CVE_STATUS_GROUPS") or "").split(): + cve_group = d.getVar(cve_status_group) + if cve_group is not None: + for cve in cve_group.split(): + d.setVarFlag("CVE_STATUS", cve, d.getVarFlag(cve_status_group, "status")) + else: + bb.warn("CVE_STATUS_GROUPS contains undefined variable %s" % cve_status_group) +} + +def generate_json_report(d, out_path, link_path): + if os.path.exists(d.getVar("CVE_CHECK_SUMMARY_INDEX_PATH")): + import json + from oe.cve_check import cve_check_merge_jsons, update_symlinks + + bb.note("Generating JSON CVE summary") + index_file = d.getVar("CVE_CHECK_SUMMARY_INDEX_PATH") + summary = {"version":"1", "package": []} + with open(index_file) as f: + filename = f.readline() + while filename: + with open(filename.rstrip()) as j: + data = json.load(j) + cve_check_merge_jsons(summary, data) + filename = f.readline() + + summary["package"].sort(key=lambda d: d['name']) + + with open(out_path, "w") as f: + json.dump(summary, f, indent=2) + + update_symlinks(out_path, link_path) + +python vex_save_summary_handler () { + import shutil + import datetime + from oe.cve_check import update_symlinks + + cvelogpath = d.getVar("CVE_CHECK_SUMMARY_DIR") + + bb.utils.mkdirhier(cvelogpath) + timestamp = datetime.datetime.now().strftime('%Y%m%d%H%M%S') + + json_summary_link_name = os.path.join(cvelogpath, d.getVar("CVE_CHECK_SUMMARY_FILE_NAME_JSON")) + json_summary_name = os.path.join(cvelogpath, "cve-summary-%s.json" % (timestamp)) + generate_json_report(d, json_summary_name, json_summary_link_name) + bb.plain("Complete CVE JSON report summary created at: %s" % json_summary_link_name) +} + +addhandler vex_save_summary_handler +vex_save_summary_handler[eventmask] = "bb.event.BuildCompleted" + +python do_generate_vex () { + """ + Generate metadata needed for vulnerability checking for + the current recipe + """ + from oe.cve_check import get_patched_cves, decode_cve_status + + cves_status = [] + products = d.getVar("CVE_PRODUCT").split() + for product in products: + if ":" in product: + _, product = product.split(":", 1) + cves_status.append([product, False]) + + patched_cves = get_patched_cves(d) + cve_data = {} + for cve_id in (d.getVarFlags("CVE_STATUS") or {}): + mapping, detail, description = decode_cve_status(d, cve_id) + if not mapping or not detail: + bb.warn(f"Skipping {cve_id} — missing or unknown CVE status") + continue + cve_data[cve_id] = { + "abbrev-status": mapping, + "status": detail, + "justification": description + } + patched_cves.discard(cve_id) + + # decode_cve_status is decoding CVE_STATUS, so patch files need to be hardcoded + for cve_id in patched_cves: + # fix-file-included is not available in scarthgap + cve_data[cve_id] = { + "abbrev-status": "Patched", + "status": "backported-patch", + } + + cve_write_data_json(d, cve_data, cves_status) +} + +addtask generate_vex before do_build + +python vex_cleanup () { + """ + Delete the file used to gather all the CVE information. + """ + bb.utils.remove(e.data.getVar("CVE_CHECK_SUMMARY_INDEX_PATH")) +} + +addhandler vex_cleanup +vex_cleanup[eventmask] = "bb.event.BuildCompleted" + +python vex_write_rootfs_manifest () { + """ + Create VEX/CVE manifest when building an image + """ + + import json + from oe.rootfs import image_list_installed_packages + from oe.cve_check import cve_check_merge_jsons, update_symlinks + + deploy_file_json = d.getVar("CVE_CHECK_RECIPE_FILE_JSON") + if os.path.exists(deploy_file_json): + bb.utils.remove(deploy_file_json) + + # Create a list of relevant recipies + recipies = set() + for pkg in list(image_list_installed_packages(d)): + pkg_info = os.path.join(d.getVar('PKGDATA_DIR'), + 'runtime-reverse', pkg) + pkg_data = oe.packagedata.read_pkgdatafile(pkg_info) + recipies.add(pkg_data["PN"]) + + bb.note("Writing rootfs VEX manifest") + deploy_dir = d.getVar("IMGDEPLOYDIR") + link_name = d.getVar("IMAGE_LINK_NAME") + + json_data = {"version":"1", "package": []} + text_data = "" + + save_pn = d.getVar("PN") + + for pkg in recipies: + # To be able to use the CVE_CHECK_RECIPE_FILE_JSON variable we have to evaluate + # it with the different PN names set each time. + d.setVar("PN", pkg) + + pkgfilepath = d.getVar("CVE_CHECK_RECIPE_FILE_JSON") + if os.path.exists(pkgfilepath): + with open(pkgfilepath) as j: + data = json.load(j) + cve_check_merge_jsons(json_data, data) + + d.setVar("PN", save_pn) + + link_path = os.path.join(deploy_dir, "%s.json" % link_name) + manifest_name = d.getVar("CVE_CHECK_MANIFEST_JSON") + + with open(manifest_name, "w") as f: + json.dump(json_data, f, indent=2) + + update_symlinks(manifest_name, link_path) + bb.plain("Image VEX JSON report stored in: %s" % manifest_name) +} + +ROOTFS_POSTPROCESS_COMMAND:prepend = "vex_write_rootfs_manifest; " +do_rootfs[recrdeptask] += "do_generate_vex " +do_populate_sdk[recrdeptask] += "do_generate_vex " + +def cve_write_data_json(d, cve_data, cve_status): + """ + Prepare CVE data for the JSON format, then write it. + Done for each recipe. + """ + + from oe.cve_check import get_cpe_ids + import json + + output = {"version":"1", "package": []} + nvd_link = "https://nvd.nist.gov/vuln/detail/" + + fdir_name = d.getVar("FILE_DIRNAME") + layer = fdir_name.split("/")[-3] + + include_layers = d.getVar("CVE_CHECK_LAYER_INCLUDELIST").split() + exclude_layers = d.getVar("CVE_CHECK_LAYER_EXCLUDELIST").split() + + if exclude_layers and layer in exclude_layers: + return + + if include_layers and layer not in include_layers: + return + + product_data = [] + for s in cve_status: + p = {"product": s[0], "cvesInRecord": "Yes"} + if s[1] == False: + p["cvesInRecord"] = "No" + product_data.append(p) + product_data = list({p['product']:p for p in product_data}.values()) + + package_version = "%s%s" % (d.getVar("EXTENDPE"), d.getVar("PV")) + cpes = get_cpe_ids(d.getVar("CVE_PRODUCT"), d.getVar("CVE_VERSION")) + package_data = { + "name" : d.getVar("PN"), + "layer" : layer, + "version" : package_version, + "products": product_data, + "cpes": cpes + } + + cve_list = [] + + for cve in sorted(cve_data): + issue_link = "%s%s" % (nvd_link, cve) + + cve_item = { + "id" : cve, + "status" : cve_data[cve]["abbrev-status"], + "link": issue_link, + } + if 'NVD-summary' in cve_data[cve]: + cve_item["summary"] = cve_data[cve]["NVD-summary"] + cve_item["scorev2"] = cve_data[cve]["NVD-scorev2"] + cve_item["scorev3"] = cve_data[cve]["NVD-scorev3"] + cve_item["vector"] = cve_data[cve]["NVD-vector"] + cve_item["vectorString"] = cve_data[cve]["NVD-vectorString"] + if 'status' in cve_data[cve]: + cve_item["detail"] = cve_data[cve]["status"] + if 'justification' in cve_data[cve]: + cve_item["description"] = cve_data[cve]["justification"] + if 'resource' in cve_data[cve]: + cve_item["patch-file"] = cve_data[cve]["resource"] + cve_list.append(cve_item) + + package_data["issue"] = cve_list + output["package"].append(package_data) + + deploy_file = d.getVar("CVE_CHECK_RECIPE_FILE_JSON") + + write_string = json.dumps(output, indent=2) + + cvelogpath = d.getVar("CVE_CHECK_SUMMARY_DIR") + index_path = d.getVar("CVE_CHECK_SUMMARY_INDEX_PATH") + bb.utils.mkdirhier(cvelogpath) + fragment_file = os.path.basename(deploy_file) + fragment_path = os.path.join(cvelogpath, fragment_file) + with open(fragment_path, "w") as f: + f.write(write_string) + with open(index_path, "a+") as f: + f.write("%s\n" % fragment_path) From patchwork Fri Nov 21 09:54:11 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin ROBIN X-Patchwork-Id: 75147 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3DA4ACFA765 for ; Fri, 21 Nov 2025 09:54:35 +0000 (UTC) Received: from smtpout-02.galae.net (smtpout-02.galae.net [185.246.84.56]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.8130.1763718866009982324 for ; Fri, 21 Nov 2025 01:54:26 -0800 Authentication-Results: mx.groups.io; dkim=fail reason="dkim: body hash did not verify" header.i=@bootlin.com header.s=dkim header.b=OUGb7kds; spf=pass (domain: bootlin.com, ip: 185.246.84.56, mailfrom: benjamin.robin@bootlin.com) Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-02.galae.net (Postfix) with ESMTPS id 807101A1C82; Fri, 21 Nov 2025 09:54:24 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 5722B60719; Fri, 21 Nov 2025 09:54:24 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id D20A710371B99; Fri, 21 Nov 2025 10:54:22 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1763718863; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=dTOUF5ZoILiTaQ8L/tu/O80mHkDv9FNtrmyrkVO+bnU=; b=OUGb7kdsyBlRkIk1mYx1vgJfxy0chSE6K0xwYUjP/nflnlHq03SvTmdy7PgFw5esjAoUcc 9ii5HBGZLQiulTWT0sQqHqrGYt/2u1IfIJfIminL7cV8Q0u407zIjRS2zRNfrOYhUlatab 8K2pToLm+pMusgzG1pfhew3uEIq5aI/lQYH4RoszYYIR6XlFuw+z5p2yILsFwsOLV3j9/t y3kVXb2VrjQCuiHk5Kxu68JtoiZQK+VPHDaHgMG4EuuFWjMCNTgtOGjKVMTu0AZ9yZfEmJ XY5QEahq+E4zASagTVhE3/w0TE1VH0zCCjFrHC/bG32+IWcoSGCTqmf+uEOOnQ== From: "Benjamin Robin (Schneider Electric)" To: openembedded-core@lists.openembedded.org Cc: "Benjamin Robin (Schneider Electric)" , thomas.petazzoni@bootlin.com, mathieu.dubois-briand@bootlin.com, miquel.raynal@bootlin.com, antonin.godard@bootlin.com, kamel.bouhara@bootlin.com, pascal.eberhard@se.com, jpewhacker@gmail.com, Peter Marko , Richard Purdie Subject: [scarthgap v2 3/5] cve-check: extract extending CVE_STATUS to library function Date: Fri, 21 Nov 2025 10:54:11 +0100 Message-ID: <20251121095415.288301-4-benjamin.robin@bootlin.com> X-Mailer: git-send-email 2.51.2 In-Reply-To: <20251121095415.288301-1-benjamin.robin@bootlin.com> References: <20251121095415.288301-1-benjamin.robin@bootlin.com> MIME-Version: 1.0 X-Last-TLS-Session-Version: TLSv1.3 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Fri, 21 Nov 2025 09:54:35 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/226660 The same code for extending CVE_STATUS by CVE_CHECK_IGNORE and CVE_STATUS_GROUPS is used on multiple places. Create a library function to have the code on single place and ready for reuse by additional classes. Conflicts: meta/classes/cve-check.bbclass meta/lib/oe/cve_check.py Signed-off-by: Peter Marko Signed-off-by: Richard Purdie (cherry picked from commit 45e18f4270d084d81c21b1e5a4a601ce975d8a77) Signed-off-by: Benjamin Robin (Schneider Electric) --- meta/classes/cve-check.bbclass | 17 ++--------------- meta/classes/vex.bbclass | 17 ++--------------- meta/lib/oe/cve_check.py | 22 ++++++++++++++++++++++ 3 files changed, 26 insertions(+), 30 deletions(-) diff --git a/meta/classes/cve-check.bbclass b/meta/classes/cve-check.bbclass index d08c6ac670cf..f5bbaa5d159a 100644 --- a/meta/classes/cve-check.bbclass +++ b/meta/classes/cve-check.bbclass @@ -107,21 +107,8 @@ CVE_CHECK_LAYER_INCLUDELIST ??= "" CVE_VERSION_SUFFIX ??= "" python () { - # Fallback all CVEs from CVE_CHECK_IGNORE to CVE_STATUS - cve_check_ignore = d.getVar("CVE_CHECK_IGNORE") - if cve_check_ignore: - bb.warn("CVE_CHECK_IGNORE is deprecated in favor of CVE_STATUS") - for cve in (d.getVar("CVE_CHECK_IGNORE") or "").split(): - d.setVarFlag("CVE_STATUS", cve, "ignored") - - # Process CVE_STATUS_GROUPS to set multiple statuses and optional detail or description at once - for cve_status_group in (d.getVar("CVE_STATUS_GROUPS") or "").split(): - cve_group = d.getVar(cve_status_group) - if cve_group is not None: - for cve in cve_group.split(): - d.setVarFlag("CVE_STATUS", cve, d.getVarFlag(cve_status_group, "status")) - else: - bb.warn("CVE_STATUS_GROUPS contains undefined variable %s" % cve_status_group) + from oe.cve_check import extend_cve_status + extend_cve_status(d) } def generate_json_report(d, out_path, link_path): diff --git a/meta/classes/vex.bbclass b/meta/classes/vex.bbclass index 73dd9338a1d5..c447b37db89b 100644 --- a/meta/classes/vex.bbclass +++ b/meta/classes/vex.bbclass @@ -76,21 +76,8 @@ python () { if bb.data.inherits_class("cve-check", d): raise bb.parse.SkipRecipe("Skipping recipe: found incompatible combination of cve-check and vex enabled at the same time.") - # Fallback all CVEs from CVE_CHECK_IGNORE to CVE_STATUS - cve_check_ignore = d.getVar("CVE_CHECK_IGNORE") - if cve_check_ignore: - bb.warn("CVE_CHECK_IGNORE is deprecated in favor of CVE_STATUS") - for cve in (d.getVar("CVE_CHECK_IGNORE") or "").split(): - d.setVarFlag("CVE_STATUS", cve, "ignored") - - # Process CVE_STATUS_GROUPS to set multiple statuses and optional detail or description at once - for cve_status_group in (d.getVar("CVE_STATUS_GROUPS") or "").split(): - cve_group = d.getVar(cve_status_group) - if cve_group is not None: - for cve in cve_group.split(): - d.setVarFlag("CVE_STATUS", cve, d.getVarFlag(cve_status_group, "status")) - else: - bb.warn("CVE_STATUS_GROUPS contains undefined variable %s" % cve_status_group) + from oe.cve_check import extend_cve_status + extend_cve_status(d) } def generate_json_report(d, out_path, link_path): diff --git a/meta/lib/oe/cve_check.py b/meta/lib/oe/cve_check.py index ed5c714cb8b9..7c09b7824215 100644 --- a/meta/lib/oe/cve_check.py +++ b/meta/lib/oe/cve_check.py @@ -243,3 +243,25 @@ def decode_cve_status(d, cve): status_mapping = "Unpatched" return (status_mapping, detail, description) + +def extend_cve_status(d): + # do this only once in case multiple classes use this + if d.getVar("CVE_STATUS_EXTENDED"): + return + d.setVar("CVE_STATUS_EXTENDED", "1") + + # Fallback all CVEs from CVE_CHECK_IGNORE to CVE_STATUS + cve_check_ignore = d.getVar("CVE_CHECK_IGNORE") + if cve_check_ignore: + bb.warn("CVE_CHECK_IGNORE is deprecated in favor of CVE_STATUS") + for cve in (d.getVar("CVE_CHECK_IGNORE") or "").split(): + d.setVarFlag("CVE_STATUS", cve, "ignored") + + # Process CVE_STATUS_GROUPS to set multiple statuses and optional detail or description at once + for cve_status_group in (d.getVar("CVE_STATUS_GROUPS") or "").split(): + cve_group = d.getVar(cve_status_group) + if cve_group is not None: + for cve in cve_group.split(): + d.setVarFlag("CVE_STATUS", cve, d.getVarFlag(cve_status_group, "status")) + else: + bb.warn("CVE_STATUS_GROUPS contains undefined variable %s" % cve_status_group) From patchwork Fri Nov 21 09:54:12 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin ROBIN X-Patchwork-Id: 75149 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4CA83CFA76B for ; Fri, 21 Nov 2025 09:54:35 +0000 (UTC) Received: from smtpout-04.galae.net (smtpout-04.galae.net [185.171.202.116]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.8131.1763718866533331410 for ; Fri, 21 Nov 2025 01:54:26 -0800 Authentication-Results: mx.groups.io; dkim=fail reason="dkim: body hash did not verify" header.i=@bootlin.com header.s=dkim header.b=IrjJBjg7; spf=pass (domain: bootlin.com, ip: 185.171.202.116, mailfrom: benjamin.robin@bootlin.com) Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-04.galae.net (Postfix) with ESMTPS id C0B19C10199; Fri, 21 Nov 2025 09:54:02 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 2840D60719; Fri, 21 Nov 2025 09:54:25 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 08E2810371E8C; Fri, 21 Nov 2025 10:54:23 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1763718864; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=wVnSF+0mwo1kAqyA1/Gulyd28KtE7GEVWFloAlwhxbc=; b=IrjJBjg7ntre6L6jV2utIARGUyZ9nMQbN5CtEzEhVXAu7z6uK65NWsYQwB+F80JqAhpfNn aeMG2n7SzSffZMwjZeDixJWHdHKeU5KIIMgprafr4QUq8v+cYn/Dob/r4C4isn1j11Gv3l CRbgL7gTXVEcmBzZya7ch851yBF1nU/fF9Q2dt96Y2K5QIg9c3SdXnKL7tFf9neqNMQVAk cc3bZBmLraCgfN6F8kDIgtpg44YReOYJg2ZHlSHkVGYV0uH2lAs+wzGKZHfktsA5hiUQFl 2ZO+T/sFLqY22VGCh9pUsOyiDL7nueFqia2kjK8jTYfjjWH0L9iZJvHFdG406w== From: "Benjamin Robin (Schneider Electric)" To: openembedded-core@lists.openembedded.org Cc: "Benjamin Robin (Schneider Electric)" , thomas.petazzoni@bootlin.com, mathieu.dubois-briand@bootlin.com, miquel.raynal@bootlin.com, antonin.godard@bootlin.com, kamel.bouhara@bootlin.com, pascal.eberhard@se.com, jpewhacker@gmail.com, Peter Marko , Richard Purdie Subject: [scarthgap v2 4/5] spdx: extend CVE_STATUS variables Date: Fri, 21 Nov 2025 10:54:12 +0100 Message-ID: <20251121095415.288301-5-benjamin.robin@bootlin.com> X-Mailer: git-send-email 2.51.2 In-Reply-To: <20251121095415.288301-1-benjamin.robin@bootlin.com> References: <20251121095415.288301-1-benjamin.robin@bootlin.com> MIME-Version: 1.0 X-Last-TLS-Session-Version: TLSv1.3 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Fri, 21 Nov 2025 09:54:35 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/226661 If spdx is generated without inheriting cve/vex classes (which is poky default), only explicitly set CVE_STATUS fields are handled. Calculated ones (e.g. from CVE_STATUS_GROUPS) are ignored. Fix this by expanding the CVE_STATUS in spdx classes. Signed-off-by: Peter Marko Signed-off-by: Richard Purdie (cherry picked from commit ead9c6a8770463c21210a57cc5320f44f7754dd3) Signed-off-by: Benjamin Robin (Schneider Electric) --- meta/classes/spdx-common.bbclass | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/meta/classes/spdx-common.bbclass b/meta/classes/spdx-common.bbclass index 36feb5680777..713a7fc651e5 100644 --- a/meta/classes/spdx-common.bbclass +++ b/meta/classes/spdx-common.bbclass @@ -37,6 +37,11 @@ SPDX_CUSTOM_ANNOTATION_VARS ??= "" SPDX_MULTILIB_SSTATE_ARCHS ??= "${SSTATE_ARCHS}" +python () { + from oe.cve_check import extend_cve_status + extend_cve_status(d) +} + def create_spdx_source_deps(d): import oe.spdx_common From patchwork Fri Nov 21 09:54:13 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin ROBIN X-Patchwork-Id: 75150 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4CA21CFA76A for ; Fri, 21 Nov 2025 09:54:35 +0000 (UTC) Received: from smtpout-04.galae.net (smtpout-04.galae.net [185.171.202.116]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.8230.1763718867480578953 for ; Fri, 21 Nov 2025 01:54:28 -0800 Authentication-Results: mx.groups.io; dkim=fail reason="dkim: body hash did not verify" header.i=@bootlin.com header.s=dkim header.b=vQdROfTd; spf=pass (domain: bootlin.com, ip: 185.171.202.116, mailfrom: benjamin.robin@bootlin.com) Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-04.galae.net (Postfix) with ESMTPS id C2643C1019C; Fri, 21 Nov 2025 09:54:03 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 2A1FD60719; Fri, 21 Nov 2025 09:54:26 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id D899610371E89; Fri, 21 Nov 2025 10:54:24 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1763718865; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=L8UW3c1R008XEdue4HpXTQbvoycIwuhixU+JiLGQn0w=; b=vQdROfTd7jUrlgJr3fFL5yvupKP72hwBfnoxaAd41Wu+WR0QdrWE/vaD1/Dc6jNBoNS7Bt ZOQbJUhf+HHT/C9rai5CffwqhkvFYufVQSo0/ACeTI9OhFLmJSnjwqb4Bgq0zB6PsB5s9K ko5hFPOV+lOw8FKySrR4kIeezSANImW1FQnK8E2N5UXtdYqze8lW3xu7Fw15zs6l6RYQAc 4YjZXYLce14EVfjZ/ZX4B/fEETv6+D5R9nwn7sLnlKO8BfnAZLHs7I1iVGWZLqxPqkYq01 3r71zlW1ZzkkFTdR4Q0aF1Q3AVm29OaPrliR3wqkrqabc3+5QyhyFvYQM8sSXw== From: "Benjamin Robin (Schneider Electric)" To: openembedded-core@lists.openembedded.org Cc: "Benjamin Robin (Schneider Electric)" , thomas.petazzoni@bootlin.com, mathieu.dubois-briand@bootlin.com, miquel.raynal@bootlin.com, antonin.godard@bootlin.com, kamel.bouhara@bootlin.com, pascal.eberhard@se.com, jpewhacker@gmail.com, Peter Marko , Richard Purdie Subject: [scarthgap v2 5/5] vex: fix rootfs manifest Date: Fri, 21 Nov 2025 10:54:13 +0100 Message-ID: <20251121095415.288301-6-benjamin.robin@bootlin.com> X-Mailer: git-send-email 2.51.2 In-Reply-To: <20251121095415.288301-1-benjamin.robin@bootlin.com> References: <20251121095415.288301-1-benjamin.robin@bootlin.com> MIME-Version: 1.0 X-Last-TLS-Session-Version: TLSv1.3 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Fri, 21 Nov 2025 09:54:35 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/226662 Rootfs VEX file is created by gathering files from CVE_CHECK_DIR (deploy directory), however recipes generate the files only in CVE_CHECK_DIR (log directory). This make the rootfs VEX be always empty without any message. The code is copied from cve_check class, which writes to both, so let keep them aligned and make also vex write both files. Also add a warning for case that a cve file would be still missing. Signed-off-by: Peter Marko Signed-off-by: Mathieu Dubois-Briand Signed-off-by: Richard Purdie (cherry picked from commit ee6541d0940c65685aaafd7d41a59a9406392e7d) Signed-off-by: Benjamin Robin (Schneider Electric) --- meta/classes/vex.bbclass | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/meta/classes/vex.bbclass b/meta/classes/vex.bbclass index c447b37db89b..707e6f45a19a 100644 --- a/meta/classes/vex.bbclass +++ b/meta/classes/vex.bbclass @@ -213,6 +213,8 @@ python vex_write_rootfs_manifest () { with open(pkgfilepath) as j: data = json.load(j) cve_check_merge_jsons(json_data, data) + else: + bb.warn("Missing cve file for %s" % pkg) d.setVar("PN", save_pn) @@ -306,9 +308,12 @@ def cve_write_data_json(d, cve_data, cve_status): cvelogpath = d.getVar("CVE_CHECK_SUMMARY_DIR") index_path = d.getVar("CVE_CHECK_SUMMARY_INDEX_PATH") bb.utils.mkdirhier(cvelogpath) + bb.utils.mkdirhier(os.path.dirname(deploy_file)) fragment_file = os.path.basename(deploy_file) fragment_path = os.path.join(cvelogpath, fragment_file) with open(fragment_path, "w") as f: f.write(write_string) + with open(deploy_file, "w") as f: + f.write(write_string) with open(index_path, "a+") as f: f.write("%s\n" % fragment_path)