From patchwork Tue Mar 31 13:24:27 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ross Burton X-Patchwork-Id: 84911 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 056D1109B469 for ; Tue, 31 Mar 2026 13:24:42 +0000 (UTC) Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.19900.1774963475255890606 for ; Tue, 31 Mar 2026 06:24:35 -0700 Authentication-Results: mx.groups.io; dkim=fail reason="dkim: body hash did not verify" header.i=@arm.com header.s=foss header.b=DxdBB+zF; spf=pass (domain: arm.com, ip: 217.140.110.172, mailfrom: ross.burton@arm.com) Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id E1243495B for ; Tue, 31 Mar 2026 06:24:28 -0700 (PDT) Received: from cesw-amp-gbt-1s-m12830-04.lab.cambridge.arm.com (usa-sjc-imap-foss1.foss.arm.com [10.121.207.14]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 770183F7D8 for ; Tue, 31 Mar 2026 06:24:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1774963474; bh=2AnxErEhFWWEAFlBGg1A9GSsgLBXY1FXGII8twvH8R0=; h=From:To:Subject:Date:From; b=DxdBB+zFRIHHB1Mw+pgmXvviVBEAp/S0hjyZ6xtmYHDG9u3msTGFRwXJAl235K/I0 9icmCzKREufj5pc3Hbh6IXcL6akBENXBzGjOh4PceFaOj9hUEIoFUX/ud16ZPoVxnw yiq0yAU9qROYyM2JeW4FotFNHfzHco1bk2Rl3BWk= From: Ross Burton To: openembedded-core@lists.openembedded.org Subject: [PATCH 1/3] classes/vex: remove Date: Tue, 31 Mar 2026 14:24:27 +0100 Message-ID: <20260331132430.781647-1-ross.burton@arm.com> X-Mailer: git-send-email 2.43.0 MIME-Version: 1.0 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 ; Tue, 31 Mar 2026 13:24:42 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/234285 This class existed as a provider of information for external CVE tooling, and uses a non-standard format that is OpenEmbedded-specific[1]. However, the SPDX 3 output can contain all of this needed information, in a format that is standardised. I'm unaware of any active users of this class beyond sbom-cve-check, which can also read the data from the SPDX if SPDX_INCLUDE_VEX has been set. So that we don't have to maintain this class for the lifetime of the Wrynose LTS, delete it. [1] oe-core 6352ad93a72 ("vex.bbclass: add a new class") Signed-off-by: Ross Burton --- meta/classes/vex.bbclass | 303 --------------------------------------- 1 file changed, 303 deletions(-) delete mode 100644 meta/classes/vex.bbclass diff --git a/meta/classes/vex.bbclass b/meta/classes/vex.bbclass deleted file mode 100644 index c57b8209c23..00000000000 --- a/meta/classes/vex.bbclass +++ /dev/null @@ -1,303 +0,0 @@ -# -# 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}.vex.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.") - - from oe.cve_check import extend_cve_status - extend_cve_status(d) -} - -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 - - try: - patched_cves = get_patched_cves(d) - cves_status = [] - products = d.getVar("CVE_PRODUCT").split() - for product in products: - if ":" in product: - _, product = product.split(":", 1) - cves_status.append([product, False]) - - except FileNotFoundError: - bb.fatal("Failure in searching patches") - - cve_write_data_json(d, patched_cves, 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) - else: - bb.warn("Missing cve file for %s" % pkg) - - d.setVar("PN", save_pn) - - link_path = os.path.join(deploy_dir, "%s.vex.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["scorev4"] = cve_data[cve]["NVD-scorev4"] - 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) - 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)