public inbox for openembedded-core@lists.openembedded.org
 help / color / mirror / Atom feed
* [OE-core] [scarthgap] [PATCH v1 0/4] cve-check: fix incorrect CVE assessments and runtime warnings - cover letter
@ 2026-03-18  5:39 Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
  2026-03-18  5:39 ` [OE-core] [scarthgap] [PATCH v1 1/4] cve-check: encode affected product/vendor in CVE_STATUS Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
                   ` (4 more replies)
  0 siblings, 5 replies; 13+ messages in thread
From: Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-03-18  5:39 UTC (permalink / raw)
  To: openembedded-core; +Cc: xe-linux-external, vchavda

From: Het Patel <hetpat@cisco.com>

The patches address the following bugs:

1. Incomplete CVE Assessment Details: Currently, the `detail` field is missing for approximately 81% of entries, rendering reports unreliable for auditing. These changes ensure that the rationale for a "Patched" or "Unpatched" assessment is properly recorded, allowing for a clear distinction between version-based assessments and missing data.

2. Runtime Warnings: Corrects four instances where debug calls were missing the required log level parameter. This change eliminates the runtime warnings that currently trigger during every CVE scan.

Testing:
- Applied cleanly to the current `scarthgap` HEAD.
- Verified via a full CVE scan.
- Confirmed that all existing CVE statuses are preserved with no regressions observed.

Het Patel (4):
  cve-check: encode affected product/vendor in CVE_STATUS
  cve-check: annotate CVEs during analysis
  cve-check-map: add new statuses
  cve-check: fix debug message

 meta/classes/cve-check.bbclass | 246 +++++++++++++++++++++--------------------
 meta/conf/cve-check-map.conf   |   9 +
 meta/lib/oe/cve_check.py       |  74 +++++++++---
 3 files changed, 197 insertions(+), 132 deletions(-)


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [OE-core] [scarthgap] [PATCH v1 1/4] cve-check: encode affected product/vendor in CVE_STATUS
  2026-03-18  5:39 [OE-core] [scarthgap] [PATCH v1 0/4] cve-check: fix incorrect CVE assessments and runtime warnings - cover letter Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
@ 2026-03-18  5:39 ` Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
  2026-03-18  7:31   ` Marko, Peter (FT D EU SK BFS1)
  2026-03-18  5:39 ` [OE-core] [scarthgap] [PATCH v1 2/4] cve-check: annotate CVEs during analysis Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 13+ messages in thread
From: Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-03-18  5:39 UTC (permalink / raw)
  To: openembedded-core; +Cc: xe-linux-external, vchavda

From: Marta Rybczynska <rybczynska@gmail.com>

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.

Backport Changes:
- Discarded the changes to meta/lib/oe/spdx30_tasks.py, as the
commit history for this file diverges from the base commit
itself (9c9b9545049a in the scarthgap branch).
- Additionally, the changes do not introduce any major features
and are primarily focused on code restructuring.

Signed-off-by: Marta Rybczynska <marta.rybczynska@syslinbit.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
(cherry picked from commit abca80a716e92fc18d3085aba1a15f4bac72379c)
Signed-off-by: Het Patel <hetpat@cisco.com>
---
 meta/classes/cve-check.bbclass | 24 ++++++++++-----------
 meta/lib/oe/cve_check.py       | 39 ++++++++++++++++++++++++++--------
 2 files changed, 42 insertions(+), 21 deletions(-)

diff --git a/meta/classes/cve-check.bbclass b/meta/classes/cve-check.bbclass
index 3f4704fb4e..de5ddf6f04 100644
--- a/meta/classes/cve-check.bbclass
+++ b/meta/classes/cve-check.bbclass
@@ -316,8 +316,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
@@ -500,11 +500,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"]
@@ -632,11 +632,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
diff --git a/meta/lib/oe/cve_check.py b/meta/lib/oe/cve_check.py
index 7c09b78242..767d1a6750 100644
--- a/meta/lib/oe/cve_check.py
+++ b/meta/lib/oe/cve_check.py
@@ -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,22 +227,43 @@ 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
 
 def extend_cve_status(d):
     # do this only once in case multiple classes use this


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [OE-core] [scarthgap] [PATCH v1 2/4] cve-check: annotate CVEs during analysis
  2026-03-18  5:39 [OE-core] [scarthgap] [PATCH v1 0/4] cve-check: fix incorrect CVE assessments and runtime warnings - cover letter Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
  2026-03-18  5:39 ` [OE-core] [scarthgap] [PATCH v1 1/4] cve-check: encode affected product/vendor in CVE_STATUS Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
@ 2026-03-18  5:39 ` Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
  2026-03-18  7:38   ` Marko, Peter (FT D EU SK BFS1)
  2026-03-18  5:39 ` [OE-core] [scarthgap] [PATCH v1 3/4] cve-check-map: add new statuses Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 13+ messages in thread
From: Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-03-18  5:39 UTC (permalink / raw)
  To: openembedded-core; +Cc: xe-linux-external, vchavda

From: Marta Rybczynska <rybczynska@gmail.com>

Add status information for each CVE under analysis.

Previously the information passed between different function of the
cve-check class included only tables of patched, unpatched, ignored
vulnerabilities and the general status of the recipe.

The VEX work requires more information, and we need to pass them
between different functions, so that it can be enriched as the
analysis progresses. Instead of multiple tables, use a single one
with annotations for each CVE encountered. For example, a patched
CVE will have:

{"abbrev-status": "Patched", "status": "version-not-in-range"}

abbrev-status contains the general status (Patched, Unpatched,
Ignored and Unknown that will be added in the VEX code)
status contains more detailed information that can come from
CVE_STATUS and the analysis.

Additional fields of the annotation include for example the name
of the patch file fixing a given CVE.

We also use the annotation in CVE_STATUS to filter out entries
that do not apply to the given recipe

Backport Changes:
- Cherry-picking this patch, which precedes commit [358dbfcd80ae] in
master. Since commit [358dbfcd80ae] was already cherry-picked earlier
in scarthgap, adjusted the changes accordingly to avoid conflicts.

Signed-off-by: Marta Rybczynska <marta.rybczynska@syslinbit.com>
Signed-off-by: Samantha Jalabert <samantha.jalabert@syslinbit.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
(cherry picked from commit 452e605b55ad61c08f4af7089a5a9c576ca28f7d)
Signed-off-by: Het Patel <hetpat@cisco.com>
---
 meta/classes/cve-check.bbclass | 214 +++++++++++++++++----------------
 meta/lib/oe/cve_check.py       |  35 +++++-
 2 files changed, 142 insertions(+), 107 deletions(-)

diff --git a/meta/classes/cve-check.bbclass b/meta/classes/cve-check.bbclass
index de5ddf6f04..32fb9e8a5c 100644
--- a/meta/classes/cve-check.bbclass
+++ b/meta/classes/cve-check.bbclass
@@ -176,10 +176,10 @@ python do_cve_check () {
                 patched_cves = get_patched_cves(d)
             except FileNotFoundError:
                 bb.fatal("Failure in searching patches")
-            ignored, patched, unpatched, status = check_cves(d, patched_cves)
-            if patched or unpatched or (d.getVar("CVE_CHECK_COVERAGE") == "1" and status):
-                cve_data = get_cve_info(d, patched + unpatched + ignored)
-                cve_write_data(d, patched, unpatched, ignored, cve_data, status)
+            cve_data, status = check_cves(d, patched_cves)
+            if len(cve_data) or (d.getVar("CVE_CHECK_COVERAGE") == "1" and status):
+                get_cve_info(d, cve_data)
+                cve_write_data(d, cve_data, status)
         else:
             bb.note("No CVE database found, skipping CVE check")
 
@@ -287,7 +287,51 @@ ROOTFS_POSTPROCESS_COMMAND:prepend = "${@'cve_check_write_rootfs_manifest ' if d
 do_rootfs[recrdeptask] += "${@'do_cve_check' if d.getVar('CVE_CHECK_CREATE_MANIFEST') == '1' else ''}"
 do_populate_sdk[recrdeptask] += "${@'do_cve_check' if d.getVar('CVE_CHECK_CREATE_MANIFEST') == '1' else ''}"
 
-def check_cves(d, patched_cves):
+def cve_is_ignored(d, cve_data, cve):
+    if cve not in cve_data:
+        return False
+    if cve_data[cve]['abbrev-status'] == "Ignored":
+        return True
+    return False
+
+def cve_is_patched(d, cve_data, cve):
+    if cve not in cve_data:
+        return False
+    if cve_data[cve]['abbrev-status'] == "Patched":
+        return True
+    return False
+
+def cve_update(d, cve_data, cve, entry):
+    # If no entry, just add it
+    if cve not in cve_data:
+        cve_data[cve] = entry
+        return
+    # If we are updating, there might be change in the status
+    bb.debug("Trying CVE entry update for %s from %s to %s" % (cve, cve_data[cve]['abbrev-status'], entry['abbrev-status']))
+    if cve_data[cve]['abbrev-status'] == "Unknown":
+        cve_data[cve] = entry
+        return
+    if cve_data[cve]['abbrev-status'] == entry['abbrev-status']:
+        return
+    # Update like in {'abbrev-status': 'Patched', 'status': 'version-not-in-range'} to {'abbrev-status': 'Unpatched', 'status': 'version-in-range'}
+    if entry['abbrev-status'] == "Unpatched" and cve_data[cve]['abbrev-status'] == "Patched":
+        if entry['status'] == "version-in-range" and cve_data[cve]['status'] == "version-not-in-range":
+            # New result from the scan, vulnerable
+            cve_data[cve] = entry
+            bb.debug("CVE entry %s update from Patched to Unpatched from the scan result" % cve)
+            return
+    if entry['abbrev-status'] == "Patched" and cve_data[cve]['abbrev-status'] == "Unpatched":
+        if entry['status'] == "version-not-in-range" and cve_data[cve]['status'] == "version-in-range":
+            # Range does not match the scan, but we already have a vulnerable match, ignore
+            bb.debug("CVE entry %s update from Patched to Unpatched from the scan result - not applying" % cve)
+            return
+    # If we have an "Ignored", it has a priority
+    if cve_data[cve]['abbrev-status'] == "Ignored":
+        bb.debug("CVE %s not updating because Ignored" % cve)
+        return
+    bb.warn("Unhandled CVE entry update for %s from %s to %s" % (cve, cve_data[cve], entry))
+
+def check_cves(d, cve_data):
     """
     Connect to the NVD database and find unpatched cves.
     """
@@ -297,28 +341,19 @@ def check_cves(d, patched_cves):
     real_pv = d.getVar("PV")
     suffix = d.getVar("CVE_VERSION_SUFFIX")
 
-    cves_unpatched = []
-    cves_ignored = []
     cves_status = []
     cves_in_recipe = False
     # CVE_PRODUCT can contain more than one product (eg. curl/libcurl)
     products = d.getVar("CVE_PRODUCT").split()
     # If this has been unset then we're not scanning for CVEs here (for example, image recipes)
     if not products:
-        return ([], [], [], [])
+        return ([], [])
     pv = d.getVar("CVE_VERSION").split("+git")[0]
 
     # If the recipe has been skipped/ignored we return empty lists
     if pn in d.getVar("CVE_CHECK_SKIP_RECIPE").split():
         bb.note("Recipe has been skipped by cve-check")
-        return ([], [], [], [])
-
-    # 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 'mapping' in decoded_status and decoded_status['mapping'] == "Ignored":
-            cve_ignore.append(cve)
+        return ([], [])
 
     import sqlite3
     db_file = d.expand("file:${CVE_CHECK_DB_FILE}?mode=ro")
@@ -337,11 +372,10 @@ def check_cves(d, patched_cves):
         for cverow in cve_cursor:
             cve = cverow[0]
 
-            if cve in cve_ignore:
+            if cve_is_ignored(d, cve_data, cve):
                 bb.note("%s-%s ignores %s" % (product, pv, cve))
-                cves_ignored.append(cve)
                 continue
-            elif cve in patched_cves:
+            elif cve_is_patched(d, cve_data, cve):
                 bb.note("%s has been patched" % (cve))
                 continue
             # Write status once only for each product
@@ -357,7 +391,7 @@ def check_cves(d, patched_cves):
             for row in product_cursor:
                 (_, _, _, version_start, operator_start, version_end, operator_end) = row
                 #bb.debug(2, "Evaluating row " + str(row))
-                if cve in cve_ignore:
+                if cve_is_ignored(d, cve_data, cve):
                     ignored = True
 
                 version_start = convert_cve_version(version_start)
@@ -396,16 +430,16 @@ def check_cves(d, patched_cves):
                 if vulnerable:
                     if ignored:
                         bb.note("%s is ignored in %s-%s" % (cve, pn, real_pv))
-                        cves_ignored.append(cve)
+                        cve_update(d, cve_data, cve, {"abbrev-status": "Ignored"})
                     else:
                         bb.note("%s-%s is vulnerable to %s" % (pn, real_pv, cve))
-                        cves_unpatched.append(cve)
+                        cve_update(d, cve_data, cve, {"abbrev-status": "Unpatched", "status": "version-in-range"})
                     break
             product_cursor.close()
 
             if not vulnerable:
                 bb.note("%s-%s is not vulnerable to %s" % (pn, real_pv, cve))
-                patched_cves.add(cve)
+                cve_update(d, cve_data, cve, {"abbrev-status": "Patched", "status": "version-not-in-range"})
         cve_cursor.close()
 
         if not cves_in_product:
@@ -413,49 +447,46 @@ def check_cves(d, patched_cves):
             cves_status.append([product, False])
 
     conn.close()
-    diff_ignore = list(set(cve_ignore) - set(cves_ignored))
-    if diff_ignore:
-        oe.qa.handle_error("cve_status_not_in_db", "Found CVE (%s) with CVE_STATUS set that are not found in database for this component" % " ".join(diff_ignore), d)
 
     if not cves_in_recipe:
         bb.note("No CVE records for products in recipe %s" % (pn))
 
-    return (list(cves_ignored), list(patched_cves), cves_unpatched, cves_status)
+    return (cve_data, cves_status)
 
-def get_cve_info(d, cves):
+def get_cve_info(d, cve_data):
     """
     Get CVE information from the database.
     """
 
     import sqlite3
 
-    cve_data = {}
     db_file = d.expand("file:${CVE_CHECK_DB_FILE}?mode=ro")
     conn = sqlite3.connect(db_file, uri=True)
 
-    for cve in cves:
+    for cve in cve_data:
         cursor = conn.execute("SELECT * FROM NVD WHERE ID IS ?", (cve,))
         for row in cursor:
-            cve_data[row[0]] = {}
-            cve_data[row[0]]["summary"] = row[1]
-            cve_data[row[0]]["scorev2"] = row[2]
-            cve_data[row[0]]["scorev3"] = row[3]
-            cve_data[row[0]]["scorev4"] = row[4]
-            cve_data[row[0]]["modified"] = row[5]
-            cve_data[row[0]]["vector"] = row[6]
-            cve_data[row[0]]["vectorString"] = row[7]
+            # The CVE itdelf has been added already
+            if row[0] not in cve_data:
+                bb.note("CVE record %s not present" % row[0])
+                continue
+            #cve_data[row[0]] = {}
+            cve_data[row[0]]["NVD-summary"] = row[1]
+            cve_data[row[0]]["NVD-scorev2"] = row[2]
+            cve_data[row[0]]["NVD-scorev3"] = row[3]
+            cve_data[row[0]]["NVD-scorev4"] = row[4]
+            cve_data[row[0]]["NVD-modified"] = row[5]
+            cve_data[row[0]]["NVD-vector"] = row[6]
+            cve_data[row[0]]["NVD-vectorString"] = row[7]
         cursor.close()
     conn.close()
-    return cve_data
 
-def cve_write_data_text(d, patched, unpatched, ignored, cve_data):
+def cve_write_data_text(d, cve_data):
     """
     Write CVE information in WORKDIR; and to CVE_CHECK_DIR, and
     CVE manifest if enabled.
     """
 
-    from oe.cve_check import decode_cve_status
-
     cve_file = d.getVar("CVE_CHECK_LOG")
     fdir_name  = d.getVar("FILE_DIRNAME")
     layer = fdir_name.split("/")[-3]
@@ -472,7 +503,7 @@ def cve_write_data_text(d, patched, unpatched, ignored, cve_data):
         return
 
     # Early exit, the text format does not report packages without CVEs
-    if not patched+unpatched+ignored:
+    if not len(cve_data):
         return
 
     nvd_link = "https://nvd.nist.gov/vuln/detail/"
@@ -481,37 +512,30 @@ def cve_write_data_text(d, patched, unpatched, ignored, cve_data):
     bb.utils.mkdirhier(os.path.dirname(cve_file))
 
     for cve in sorted(cve_data):
-        is_patched = cve in patched
-        is_ignored = cve in ignored
-
-        status = "Unpatched"
-        if (is_patched or is_ignored) and not report_all:
+        if not report_all and (cve_data[cve]["abbrev-status"] == "Patched" or cve_data[cve]["abbrev-status"] == "Ignored"):
             continue
-        if is_ignored:
-            status = "Ignored"
-        elif is_patched:
-            status = "Patched"
-        else:
-            # default value of status is Unpatched
-            unpatched_cves.append(cve)
-
         write_string += "LAYER: %s\n" % layer
         write_string += "PACKAGE NAME: %s\n" % d.getVar("PN")
         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
-        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"]
-        write_string += "CVSS v4 BASE SCORE: %s\n" % cve_data[cve]["scorev4"]
-        write_string += "VECTOR: %s\n" % cve_data[cve]["vector"]
-        write_string += "VECTORSTRING: %s\n" % cve_data[cve]["vectorString"]
+        write_string += "CVE STATUS: %s\n" % cve_data[cve]["abbrev-status"]
+
+        if 'status' in cve_data[cve]:
+            write_string += "CVE DETAIL: %s\n" % cve_data[cve]["status"]
+        if 'justification' in cve_data[cve]:
+            write_string += "CVE DESCRIPTION: %s\n" % cve_data[cve]["justification"]
+
+        if "NVD-summary" in cve_data[cve]:
+            write_string += "CVE SUMMARY: %s\n" % cve_data[cve]["NVD-summary"]
+            write_string += "CVSS v2 BASE SCORE: %s\n" % cve_data[cve]["NVD-scorev2"]
+            write_string += "CVSS v3 BASE SCORE: %s\n" % cve_data[cve]["NVD-scorev3"]
+            write_string += "CVSS v3 BASE SCORE: %s\n" % cve_data[cve]["NVD-scorev4"]
+            write_string += "VECTOR: %s\n" % cve_data[cve]["NVD-vector"]
+            write_string += "VECTORSTRING: %s\n" % cve_data[cve]["NVD-vectorString"]
+
         write_string += "MORE INFORMATION: %s%s\n\n" % (nvd_link, cve)
+        if cve_data[cve]["abbrev-status"] == "Unpatched":
+            unpatched_cves.append(cve)
 
     if unpatched_cves and d.getVar("CVE_CHECK_SHOW_WARNINGS") == "1":
         bb.warn("Found unpatched CVE (%s), for more information check %s" % (" ".join(unpatched_cves),cve_file))
@@ -563,13 +587,11 @@ def cve_check_write_json_output(d, output, direct_file, deploy_file, manifest_fi
         with open(index_path, "a+") as f:
             f.write("%s\n" % fragment_path)
 
-def cve_write_data_json(d, patched, unpatched, ignored, cve_data, cve_status):
+def cve_write_data_json(d, cve_data, cve_status):
     """
     Prepare CVE data for the JSON format, then write it.
     """
 
-    from oe.cve_check import decode_cve_status
-
     output = {"version":"1", "package": []}
     nvd_link = "https://nvd.nist.gov/vuln/detail/"
 
@@ -587,8 +609,6 @@ def cve_write_data_json(d, patched, unpatched, ignored, cve_data, cve_status):
     if include_layers and layer not in include_layers:
         return
 
-    unpatched_cves = []
-
     product_data = []
     for s in cve_status:
         p = {"product": s[0], "cvesInRecord": "Yes"}
@@ -603,40 +623,32 @@ def cve_write_data_json(d, patched, unpatched, ignored, cve_data, cve_status):
         "version" : package_version,
         "products": product_data
     }
+
     cve_list = []
 
     for cve in sorted(cve_data):
-        is_patched = cve in patched
-        is_ignored = cve in ignored
-        status = "Unpatched"
-        if (is_patched or is_ignored) and not report_all:
+        if not report_all and (cve_data[cve]["abbrev-status"] == "Patched" or cve_data[cve]["abbrev-status"] == "Ignored"):
             continue
-        if is_ignored:
-            status = "Ignored"
-        elif is_patched:
-            status = "Patched"
-        else:
-            # default value of status is Unpatched
-            unpatched_cves.append(cve)
-
         issue_link = "%s%s" % (nvd_link, cve)
 
         cve_item = {
             "id" : cve,
-            "summary" : cve_data[cve]["summary"],
-            "scorev2" : cve_data[cve]["scorev2"],
-            "scorev3" : cve_data[cve]["scorev3"],
-            "scorev4" : cve_data[cve]["scorev4"],
-            "vector" : cve_data[cve]["vector"],
-            "vectorString" : cve_data[cve]["vectorString"],
-            "status" : status,
-            "link": issue_link
+            "status" : cve_data[cve]["abbrev-status"],
+            "link": issue_link,
         }
-        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']
+        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
@@ -648,12 +660,12 @@ def cve_write_data_json(d, patched, unpatched, ignored, cve_data, cve_status):
 
     cve_check_write_json_output(d, output, direct_file, deploy_file, manifest_file)
 
-def cve_write_data(d, patched, unpatched, ignored, cve_data, status):
+def cve_write_data(d, cve_data, status):
     """
     Write CVE data in each enabled format.
     """
 
     if d.getVar("CVE_CHECK_FORMAT_TEXT") == "1":
-        cve_write_data_text(d, patched, unpatched, ignored, cve_data)
+        cve_write_data_text(d, cve_data)
     if d.getVar("CVE_CHECK_FORMAT_JSON") == "1":
-        cve_write_data_json(d, patched, unpatched, ignored, cve_data, status)
+        cve_write_data_json(d, cve_data, status)
diff --git a/meta/lib/oe/cve_check.py b/meta/lib/oe/cve_check.py
index 767d1a6750..37230b7957 100644
--- a/meta/lib/oe/cve_check.py
+++ b/meta/lib/oe/cve_check.py
@@ -88,7 +88,7 @@ def get_patched_cves(d):
     # (cve_match regular expression)
     cve_file_name_match = re.compile(r".*(CVE-\d{4}-\d+)", re.IGNORECASE)
 
-    patched_cves = set()
+    patched_cves = {}
     patches = oe.patch.src_patches(d)
     bb.debug(2, "Scanning %d patches for CVEs" % len(patches))
     for url in patches:
@@ -98,7 +98,7 @@ def get_patched_cves(d):
         fname_match = cve_file_name_match.search(patch_file)
         if fname_match:
             cve = fname_match.group(1).upper()
-            patched_cves.add(cve)
+            patched_cves[cve] = {"abbrev-status": "Patched", "status": "fix-file-included", "resource": patch_file}
             bb.debug(2, "Found %s from patch file name %s" % (cve, patch_file))
 
         # Remote patches won't be present and compressed patches won't be
@@ -124,7 +124,7 @@ def get_patched_cves(d):
             cves = patch_text[match.start()+5:match.end()]
             for cve in cves.split():
                 bb.debug(2, "Patch %s solves %s" % (patch_file, cve))
-                patched_cves.add(cve)
+                patched_cves[cve] = {"abbrev-status": "Patched", "status": "fix-file-included", "resource": patch_file}
                 text_match = True
 
         if not fname_match and not text_match:
@@ -133,9 +133,15 @@ 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 'mapping' in decoded_status and decoded_status['mapping'] == "Patched":
-            bb.debug(2, "CVE %s is additionally patched" % cve)
-            patched_cves.add(cve)
+        products = d.getVar("CVE_PRODUCT")
+        if has_cve_product_match(decoded_status, products) == True:
+            patched_cves[cve] = {
+                "abbrev-status": decoded_status["mapping"],
+                "status": decoded_status["detail"],
+                "justification": decoded_status["description"],
+                "affected-vendor": decoded_status["vendor"],
+                "affected-product": decoded_status["product"]
+            }
 
     return patched_cves
 
@@ -286,3 +292,20 @@ def extend_cve_status(d):
                 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 has_cve_product_match(detailed_status, products):
+    """
+    Check product/vendor match between detailed_status from decode_cve_status and a string of
+    products (like from CVE_PRODUCT)
+    """
+    for product in products.split():
+        vendor = "*"
+        if ":" in product:
+            vendor, product = product.split(":", 1)
+
+        if (vendor == detailed_status["vendor"] or detailed_status["vendor"] == "*") and \
+            (product == detailed_status["product"] or detailed_status["product"] == "*"):
+            return True
+
+    #if no match, return False
+    return False


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [OE-core] [scarthgap] [PATCH v1 3/4] cve-check-map: add new statuses
  2026-03-18  5:39 [OE-core] [scarthgap] [PATCH v1 0/4] cve-check: fix incorrect CVE assessments and runtime warnings - cover letter Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
  2026-03-18  5:39 ` [OE-core] [scarthgap] [PATCH v1 1/4] cve-check: encode affected product/vendor in CVE_STATUS Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
  2026-03-18  5:39 ` [OE-core] [scarthgap] [PATCH v1 2/4] cve-check: annotate CVEs during analysis Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
@ 2026-03-18  5:39 ` Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
  2026-03-18  5:39 ` [OE-core] [scarthgap] [PATCH v1 4/4] cve-check: fix debug message Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
  2026-03-18 11:07 ` [OE-core] [scarthgap] [PATCH v1 0/4] cve-check: fix incorrect CVE assessments and runtime warnings - cover letter Yoann Congal
  4 siblings, 0 replies; 13+ messages in thread
From: Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-03-18  5:39 UTC (permalink / raw)
  To: openembedded-core; +Cc: xe-linux-external, vchavda

From: Marta Rybczynska <rybczynska@gmail.com>

Add 'fix-file-included', 'version-not-in-range' and 'version-in-range' generated
by the cve-check.

'fix-file-included' means that a fix file for the CVE has been located.

'version-not-in-range' means that the product version has been found outside of
the vulnerable range.

'version-in-range' means that the product version has been found inside of the
vulnerable range.

Signed-off-by: Marta Rybczynska <marta.rybczynska@syslinbit.com>
Signed-off-by: Samantha Jalabert <samantha.jalabert@syslinbit.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
(cherry picked from commit d25f1817752bc8a84c40dcbef75f7559801ce15e)
Signed-off-by: Het Patel <hetpat@cisco.com>
---
 meta/conf/cve-check-map.conf | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/meta/conf/cve-check-map.conf b/meta/conf/cve-check-map.conf
index 17b0f15571..ac956379d1 100644
--- a/meta/conf/cve-check-map.conf
+++ b/meta/conf/cve-check-map.conf
@@ -8,11 +8,17 @@ CVE_CHECK_STATUSMAP[backported-patch] = "Patched"
 CVE_CHECK_STATUSMAP[cpe-stable-backport] = "Patched"
 # use when NVD DB does not mention correct version or does not mention any verion at all
 CVE_CHECK_STATUSMAP[fixed-version] = "Patched"
+# use when a fix file has been included (set automatically)
+CVE_CHECK_STATUSMAP[fix-file-included] = "Patched"
+# do not use directly: automatic scan reports version number NOT in the vulnerable range (set automatically)
+CVE_CHECK_STATUSMAP[version-not-in-range] = "Patched"
 
 # used internally by this class if CVE vulnerability is detected which is not marked as fixed or ignored
 CVE_CHECK_STATUSMAP[unpatched] = "Unpatched"
 # use when CVE is confirmed by upstream but fix is still not available
 CVE_CHECK_STATUSMAP[vulnerable-investigating] = "Unpatched"
+# do not use directly: automatic scan reports version number IS in the vulnerable range (set automatically)
+CVE_CHECK_STATUSMAP[version-in-range] = "Unpatched"
 
 # used for migration from old concept, do not use for new vulnerabilities
 CVE_CHECK_STATUSMAP[ignored] = "Ignored"
@@ -26,3 +32,6 @@ CVE_CHECK_STATUSMAP[not-applicable-config] = "Ignored"
 CVE_CHECK_STATUSMAP[not-applicable-platform] = "Ignored"
 # use when upstream acknowledged the vulnerability but does not plan to fix it
 CVE_CHECK_STATUSMAP[upstream-wontfix] = "Ignored"
+
+# use when it is impossible to conclude if the vulnerability is present or not
+CVE_CHECK_STATUSMAP[unknown] = "Unknown"


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [OE-core] [scarthgap] [PATCH v1 4/4] cve-check: fix debug message
  2026-03-18  5:39 [OE-core] [scarthgap] [PATCH v1 0/4] cve-check: fix incorrect CVE assessments and runtime warnings - cover letter Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
                   ` (2 preceding siblings ...)
  2026-03-18  5:39 ` [OE-core] [scarthgap] [PATCH v1 3/4] cve-check-map: add new statuses Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
@ 2026-03-18  5:39 ` Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
  2026-03-18 11:07 ` [OE-core] [scarthgap] [PATCH v1 0/4] cve-check: fix incorrect CVE assessments and runtime warnings - cover letter Yoann Congal
  4 siblings, 0 replies; 13+ messages in thread
From: Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-03-18  5:39 UTC (permalink / raw)
  To: openembedded-core; +Cc: xe-linux-external, vchavda

From: Daniel Turull <daniel.turull@ericsson.com>

Debug level was not added as a parameter, causing a warning.

Signed-off-by: Daniel Turull <daniel.turull@ericsson.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
(cherry picked from commit 40157fcbd9066f261812ba665ec963b2e496aa53)
Signed-off-by: Het Patel <hetpat@cisco.com>
---
 meta/classes/cve-check.bbclass | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/meta/classes/cve-check.bbclass b/meta/classes/cve-check.bbclass
index 32fb9e8a5c..d84fe2a0d3 100644
--- a/meta/classes/cve-check.bbclass
+++ b/meta/classes/cve-check.bbclass
@@ -307,7 +307,7 @@ def cve_update(d, cve_data, cve, entry):
         cve_data[cve] = entry
         return
     # If we are updating, there might be change in the status
-    bb.debug("Trying CVE entry update for %s from %s to %s" % (cve, cve_data[cve]['abbrev-status'], entry['abbrev-status']))
+    bb.debug(1, "Trying CVE entry update for %s from %s to %s" % (cve, cve_data[cve]['abbrev-status'], entry['abbrev-status']))
     if cve_data[cve]['abbrev-status'] == "Unknown":
         cve_data[cve] = entry
         return
@@ -318,16 +318,16 @@ def cve_update(d, cve_data, cve, entry):
         if entry['status'] == "version-in-range" and cve_data[cve]['status'] == "version-not-in-range":
             # New result from the scan, vulnerable
             cve_data[cve] = entry
-            bb.debug("CVE entry %s update from Patched to Unpatched from the scan result" % cve)
+            bb.debug(1, "CVE entry %s update from Patched to Unpatched from the scan result" % cve)
             return
     if entry['abbrev-status'] == "Patched" and cve_data[cve]['abbrev-status'] == "Unpatched":
         if entry['status'] == "version-not-in-range" and cve_data[cve]['status'] == "version-in-range":
             # Range does not match the scan, but we already have a vulnerable match, ignore
-            bb.debug("CVE entry %s update from Patched to Unpatched from the scan result - not applying" % cve)
+            bb.debug(1, "CVE entry %s update from Patched to Unpatched from the scan result - not applying" % cve)
             return
     # If we have an "Ignored", it has a priority
     if cve_data[cve]['abbrev-status'] == "Ignored":
-        bb.debug("CVE %s not updating because Ignored" % cve)
+        bb.debug(1, "CVE %s not updating because Ignored" % cve)
         return
     bb.warn("Unhandled CVE entry update for %s from %s to %s" % (cve, cve_data[cve], entry))
 


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* RE: [OE-core] [scarthgap] [PATCH v1 1/4] cve-check: encode affected product/vendor in CVE_STATUS
  2026-03-18  5:39 ` [OE-core] [scarthgap] [PATCH v1 1/4] cve-check: encode affected product/vendor in CVE_STATUS Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
@ 2026-03-18  7:31   ` Marko, Peter (FT D EU SK BFS1)
  2026-03-18 12:54     ` Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
  0 siblings, 1 reply; 13+ messages in thread
From: Marko, Peter (FT D EU SK BFS1) @ 2026-03-18  7:31 UTC (permalink / raw)
  To: hetpat@cisco.com, openembedded-core@lists.openembedded.org
  Cc: xe-linux-external@cisco.com, vchavda@cisco.com

This commit needs two additional fixes:
https://git.openembedded.org/openembedded-core/commit/?id=3c4d8ca41ac0b429af92bf0ea84f1dfd0cda9e1f
https://git.openembedded.org/openembedded-core/commit/?id=cc33dd9176726cb4b2d2f142ed1bc655da8e0a9f

Peter

> -----Original Message-----
> From: openembedded-core@lists.openembedded.org <openembedded-
> core@lists.openembedded.org> On Behalf Of Het Patel via
> lists.openembedded.org
> Sent: Wednesday, March 18, 2026 6:39
> To: openembedded-core@lists.openembedded.org
> Cc: xe-linux-external@cisco.com; vchavda@cisco.com
> Subject: [OE-core] [scarthgap] [PATCH v1 1/4] cve-check: encode affected
> product/vendor in CVE_STATUS
> 
> From: Marta Rybczynska <rybczynska@gmail.com>
> 
> 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.
> 
> Backport Changes:
> - Discarded the changes to meta/lib/oe/spdx30_tasks.py, as the
> commit history for this file diverges from the base commit
> itself (9c9b9545049a in the scarthgap branch).
> - Additionally, the changes do not introduce any major features
> and are primarily focused on code restructuring.
> 
> Signed-off-by: Marta Rybczynska <marta.rybczynska@syslinbit.com>
> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
> (cherry picked from commit abca80a716e92fc18d3085aba1a15f4bac72379c)
> Signed-off-by: Het Patel <hetpat@cisco.com>
> ---
>  meta/classes/cve-check.bbclass | 24 ++++++++++-----------
>  meta/lib/oe/cve_check.py       | 39 ++++++++++++++++++++++++++--------
>  2 files changed, 42 insertions(+), 21 deletions(-)
> 
> diff --git a/meta/classes/cve-check.bbclass b/meta/classes/cve-check.bbclass
> index 3f4704fb4e..de5ddf6f04 100644
> --- a/meta/classes/cve-check.bbclass
> +++ b/meta/classes/cve-check.bbclass
> @@ -316,8 +316,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
> @@ -500,11 +500,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"]
> @@ -632,11 +632,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
> diff --git a/meta/lib/oe/cve_check.py b/meta/lib/oe/cve_check.py
> index 7c09b78242..767d1a6750 100644
> --- a/meta/lib/oe/cve_check.py
> +++ b/meta/lib/oe/cve_check.py
> @@ -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,22 +227,43 @@ 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
> 
>  def extend_cve_status(d):
>      # do this only once in case multiple classes use this


^ permalink raw reply	[flat|nested] 13+ messages in thread

* RE: [OE-core] [scarthgap] [PATCH v1 2/4] cve-check: annotate CVEs during analysis
  2026-03-18  5:39 ` [OE-core] [scarthgap] [PATCH v1 2/4] cve-check: annotate CVEs during analysis Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
@ 2026-03-18  7:38   ` Marko, Peter (FT D EU SK BFS1)
  2026-03-18 12:55     ` Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
  0 siblings, 1 reply; 13+ messages in thread
From: Marko, Peter (FT D EU SK BFS1) @ 2026-03-18  7:38 UTC (permalink / raw)
  To: hetpat@cisco.com, openembedded-core@lists.openembedded.org
  Cc: xe-linux-external@cisco.com, vchavda@cisco.com, Marta Rybczynska

If I remember correctly, this commit brings significant (breaking) change how ignored CVEs are reported.
Before, only ignored CVEs matching NVD DB are reported, after, all ignored CVEs are reported.

Therefore it would also need a change in common cve includes (like https://git.openembedded.org/openembedded-core/commit/?id=e1bf43561093b3b9215cde9e9f7d80b4ffcdc64e).
And change all such files in user layers, so updating migration notes (which was unfortunately not done for new releases, however this patch is trying to do this in mid of LTS release).

Peter

> -----Original Message-----
> From: openembedded-core@lists.openembedded.org <openembedded-
> core@lists.openembedded.org> On Behalf Of Het Patel via
> lists.openembedded.org
> Sent: Wednesday, March 18, 2026 6:39
> To: openembedded-core@lists.openembedded.org
> Cc: xe-linux-external@cisco.com; vchavda@cisco.com
> Subject: [OE-core] [scarthgap] [PATCH v1 2/4] cve-check: annotate CVEs during
> analysis
> 
> From: Marta Rybczynska <rybczynska@gmail.com>
> 
> Add status information for each CVE under analysis.
> 
> Previously the information passed between different function of the
> cve-check class included only tables of patched, unpatched, ignored
> vulnerabilities and the general status of the recipe.
> 
> The VEX work requires more information, and we need to pass them
> between different functions, so that it can be enriched as the
> analysis progresses. Instead of multiple tables, use a single one
> with annotations for each CVE encountered. For example, a patched
> CVE will have:
> 
> {"abbrev-status": "Patched", "status": "version-not-in-range"}
> 
> abbrev-status contains the general status (Patched, Unpatched,
> Ignored and Unknown that will be added in the VEX code)
> status contains more detailed information that can come from
> CVE_STATUS and the analysis.
> 
> Additional fields of the annotation include for example the name
> of the patch file fixing a given CVE.
> 
> We also use the annotation in CVE_STATUS to filter out entries
> that do not apply to the given recipe
> 
> Backport Changes:
> - Cherry-picking this patch, which precedes commit [358dbfcd80ae] in
> master. Since commit [358dbfcd80ae] was already cherry-picked earlier
> in scarthgap, adjusted the changes accordingly to avoid conflicts.
> 
> Signed-off-by: Marta Rybczynska <marta.rybczynska@syslinbit.com>
> Signed-off-by: Samantha Jalabert <samantha.jalabert@syslinbit.com>
> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
> (cherry picked from commit 452e605b55ad61c08f4af7089a5a9c576ca28f7d)
> Signed-off-by: Het Patel <hetpat@cisco.com>
> ---
>  meta/classes/cve-check.bbclass | 214 +++++++++++++++++----------------
>  meta/lib/oe/cve_check.py       |  35 +++++-
>  2 files changed, 142 insertions(+), 107 deletions(-)
> 
> diff --git a/meta/classes/cve-check.bbclass b/meta/classes/cve-check.bbclass
> index de5ddf6f04..32fb9e8a5c 100644
> --- a/meta/classes/cve-check.bbclass
> +++ b/meta/classes/cve-check.bbclass
> @@ -176,10 +176,10 @@ python do_cve_check () {
>                  patched_cves = get_patched_cves(d)
>              except FileNotFoundError:
>                  bb.fatal("Failure in searching patches")
> -            ignored, patched, unpatched, status = check_cves(d, patched_cves)
> -            if patched or unpatched or (d.getVar("CVE_CHECK_COVERAGE") == "1"
> and status):
> -                cve_data = get_cve_info(d, patched + unpatched + ignored)
> -                cve_write_data(d, patched, unpatched, ignored, cve_data, status)
> +            cve_data, status = check_cves(d, patched_cves)
> +            if len(cve_data) or (d.getVar("CVE_CHECK_COVERAGE") == "1" and
> status):
> +                get_cve_info(d, cve_data)
> +                cve_write_data(d, cve_data, status)
>          else:
>              bb.note("No CVE database found, skipping CVE check")
> 
> @@ -287,7 +287,51 @@ ROOTFS_POSTPROCESS_COMMAND:prepend =
> "${@'cve_check_write_rootfs_manifest ' if d
>  do_rootfs[recrdeptask] += "${@'do_cve_check' if
> d.getVar('CVE_CHECK_CREATE_MANIFEST') == '1' else ''}"
>  do_populate_sdk[recrdeptask] += "${@'do_cve_check' if
> d.getVar('CVE_CHECK_CREATE_MANIFEST') == '1' else ''}"
> 
> -def check_cves(d, patched_cves):
> +def cve_is_ignored(d, cve_data, cve):
> +    if cve not in cve_data:
> +        return False
> +    if cve_data[cve]['abbrev-status'] == "Ignored":
> +        return True
> +    return False
> +
> +def cve_is_patched(d, cve_data, cve):
> +    if cve not in cve_data:
> +        return False
> +    if cve_data[cve]['abbrev-status'] == "Patched":
> +        return True
> +    return False
> +
> +def cve_update(d, cve_data, cve, entry):
> +    # If no entry, just add it
> +    if cve not in cve_data:
> +        cve_data[cve] = entry
> +        return
> +    # If we are updating, there might be change in the status
> +    bb.debug("Trying CVE entry update for %s from %s to %s" % (cve,
> cve_data[cve]['abbrev-status'], entry['abbrev-status']))
> +    if cve_data[cve]['abbrev-status'] == "Unknown":
> +        cve_data[cve] = entry
> +        return
> +    if cve_data[cve]['abbrev-status'] == entry['abbrev-status']:
> +        return
> +    # Update like in {'abbrev-status': 'Patched', 'status': 'version-not-in-range'} to
> {'abbrev-status': 'Unpatched', 'status': 'version-in-range'}
> +    if entry['abbrev-status'] == "Unpatched" and cve_data[cve]['abbrev-status'] ==
> "Patched":
> +        if entry['status'] == "version-in-range" and cve_data[cve]['status'] ==
> "version-not-in-range":
> +            # New result from the scan, vulnerable
> +            cve_data[cve] = entry
> +            bb.debug("CVE entry %s update from Patched to Unpatched from the
> scan result" % cve)
> +            return
> +    if entry['abbrev-status'] == "Patched" and cve_data[cve]['abbrev-status'] ==
> "Unpatched":
> +        if entry['status'] == "version-not-in-range" and cve_data[cve]['status'] ==
> "version-in-range":
> +            # Range does not match the scan, but we already have a vulnerable
> match, ignore
> +            bb.debug("CVE entry %s update from Patched to Unpatched from the
> scan result - not applying" % cve)
> +            return
> +    # If we have an "Ignored", it has a priority
> +    if cve_data[cve]['abbrev-status'] == "Ignored":
> +        bb.debug("CVE %s not updating because Ignored" % cve)
> +        return
> +    bb.warn("Unhandled CVE entry update for %s from %s to %s" % (cve,
> cve_data[cve], entry))
> +
> +def check_cves(d, cve_data):
>      """
>      Connect to the NVD database and find unpatched cves.
>      """
> @@ -297,28 +341,19 @@ def check_cves(d, patched_cves):
>      real_pv = d.getVar("PV")
>      suffix = d.getVar("CVE_VERSION_SUFFIX")
> 
> -    cves_unpatched = []
> -    cves_ignored = []
>      cves_status = []
>      cves_in_recipe = False
>      # CVE_PRODUCT can contain more than one product (eg. curl/libcurl)
>      products = d.getVar("CVE_PRODUCT").split()
>      # If this has been unset then we're not scanning for CVEs here (for example,
> image recipes)
>      if not products:
> -        return ([], [], [], [])
> +        return ([], [])
>      pv = d.getVar("CVE_VERSION").split("+git")[0]
> 
>      # If the recipe has been skipped/ignored we return empty lists
>      if pn in d.getVar("CVE_CHECK_SKIP_RECIPE").split():
>          bb.note("Recipe has been skipped by cve-check")
> -        return ([], [], [], [])
> -
> -    # 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 'mapping' in decoded_status and decoded_status['mapping'] ==
> "Ignored":
> -            cve_ignore.append(cve)
> +        return ([], [])
> 
>      import sqlite3
>      db_file = d.expand("file:${CVE_CHECK_DB_FILE}?mode=ro")
> @@ -337,11 +372,10 @@ def check_cves(d, patched_cves):
>          for cverow in cve_cursor:
>              cve = cverow[0]
> 
> -            if cve in cve_ignore:
> +            if cve_is_ignored(d, cve_data, cve):
>                  bb.note("%s-%s ignores %s" % (product, pv, cve))
> -                cves_ignored.append(cve)
>                  continue
> -            elif cve in patched_cves:
> +            elif cve_is_patched(d, cve_data, cve):
>                  bb.note("%s has been patched" % (cve))
>                  continue
>              # Write status once only for each product
> @@ -357,7 +391,7 @@ def check_cves(d, patched_cves):
>              for row in product_cursor:
>                  (_, _, _, version_start, operator_start, version_end, operator_end) = row
>                  #bb.debug(2, "Evaluating row " + str(row))
> -                if cve in cve_ignore:
> +                if cve_is_ignored(d, cve_data, cve):
>                      ignored = True
> 
>                  version_start = convert_cve_version(version_start)
> @@ -396,16 +430,16 @@ def check_cves(d, patched_cves):
>                  if vulnerable:
>                      if ignored:
>                          bb.note("%s is ignored in %s-%s" % (cve, pn, real_pv))
> -                        cves_ignored.append(cve)
> +                        cve_update(d, cve_data, cve, {"abbrev-status": "Ignored"})
>                      else:
>                          bb.note("%s-%s is vulnerable to %s" % (pn, real_pv, cve))
> -                        cves_unpatched.append(cve)
> +                        cve_update(d, cve_data, cve, {"abbrev-status": "Unpatched",
> "status": "version-in-range"})
>                      break
>              product_cursor.close()
> 
>              if not vulnerable:
>                  bb.note("%s-%s is not vulnerable to %s" % (pn, real_pv, cve))
> -                patched_cves.add(cve)
> +                cve_update(d, cve_data, cve, {"abbrev-status": "Patched", "status":
> "version-not-in-range"})
>          cve_cursor.close()
> 
>          if not cves_in_product:
> @@ -413,49 +447,46 @@ def check_cves(d, patched_cves):
>              cves_status.append([product, False])
> 
>      conn.close()
> -    diff_ignore = list(set(cve_ignore) - set(cves_ignored))
> -    if diff_ignore:
> -        oe.qa.handle_error("cve_status_not_in_db", "Found CVE (%s) with
> CVE_STATUS set that are not found in database for this component" % "
> ".join(diff_ignore), d)
> 
>      if not cves_in_recipe:
>          bb.note("No CVE records for products in recipe %s" % (pn))
> 
> -    return (list(cves_ignored), list(patched_cves), cves_unpatched, cves_status)
> +    return (cve_data, cves_status)
> 
> -def get_cve_info(d, cves):
> +def get_cve_info(d, cve_data):
>      """
>      Get CVE information from the database.
>      """
> 
>      import sqlite3
> 
> -    cve_data = {}
>      db_file = d.expand("file:${CVE_CHECK_DB_FILE}?mode=ro")
>      conn = sqlite3.connect(db_file, uri=True)
> 
> -    for cve in cves:
> +    for cve in cve_data:
>          cursor = conn.execute("SELECT * FROM NVD WHERE ID IS ?", (cve,))
>          for row in cursor:
> -            cve_data[row[0]] = {}
> -            cve_data[row[0]]["summary"] = row[1]
> -            cve_data[row[0]]["scorev2"] = row[2]
> -            cve_data[row[0]]["scorev3"] = row[3]
> -            cve_data[row[0]]["scorev4"] = row[4]
> -            cve_data[row[0]]["modified"] = row[5]
> -            cve_data[row[0]]["vector"] = row[6]
> -            cve_data[row[0]]["vectorString"] = row[7]
> +            # The CVE itdelf has been added already
> +            if row[0] not in cve_data:
> +                bb.note("CVE record %s not present" % row[0])
> +                continue
> +            #cve_data[row[0]] = {}
> +            cve_data[row[0]]["NVD-summary"] = row[1]
> +            cve_data[row[0]]["NVD-scorev2"] = row[2]
> +            cve_data[row[0]]["NVD-scorev3"] = row[3]
> +            cve_data[row[0]]["NVD-scorev4"] = row[4]
> +            cve_data[row[0]]["NVD-modified"] = row[5]
> +            cve_data[row[0]]["NVD-vector"] = row[6]
> +            cve_data[row[0]]["NVD-vectorString"] = row[7]
>          cursor.close()
>      conn.close()
> -    return cve_data
> 
> -def cve_write_data_text(d, patched, unpatched, ignored, cve_data):
> +def cve_write_data_text(d, cve_data):
>      """
>      Write CVE information in WORKDIR; and to CVE_CHECK_DIR, and
>      CVE manifest if enabled.
>      """
> 
> -    from oe.cve_check import decode_cve_status
> -
>      cve_file = d.getVar("CVE_CHECK_LOG")
>      fdir_name  = d.getVar("FILE_DIRNAME")
>      layer = fdir_name.split("/")[-3]
> @@ -472,7 +503,7 @@ def cve_write_data_text(d, patched, unpatched, ignored,
> cve_data):
>          return
> 
>      # Early exit, the text format does not report packages without CVEs
> -    if not patched+unpatched+ignored:
> +    if not len(cve_data):
>          return
> 
>      nvd_link = "https://nvd.nist.gov/vuln/detail/"
> @@ -481,37 +512,30 @@ def cve_write_data_text(d, patched, unpatched,
> ignored, cve_data):
>      bb.utils.mkdirhier(os.path.dirname(cve_file))
> 
>      for cve in sorted(cve_data):
> -        is_patched = cve in patched
> -        is_ignored = cve in ignored
> -
> -        status = "Unpatched"
> -        if (is_patched or is_ignored) and not report_all:
> +        if not report_all and (cve_data[cve]["abbrev-status"] == "Patched" or
> cve_data[cve]["abbrev-status"] == "Ignored"):
>              continue
> -        if is_ignored:
> -            status = "Ignored"
> -        elif is_patched:
> -            status = "Patched"
> -        else:
> -            # default value of status is Unpatched
> -            unpatched_cves.append(cve)
> -
>          write_string += "LAYER: %s\n" % layer
>          write_string += "PACKAGE NAME: %s\n" % d.getVar("PN")
>          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
> -        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"]
> -        write_string += "CVSS v4 BASE SCORE: %s\n" % cve_data[cve]["scorev4"]
> -        write_string += "VECTOR: %s\n" % cve_data[cve]["vector"]
> -        write_string += "VECTORSTRING: %s\n" % cve_data[cve]["vectorString"]
> +        write_string += "CVE STATUS: %s\n" % cve_data[cve]["abbrev-status"]
> +
> +        if 'status' in cve_data[cve]:
> +            write_string += "CVE DETAIL: %s\n" % cve_data[cve]["status"]
> +        if 'justification' in cve_data[cve]:
> +            write_string += "CVE DESCRIPTION: %s\n" %
> cve_data[cve]["justification"]
> +
> +        if "NVD-summary" in cve_data[cve]:
> +            write_string += "CVE SUMMARY: %s\n" % cve_data[cve]["NVD-
> summary"]
> +            write_string += "CVSS v2 BASE SCORE: %s\n" % cve_data[cve]["NVD-
> scorev2"]
> +            write_string += "CVSS v3 BASE SCORE: %s\n" % cve_data[cve]["NVD-
> scorev3"]
> +            write_string += "CVSS v3 BASE SCORE: %s\n" % cve_data[cve]["NVD-
> scorev4"]
> +            write_string += "VECTOR: %s\n" % cve_data[cve]["NVD-vector"]
> +            write_string += "VECTORSTRING: %s\n" % cve_data[cve]["NVD-
> vectorString"]
> +
>          write_string += "MORE INFORMATION: %s%s\n\n" % (nvd_link, cve)
> +        if cve_data[cve]["abbrev-status"] == "Unpatched":
> +            unpatched_cves.append(cve)
> 
>      if unpatched_cves and d.getVar("CVE_CHECK_SHOW_WARNINGS") == "1":
>          bb.warn("Found unpatched CVE (%s), for more information check %s" % ("
> ".join(unpatched_cves),cve_file))
> @@ -563,13 +587,11 @@ def cve_check_write_json_output(d, output, direct_file,
> deploy_file, manifest_fi
>          with open(index_path, "a+") as f:
>              f.write("%s\n" % fragment_path)
> 
> -def cve_write_data_json(d, patched, unpatched, ignored, cve_data, cve_status):
> +def cve_write_data_json(d, cve_data, cve_status):
>      """
>      Prepare CVE data for the JSON format, then write it.
>      """
> 
> -    from oe.cve_check import decode_cve_status
> -
>      output = {"version":"1", "package": []}
>      nvd_link = "https://nvd.nist.gov/vuln/detail/"
> 
> @@ -587,8 +609,6 @@ def cve_write_data_json(d, patched, unpatched, ignored,
> cve_data, cve_status):
>      if include_layers and layer not in include_layers:
>          return
> 
> -    unpatched_cves = []
> -
>      product_data = []
>      for s in cve_status:
>          p = {"product": s[0], "cvesInRecord": "Yes"}
> @@ -603,40 +623,32 @@ def cve_write_data_json(d, patched, unpatched,
> ignored, cve_data, cve_status):
>          "version" : package_version,
>          "products": product_data
>      }
> +
>      cve_list = []
> 
>      for cve in sorted(cve_data):
> -        is_patched = cve in patched
> -        is_ignored = cve in ignored
> -        status = "Unpatched"
> -        if (is_patched or is_ignored) and not report_all:
> +        if not report_all and (cve_data[cve]["abbrev-status"] == "Patched" or
> cve_data[cve]["abbrev-status"] == "Ignored"):
>              continue
> -        if is_ignored:
> -            status = "Ignored"
> -        elif is_patched:
> -            status = "Patched"
> -        else:
> -            # default value of status is Unpatched
> -            unpatched_cves.append(cve)
> -
>          issue_link = "%s%s" % (nvd_link, cve)
> 
>          cve_item = {
>              "id" : cve,
> -            "summary" : cve_data[cve]["summary"],
> -            "scorev2" : cve_data[cve]["scorev2"],
> -            "scorev3" : cve_data[cve]["scorev3"],
> -            "scorev4" : cve_data[cve]["scorev4"],
> -            "vector" : cve_data[cve]["vector"],
> -            "vectorString" : cve_data[cve]["vectorString"],
> -            "status" : status,
> -            "link": issue_link
> +            "status" : cve_data[cve]["abbrev-status"],
> +            "link": issue_link,
>          }
> -        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']
> +        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
> @@ -648,12 +660,12 @@ def cve_write_data_json(d, patched, unpatched,
> ignored, cve_data, cve_status):
> 
>      cve_check_write_json_output(d, output, direct_file, deploy_file, manifest_file)
> 
> -def cve_write_data(d, patched, unpatched, ignored, cve_data, status):
> +def cve_write_data(d, cve_data, status):
>      """
>      Write CVE data in each enabled format.
>      """
> 
>      if d.getVar("CVE_CHECK_FORMAT_TEXT") == "1":
> -        cve_write_data_text(d, patched, unpatched, ignored, cve_data)
> +        cve_write_data_text(d, cve_data)
>      if d.getVar("CVE_CHECK_FORMAT_JSON") == "1":
> -        cve_write_data_json(d, patched, unpatched, ignored, cve_data, status)
> +        cve_write_data_json(d, cve_data, status)
> diff --git a/meta/lib/oe/cve_check.py b/meta/lib/oe/cve_check.py
> index 767d1a6750..37230b7957 100644
> --- a/meta/lib/oe/cve_check.py
> +++ b/meta/lib/oe/cve_check.py
> @@ -88,7 +88,7 @@ def get_patched_cves(d):
>      # (cve_match regular expression)
>      cve_file_name_match = re.compile(r".*(CVE-\d{4}-\d+)", re.IGNORECASE)
> 
> -    patched_cves = set()
> +    patched_cves = {}
>      patches = oe.patch.src_patches(d)
>      bb.debug(2, "Scanning %d patches for CVEs" % len(patches))
>      for url in patches:
> @@ -98,7 +98,7 @@ def get_patched_cves(d):
>          fname_match = cve_file_name_match.search(patch_file)
>          if fname_match:
>              cve = fname_match.group(1).upper()
> -            patched_cves.add(cve)
> +            patched_cves[cve] = {"abbrev-status": "Patched", "status": "fix-file-
> included", "resource": patch_file}
>              bb.debug(2, "Found %s from patch file name %s" % (cve, patch_file))
> 
>          # Remote patches won't be present and compressed patches won't be
> @@ -124,7 +124,7 @@ def get_patched_cves(d):
>              cves = patch_text[match.start()+5:match.end()]
>              for cve in cves.split():
>                  bb.debug(2, "Patch %s solves %s" % (patch_file, cve))
> -                patched_cves.add(cve)
> +                patched_cves[cve] = {"abbrev-status": "Patched", "status": "fix-file-
> included", "resource": patch_file}
>                  text_match = True
> 
>          if not fname_match and not text_match:
> @@ -133,9 +133,15 @@ 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 'mapping' in decoded_status and decoded_status['mapping'] ==
> "Patched":
> -            bb.debug(2, "CVE %s is additionally patched" % cve)
> -            patched_cves.add(cve)
> +        products = d.getVar("CVE_PRODUCT")
> +        if has_cve_product_match(decoded_status, products) == True:
> +            patched_cves[cve] = {
> +                "abbrev-status": decoded_status["mapping"],
> +                "status": decoded_status["detail"],
> +                "justification": decoded_status["description"],
> +                "affected-vendor": decoded_status["vendor"],
> +                "affected-product": decoded_status["product"]
> +            }
> 
>      return patched_cves
> 
> @@ -286,3 +292,20 @@ def extend_cve_status(d):
>                  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 has_cve_product_match(detailed_status, products):
> +    """
> +    Check product/vendor match between detailed_status from
> decode_cve_status and a string of
> +    products (like from CVE_PRODUCT)
> +    """
> +    for product in products.split():
> +        vendor = "*"
> +        if ":" in product:
> +            vendor, product = product.split(":", 1)
> +
> +        if (vendor == detailed_status["vendor"] or detailed_status["vendor"] == "*")
> and \
> +            (product == detailed_status["product"] or detailed_status["product"] ==
> "*"):
> +            return True
> +
> +    #if no match, return False
> +    return False


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [OE-core] [scarthgap] [PATCH v1 0/4] cve-check: fix incorrect CVE assessments and runtime warnings - cover letter
  2026-03-18  5:39 [OE-core] [scarthgap] [PATCH v1 0/4] cve-check: fix incorrect CVE assessments and runtime warnings - cover letter Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
                   ` (3 preceding siblings ...)
  2026-03-18  5:39 ` [OE-core] [scarthgap] [PATCH v1 4/4] cve-check: fix debug message Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
@ 2026-03-18 11:07 ` Yoann Congal
  2026-03-18 12:57   ` Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
  4 siblings, 1 reply; 13+ messages in thread
From: Yoann Congal @ 2026-03-18 11:07 UTC (permalink / raw)
  To: hetpat, openembedded-core; +Cc: xe-linux-external, vchavda

Hello,

On Wed Mar 18, 2026 at 6:39 AM CET, Het Patel via lists.openembedded.org wrote:
> From: Het Patel <hetpat@cisco.com>
>
> The patches address the following bugs:
>
> 1. Incomplete CVE Assessment Details: Currently, the `detail` field is missing for approximately 81% of entries, rendering reports unreliable for auditing. These changes ensure that the rationale for a "Patched" or "Unpatched" assessment is properly recorded, allowing for a clear distinction between version-based assessments and missing data.
>
> 2. Runtime Warnings: Corrects four instances where debug calls were missing the required log level parameter. This change eliminates the runtime warnings that currently trigger during every CVE scan.

I appreciate that you trimed down your previous try to cleanup CVE
checking code[0]. But I still feel like it is too intrusive for stable
inclusion.

Can you please provide examples of some CVEs having "Incomplete CVE
Assessment Details:" so I can understand the problem?

> Testing:
> - Applied cleanly to the current `scarthgap` HEAD.
> - Verified via a full CVE scan.
> - Confirmed that all existing CVE statuses are preserved with no regressions observed.

Can you provide output (log+json) both before/after to verify this
claim?

Thanks!

[0]: https://lore.kernel.org/openembedded-core/20260220053443.3006180-1-hetpat@cisco.com/#r

> Het Patel (4):
>   cve-check: encode affected product/vendor in CVE_STATUS
>   cve-check: annotate CVEs during analysis
>   cve-check-map: add new statuses
>   cve-check: fix debug message
>
>  meta/classes/cve-check.bbclass | 246 +++++++++++++++++++++--------------------
>  meta/conf/cve-check-map.conf   |   9 +
>  meta/lib/oe/cve_check.py       |  74 +++++++++---
>  3 files changed, 197 insertions(+), 132 deletions(-)


-- 
Yoann Congal
Smile ECS



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [OE-core] [scarthgap] [PATCH v1 1/4] cve-check: encode affected product/vendor in CVE_STATUS
  2026-03-18  7:31   ` Marko, Peter (FT D EU SK BFS1)
@ 2026-03-18 12:54     ` Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
  0 siblings, 0 replies; 13+ messages in thread
From: Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-03-18 12:54 UTC (permalink / raw)
  To: openembedded-core@lists.openembedded.org, peter.marko@siemens.com
  Cc: xe-linux-external(mailer list), Viral Chavda (vchavda)

[-- Attachment #1: Type: text/plain, Size: 8308 bytes --]

Hi Peter,

Thanks for the response. I will include these additional patches in the new series as per your suggestion.

Regards,
Het
________________________________
From: openembedded-core@lists.openembedded.org <openembedded-core@lists.openembedded.org> on behalf of Peter Marko via lists.openembedded.org <peter.marko=siemens.com@lists.openembedded.org>
Sent: Wednesday, March 18, 2026 1:01 PM
To: Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco) <hetpat@cisco.com>; openembedded-core@lists.openembedded.org <openembedded-core@lists.openembedded.org>
Cc: xe-linux-external(mailer list) <xe-linux-external@cisco.com>; Viral Chavda (vchavda) <vchavda@cisco.com>
Subject: Re: [OE-core] [scarthgap] [PATCH v1 1/4] cve-check: encode affected product/vendor in CVE_STATUS

This commit needs two additional fixes:
https://git.openembedded.org/openembedded-core/commit/?id=3c4d8ca41ac0b429af92bf0ea84f1dfd0cda9e1f
https://git.openembedded.org/openembedded-core/commit/?id=cc33dd9176726cb4b2d2f142ed1bc655da8e0a9f

Peter

> -----Original Message-----
> From: openembedded-core@lists.openembedded.org <openembedded-
> core@lists.openembedded.org> On Behalf Of Het Patel via
> lists.openembedded.org
> Sent: Wednesday, March 18, 2026 6:39
> To: openembedded-core@lists.openembedded.org
> Cc: xe-linux-external@cisco.com; vchavda@cisco.com
> Subject: [OE-core] [scarthgap] [PATCH v1 1/4] cve-check: encode affected
> product/vendor in CVE_STATUS
>
> From: Marta Rybczynska <rybczynska@gmail.com>
>
> 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.
>
> Backport Changes:
> - Discarded the changes to meta/lib/oe/spdx30_tasks.py, as the
> commit history for this file diverges from the base commit
> itself (9c9b9545049a in the scarthgap branch).
> - Additionally, the changes do not introduce any major features
> and are primarily focused on code restructuring.
>
> Signed-off-by: Marta Rybczynska <marta.rybczynska@syslinbit.com>
> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
> (cherry picked from commit abca80a716e92fc18d3085aba1a15f4bac72379c)
> Signed-off-by: Het Patel <hetpat@cisco.com>
> ---
>  meta/classes/cve-check.bbclass | 24 ++++++++++-----------
>  meta/lib/oe/cve_check.py       | 39 ++++++++++++++++++++++++++--------
>  2 files changed, 42 insertions(+), 21 deletions(-)
>
> diff --git a/meta/classes/cve-check.bbclass b/meta/classes/cve-check.bbclass
> index 3f4704fb4e..de5ddf6f04 100644
> --- a/meta/classes/cve-check.bbclass
> +++ b/meta/classes/cve-check.bbclass
> @@ -316,8 +316,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
> @@ -500,11 +500,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"]
> @@ -632,11 +632,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
> diff --git a/meta/lib/oe/cve_check.py b/meta/lib/oe/cve_check.py
> index 7c09b78242..767d1a6750 100644
> --- a/meta/lib/oe/cve_check.py
> +++ b/meta/lib/oe/cve_check.py
> @@ -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,22 +227,43 @@ 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
>
>  def extend_cve_status(d):
>      # do this only once in case multiple classes use this

[-- Attachment #2: Type: text/html, Size: 14939 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [OE-core] [scarthgap] [PATCH v1 2/4] cve-check: annotate CVEs during analysis
  2026-03-18  7:38   ` Marko, Peter (FT D EU SK BFS1)
@ 2026-03-18 12:55     ` Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
  0 siblings, 0 replies; 13+ messages in thread
From: Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-03-18 12:55 UTC (permalink / raw)
  To: openembedded-core@lists.openembedded.org, peter.marko@siemens.com
  Cc: xe-linux-external(mailer list), Viral Chavda (vchavda),
	Marta Rybczynska

[-- Attachment #1: Type: text/plain, Size: 25218 bytes --]

Hi Peter,

Thank you for the feedback.

Without CPE tagging, old-format entries from cve-extra-exclusions.inc
(vendor=* product=*) would match all recipes via has_cve_product_match(),
causing report bloat.

So, I have to include commit [e1bf43561093] in this series to address this.
This file was missed during testing since it is not included by default.

I will update the series with this additional patch and resubmit.

Regards,
Het
________________________________
From: openembedded-core@lists.openembedded.org <openembedded-core@lists.openembedded.org> on behalf of Peter Marko via lists.openembedded.org <peter.marko=siemens.com@lists.openembedded.org>
Sent: Wednesday, March 18, 2026 1:08 PM
To: Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco) <hetpat@cisco.com>; openembedded-core@lists.openembedded.org <openembedded-core@lists.openembedded.org>
Cc: xe-linux-external(mailer list) <xe-linux-external@cisco.com>; Viral Chavda (vchavda) <vchavda@cisco.com>; Marta Rybczynska <rybczynska@gmail.com>
Subject: Re: [OE-core] [scarthgap] [PATCH v1 2/4] cve-check: annotate CVEs during analysis

If I remember correctly, this commit brings significant (breaking) change how ignored CVEs are reported.
Before, only ignored CVEs matching NVD DB are reported, after, all ignored CVEs are reported.

Therefore it would also need a change in common cve includes (like https://git.openembedded.org/openembedded-core/commit/?id=e1bf43561093b3b9215cde9e9f7d80b4ffcdc64e).
And change all such files in user layers, so updating migration notes (which was unfortunately not done for new releases, however this patch is trying to do this in mid of LTS release).

Peter

> -----Original Message-----
> From: openembedded-core@lists.openembedded.org <openembedded-
> core@lists.openembedded.org> On Behalf Of Het Patel via
> lists.openembedded.org
> Sent: Wednesday, March 18, 2026 6:39
> To: openembedded-core@lists.openembedded.org
> Cc: xe-linux-external@cisco.com; vchavda@cisco.com
> Subject: [OE-core] [scarthgap] [PATCH v1 2/4] cve-check: annotate CVEs during
> analysis
>
> From: Marta Rybczynska <rybczynska@gmail.com>
>
> Add status information for each CVE under analysis.
>
> Previously the information passed between different function of the
> cve-check class included only tables of patched, unpatched, ignored
> vulnerabilities and the general status of the recipe.
>
> The VEX work requires more information, and we need to pass them
> between different functions, so that it can be enriched as the
> analysis progresses. Instead of multiple tables, use a single one
> with annotations for each CVE encountered. For example, a patched
> CVE will have:
>
> {"abbrev-status": "Patched", "status": "version-not-in-range"}
>
> abbrev-status contains the general status (Patched, Unpatched,
> Ignored and Unknown that will be added in the VEX code)
> status contains more detailed information that can come from
> CVE_STATUS and the analysis.
>
> Additional fields of the annotation include for example the name
> of the patch file fixing a given CVE.
>
> We also use the annotation in CVE_STATUS to filter out entries
> that do not apply to the given recipe
>
> Backport Changes:
> - Cherry-picking this patch, which precedes commit [358dbfcd80ae] in
> master. Since commit [358dbfcd80ae] was already cherry-picked earlier
> in scarthgap, adjusted the changes accordingly to avoid conflicts.
>
> Signed-off-by: Marta Rybczynska <marta.rybczynska@syslinbit.com>
> Signed-off-by: Samantha Jalabert <samantha.jalabert@syslinbit.com>
> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
> (cherry picked from commit 452e605b55ad61c08f4af7089a5a9c576ca28f7d)
> Signed-off-by: Het Patel <hetpat@cisco.com>
> ---
>  meta/classes/cve-check.bbclass | 214 +++++++++++++++++----------------
>  meta/lib/oe/cve_check.py       |  35 +++++-
>  2 files changed, 142 insertions(+), 107 deletions(-)
>
> diff --git a/meta/classes/cve-check.bbclass b/meta/classes/cve-check.bbclass
> index de5ddf6f04..32fb9e8a5c 100644
> --- a/meta/classes/cve-check.bbclass
> +++ b/meta/classes/cve-check.bbclass
> @@ -176,10 +176,10 @@ python do_cve_check () {
>                  patched_cves = get_patched_cves(d)
>              except FileNotFoundError:
>                  bb.fatal("Failure in searching patches")
> -            ignored, patched, unpatched, status = check_cves(d, patched_cves)
> -            if patched or unpatched or (d.getVar("CVE_CHECK_COVERAGE") == "1"
> and status):
> -                cve_data = get_cve_info(d, patched + unpatched + ignored)
> -                cve_write_data(d, patched, unpatched, ignored, cve_data, status)
> +            cve_data, status = check_cves(d, patched_cves)
> +            if len(cve_data) or (d.getVar("CVE_CHECK_COVERAGE") == "1" and
> status):
> +                get_cve_info(d, cve_data)
> +                cve_write_data(d, cve_data, status)
>          else:
>              bb.note("No CVE database found, skipping CVE check")
>
> @@ -287,7 +287,51 @@ ROOTFS_POSTPROCESS_COMMAND:prepend =
> "${@'cve_check_write_rootfs_manifest ' if d
>  do_rootfs[recrdeptask] += "${@'do_cve_check' if
> d.getVar('CVE_CHECK_CREATE_MANIFEST') == '1' else ''}"
>  do_populate_sdk[recrdeptask] += "${@'do_cve_check' if
> d.getVar('CVE_CHECK_CREATE_MANIFEST') == '1' else ''}"
>
> -def check_cves(d, patched_cves):
> +def cve_is_ignored(d, cve_data, cve):
> +    if cve not in cve_data:
> +        return False
> +    if cve_data[cve]['abbrev-status'] == "Ignored":
> +        return True
> +    return False
> +
> +def cve_is_patched(d, cve_data, cve):
> +    if cve not in cve_data:
> +        return False
> +    if cve_data[cve]['abbrev-status'] == "Patched":
> +        return True
> +    return False
> +
> +def cve_update(d, cve_data, cve, entry):
> +    # If no entry, just add it
> +    if cve not in cve_data:
> +        cve_data[cve] = entry
> +        return
> +    # If we are updating, there might be change in the status
> +    bb.debug("Trying CVE entry update for %s from %s to %s" % (cve,
> cve_data[cve]['abbrev-status'], entry['abbrev-status']))
> +    if cve_data[cve]['abbrev-status'] == "Unknown":
> +        cve_data[cve] = entry
> +        return
> +    if cve_data[cve]['abbrev-status'] == entry['abbrev-status']:
> +        return
> +    # Update like in {'abbrev-status': 'Patched', 'status': 'version-not-in-range'} to
> {'abbrev-status': 'Unpatched', 'status': 'version-in-range'}
> +    if entry['abbrev-status'] == "Unpatched" and cve_data[cve]['abbrev-status'] ==
> "Patched":
> +        if entry['status'] == "version-in-range" and cve_data[cve]['status'] ==
> "version-not-in-range":
> +            # New result from the scan, vulnerable
> +            cve_data[cve] = entry
> +            bb.debug("CVE entry %s update from Patched to Unpatched from the
> scan result" % cve)
> +            return
> +    if entry['abbrev-status'] == "Patched" and cve_data[cve]['abbrev-status'] ==
> "Unpatched":
> +        if entry['status'] == "version-not-in-range" and cve_data[cve]['status'] ==
> "version-in-range":
> +            # Range does not match the scan, but we already have a vulnerable
> match, ignore
> +            bb.debug("CVE entry %s update from Patched to Unpatched from the
> scan result - not applying" % cve)
> +            return
> +    # If we have an "Ignored", it has a priority
> +    if cve_data[cve]['abbrev-status'] == "Ignored":
> +        bb.debug("CVE %s not updating because Ignored" % cve)
> +        return
> +    bb.warn("Unhandled CVE entry update for %s from %s to %s" % (cve,
> cve_data[cve], entry))
> +
> +def check_cves(d, cve_data):
>      """
>      Connect to the NVD database and find unpatched cves.
>      """
> @@ -297,28 +341,19 @@ def check_cves(d, patched_cves):
>      real_pv = d.getVar("PV")
>      suffix = d.getVar("CVE_VERSION_SUFFIX")
>
> -    cves_unpatched = []
> -    cves_ignored = []
>      cves_status = []
>      cves_in_recipe = False
>      # CVE_PRODUCT can contain more than one product (eg. curl/libcurl)
>      products = d.getVar("CVE_PRODUCT").split()
>      # If this has been unset then we're not scanning for CVEs here (for example,
> image recipes)
>      if not products:
> -        return ([], [], [], [])
> +        return ([], [])
>      pv = d.getVar("CVE_VERSION").split("+git")[0]
>
>      # If the recipe has been skipped/ignored we return empty lists
>      if pn in d.getVar("CVE_CHECK_SKIP_RECIPE").split():
>          bb.note("Recipe has been skipped by cve-check")
> -        return ([], [], [], [])
> -
> -    # 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 'mapping' in decoded_status and decoded_status['mapping'] ==
> "Ignored":
> -            cve_ignore.append(cve)
> +        return ([], [])
>
>      import sqlite3
>      db_file = d.expand("file:${CVE_CHECK_DB_FILE}?mode=ro")
> @@ -337,11 +372,10 @@ def check_cves(d, patched_cves):
>          for cverow in cve_cursor:
>              cve = cverow[0]
>
> -            if cve in cve_ignore:
> +            if cve_is_ignored(d, cve_data, cve):
>                  bb.note("%s-%s ignores %s" % (product, pv, cve))
> -                cves_ignored.append(cve)
>                  continue
> -            elif cve in patched_cves:
> +            elif cve_is_patched(d, cve_data, cve):
>                  bb.note("%s has been patched" % (cve))
>                  continue
>              # Write status once only for each product
> @@ -357,7 +391,7 @@ def check_cves(d, patched_cves):
>              for row in product_cursor:
>                  (_, _, _, version_start, operator_start, version_end, operator_end) = row
>                  #bb.debug(2, "Evaluating row " + str(row))
> -                if cve in cve_ignore:
> +                if cve_is_ignored(d, cve_data, cve):
>                      ignored = True
>
>                  version_start = convert_cve_version(version_start)
> @@ -396,16 +430,16 @@ def check_cves(d, patched_cves):
>                  if vulnerable:
>                      if ignored:
>                          bb.note("%s is ignored in %s-%s" % (cve, pn, real_pv))
> -                        cves_ignored.append(cve)
> +                        cve_update(d, cve_data, cve, {"abbrev-status": "Ignored"})
>                      else:
>                          bb.note("%s-%s is vulnerable to %s" % (pn, real_pv, cve))
> -                        cves_unpatched.append(cve)
> +                        cve_update(d, cve_data, cve, {"abbrev-status": "Unpatched",
> "status": "version-in-range"})
>                      break
>              product_cursor.close()
>
>              if not vulnerable:
>                  bb.note("%s-%s is not vulnerable to %s" % (pn, real_pv, cve))
> -                patched_cves.add(cve)
> +                cve_update(d, cve_data, cve, {"abbrev-status": "Patched", "status":
> "version-not-in-range"})
>          cve_cursor.close()
>
>          if not cves_in_product:
> @@ -413,49 +447,46 @@ def check_cves(d, patched_cves):
>              cves_status.append([product, False])
>
>      conn.close()
> -    diff_ignore = list(set(cve_ignore) - set(cves_ignored))
> -    if diff_ignore:
> -        oe.qa.handle_error("cve_status_not_in_db", "Found CVE (%s) with
> CVE_STATUS set that are not found in database for this component" % "
> ".join(diff_ignore), d)
>
>      if not cves_in_recipe:
>          bb.note("No CVE records for products in recipe %s" % (pn))
>
> -    return (list(cves_ignored), list(patched_cves), cves_unpatched, cves_status)
> +    return (cve_data, cves_status)
>
> -def get_cve_info(d, cves):
> +def get_cve_info(d, cve_data):
>      """
>      Get CVE information from the database.
>      """
>
>      import sqlite3
>
> -    cve_data = {}
>      db_file = d.expand("file:${CVE_CHECK_DB_FILE}?mode=ro")
>      conn = sqlite3.connect(db_file, uri=True)
>
> -    for cve in cves:
> +    for cve in cve_data:
>          cursor = conn.execute("SELECT * FROM NVD WHERE ID IS ?", (cve,))
>          for row in cursor:
> -            cve_data[row[0]] = {}
> -            cve_data[row[0]]["summary"] = row[1]
> -            cve_data[row[0]]["scorev2"] = row[2]
> -            cve_data[row[0]]["scorev3"] = row[3]
> -            cve_data[row[0]]["scorev4"] = row[4]
> -            cve_data[row[0]]["modified"] = row[5]
> -            cve_data[row[0]]["vector"] = row[6]
> -            cve_data[row[0]]["vectorString"] = row[7]
> +            # The CVE itdelf has been added already
> +            if row[0] not in cve_data:
> +                bb.note("CVE record %s not present" % row[0])
> +                continue
> +            #cve_data[row[0]] = {}
> +            cve_data[row[0]]["NVD-summary"] = row[1]
> +            cve_data[row[0]]["NVD-scorev2"] = row[2]
> +            cve_data[row[0]]["NVD-scorev3"] = row[3]
> +            cve_data[row[0]]["NVD-scorev4"] = row[4]
> +            cve_data[row[0]]["NVD-modified"] = row[5]
> +            cve_data[row[0]]["NVD-vector"] = row[6]
> +            cve_data[row[0]]["NVD-vectorString"] = row[7]
>          cursor.close()
>      conn.close()
> -    return cve_data
>
> -def cve_write_data_text(d, patched, unpatched, ignored, cve_data):
> +def cve_write_data_text(d, cve_data):
>      """
>      Write CVE information in WORKDIR; and to CVE_CHECK_DIR, and
>      CVE manifest if enabled.
>      """
>
> -    from oe.cve_check import decode_cve_status
> -
>      cve_file = d.getVar("CVE_CHECK_LOG")
>      fdir_name  = d.getVar("FILE_DIRNAME")
>      layer = fdir_name.split("/")[-3]
> @@ -472,7 +503,7 @@ def cve_write_data_text(d, patched, unpatched, ignored,
> cve_data):
>          return
>
>      # Early exit, the text format does not report packages without CVEs
> -    if not patched+unpatched+ignored:
> +    if not len(cve_data):
>          return
>
>      nvd_link = "https://nvd.nist.gov/vuln/detail/"
> @@ -481,37 +512,30 @@ def cve_write_data_text(d, patched, unpatched,
> ignored, cve_data):
>      bb.utils.mkdirhier(os.path.dirname(cve_file))
>
>      for cve in sorted(cve_data):
> -        is_patched = cve in patched
> -        is_ignored = cve in ignored
> -
> -        status = "Unpatched"
> -        if (is_patched or is_ignored) and not report_all:
> +        if not report_all and (cve_data[cve]["abbrev-status"] == "Patched" or
> cve_data[cve]["abbrev-status"] == "Ignored"):
>              continue
> -        if is_ignored:
> -            status = "Ignored"
> -        elif is_patched:
> -            status = "Patched"
> -        else:
> -            # default value of status is Unpatched
> -            unpatched_cves.append(cve)
> -
>          write_string += "LAYER: %s\n" % layer
>          write_string += "PACKAGE NAME: %s\n" % d.getVar("PN")
>          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
> -        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"]
> -        write_string += "CVSS v4 BASE SCORE: %s\n" % cve_data[cve]["scorev4"]
> -        write_string += "VECTOR: %s\n" % cve_data[cve]["vector"]
> -        write_string += "VECTORSTRING: %s\n" % cve_data[cve]["vectorString"]
> +        write_string += "CVE STATUS: %s\n" % cve_data[cve]["abbrev-status"]
> +
> +        if 'status' in cve_data[cve]:
> +            write_string += "CVE DETAIL: %s\n" % cve_data[cve]["status"]
> +        if 'justification' in cve_data[cve]:
> +            write_string += "CVE DESCRIPTION: %s\n" %
> cve_data[cve]["justification"]
> +
> +        if "NVD-summary" in cve_data[cve]:
> +            write_string += "CVE SUMMARY: %s\n" % cve_data[cve]["NVD-
> summary"]
> +            write_string += "CVSS v2 BASE SCORE: %s\n" % cve_data[cve]["NVD-
> scorev2"]
> +            write_string += "CVSS v3 BASE SCORE: %s\n" % cve_data[cve]["NVD-
> scorev3"]
> +            write_string += "CVSS v3 BASE SCORE: %s\n" % cve_data[cve]["NVD-
> scorev4"]
> +            write_string += "VECTOR: %s\n" % cve_data[cve]["NVD-vector"]
> +            write_string += "VECTORSTRING: %s\n" % cve_data[cve]["NVD-
> vectorString"]
> +
>          write_string += "MORE INFORMATION: %s%s\n\n" % (nvd_link, cve)
> +        if cve_data[cve]["abbrev-status"] == "Unpatched":
> +            unpatched_cves.append(cve)
>
>      if unpatched_cves and d.getVar("CVE_CHECK_SHOW_WARNINGS") == "1":
>          bb.warn("Found unpatched CVE (%s), for more information check %s" % ("
> ".join(unpatched_cves),cve_file))
> @@ -563,13 +587,11 @@ def cve_check_write_json_output(d, output, direct_file,
> deploy_file, manifest_fi
>          with open(index_path, "a+") as f:
>              f.write("%s\n" % fragment_path)
>
> -def cve_write_data_json(d, patched, unpatched, ignored, cve_data, cve_status):
> +def cve_write_data_json(d, cve_data, cve_status):
>      """
>      Prepare CVE data for the JSON format, then write it.
>      """
>
> -    from oe.cve_check import decode_cve_status
> -
>      output = {"version":"1", "package": []}
>      nvd_link = "https://nvd.nist.gov/vuln/detail/"
>
> @@ -587,8 +609,6 @@ def cve_write_data_json(d, patched, unpatched, ignored,
> cve_data, cve_status):
>      if include_layers and layer not in include_layers:
>          return
>
> -    unpatched_cves = []
> -
>      product_data = []
>      for s in cve_status:
>          p = {"product": s[0], "cvesInRecord": "Yes"}
> @@ -603,40 +623,32 @@ def cve_write_data_json(d, patched, unpatched,
> ignored, cve_data, cve_status):
>          "version" : package_version,
>          "products": product_data
>      }
> +
>      cve_list = []
>
>      for cve in sorted(cve_data):
> -        is_patched = cve in patched
> -        is_ignored = cve in ignored
> -        status = "Unpatched"
> -        if (is_patched or is_ignored) and not report_all:
> +        if not report_all and (cve_data[cve]["abbrev-status"] == "Patched" or
> cve_data[cve]["abbrev-status"] == "Ignored"):
>              continue
> -        if is_ignored:
> -            status = "Ignored"
> -        elif is_patched:
> -            status = "Patched"
> -        else:
> -            # default value of status is Unpatched
> -            unpatched_cves.append(cve)
> -
>          issue_link = "%s%s" % (nvd_link, cve)
>
>          cve_item = {
>              "id" : cve,
> -            "summary" : cve_data[cve]["summary"],
> -            "scorev2" : cve_data[cve]["scorev2"],
> -            "scorev3" : cve_data[cve]["scorev3"],
> -            "scorev4" : cve_data[cve]["scorev4"],
> -            "vector" : cve_data[cve]["vector"],
> -            "vectorString" : cve_data[cve]["vectorString"],
> -            "status" : status,
> -            "link": issue_link
> +            "status" : cve_data[cve]["abbrev-status"],
> +            "link": issue_link,
>          }
> -        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']
> +        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
> @@ -648,12 +660,12 @@ def cve_write_data_json(d, patched, unpatched,
> ignored, cve_data, cve_status):
>
>      cve_check_write_json_output(d, output, direct_file, deploy_file, manifest_file)
>
> -def cve_write_data(d, patched, unpatched, ignored, cve_data, status):
> +def cve_write_data(d, cve_data, status):
>      """
>      Write CVE data in each enabled format.
>      """
>
>      if d.getVar("CVE_CHECK_FORMAT_TEXT") == "1":
> -        cve_write_data_text(d, patched, unpatched, ignored, cve_data)
> +        cve_write_data_text(d, cve_data)
>      if d.getVar("CVE_CHECK_FORMAT_JSON") == "1":
> -        cve_write_data_json(d, patched, unpatched, ignored, cve_data, status)
> +        cve_write_data_json(d, cve_data, status)
> diff --git a/meta/lib/oe/cve_check.py b/meta/lib/oe/cve_check.py
> index 767d1a6750..37230b7957 100644
> --- a/meta/lib/oe/cve_check.py
> +++ b/meta/lib/oe/cve_check.py
> @@ -88,7 +88,7 @@ def get_patched_cves(d):
>      # (cve_match regular expression)
>      cve_file_name_match = re.compile(r".*(CVE-\d{4}-\d+)", re.IGNORECASE)
>
> -    patched_cves = set()
> +    patched_cves = {}
>      patches = oe.patch.src_patches(d)
>      bb.debug(2, "Scanning %d patches for CVEs" % len(patches))
>      for url in patches:
> @@ -98,7 +98,7 @@ def get_patched_cves(d):
>          fname_match = cve_file_name_match.search(patch_file)
>          if fname_match:
>              cve = fname_match.group(1).upper()
> -            patched_cves.add(cve)
> +            patched_cves[cve] = {"abbrev-status": "Patched", "status": "fix-file-
> included", "resource": patch_file}
>              bb.debug(2, "Found %s from patch file name %s" % (cve, patch_file))
>
>          # Remote patches won't be present and compressed patches won't be
> @@ -124,7 +124,7 @@ def get_patched_cves(d):
>              cves = patch_text[match.start()+5:match.end()]
>              for cve in cves.split():
>                  bb.debug(2, "Patch %s solves %s" % (patch_file, cve))
> -                patched_cves.add(cve)
> +                patched_cves[cve] = {"abbrev-status": "Patched", "status": "fix-file-
> included", "resource": patch_file}
>                  text_match = True
>
>          if not fname_match and not text_match:
> @@ -133,9 +133,15 @@ 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 'mapping' in decoded_status and decoded_status['mapping'] ==
> "Patched":
> -            bb.debug(2, "CVE %s is additionally patched" % cve)
> -            patched_cves.add(cve)
> +        products = d.getVar("CVE_PRODUCT")
> +        if has_cve_product_match(decoded_status, products) == True:
> +            patched_cves[cve] = {
> +                "abbrev-status": decoded_status["mapping"],
> +                "status": decoded_status["detail"],
> +                "justification": decoded_status["description"],
> +                "affected-vendor": decoded_status["vendor"],
> +                "affected-product": decoded_status["product"]
> +            }
>
>      return patched_cves
>
> @@ -286,3 +292,20 @@ def extend_cve_status(d):
>                  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 has_cve_product_match(detailed_status, products):
> +    """
> +    Check product/vendor match between detailed_status from
> decode_cve_status and a string of
> +    products (like from CVE_PRODUCT)
> +    """
> +    for product in products.split():
> +        vendor = "*"
> +        if ":" in product:
> +            vendor, product = product.split(":", 1)
> +
> +        if (vendor == detailed_status["vendor"] or detailed_status["vendor"] == "*")
> and \
> +            (product == detailed_status["product"] or detailed_status["product"] ==
> "*"):
> +            return True
> +
> +    #if no match, return False
> +    return False

[-- Attachment #2: Type: text/html, Size: 50655 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [OE-core] [scarthgap] [PATCH v1 0/4] cve-check: fix incorrect CVE assessments and runtime warnings - cover letter
  2026-03-18 11:07 ` [OE-core] [scarthgap] [PATCH v1 0/4] cve-check: fix incorrect CVE assessments and runtime warnings - cover letter Yoann Congal
@ 2026-03-18 12:57   ` Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
  2026-03-18 13:10     ` Yoann Congal
  0 siblings, 1 reply; 13+ messages in thread
From: Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-03-18 12:57 UTC (permalink / raw)
  To: Yoann Congal, openembedded-core@lists.openembedded.org
  Cc: xe-linux-external(mailer list), Viral Chavda (vchavda)

[-- Attachment #1: Type: text/plain, Size: 2559 bytes --]

Hi Yoann,

I will share the new series of patches, which includes a few additional ones. I will attach the corresponding output files to that.

Best regards,
Het
________________________________
From: Yoann Congal <yoann.congal@smile.fr>
Sent: Wednesday, March 18, 2026 4:37 PM
To: Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco) <hetpat@cisco.com>; openembedded-core@lists.openembedded.org <openembedded-core@lists.openembedded.org>
Cc: xe-linux-external(mailer list) <xe-linux-external@cisco.com>; Viral Chavda (vchavda) <vchavda@cisco.com>
Subject: Re: [OE-core] [scarthgap] [PATCH v1 0/4] cve-check: fix incorrect CVE assessments and runtime warnings - cover letter

Hello,

On Wed Mar 18, 2026 at 6:39 AM CET, Het Patel via lists.openembedded.org wrote:
> From: Het Patel <hetpat@cisco.com>
>
> The patches address the following bugs:
>
> 1. Incomplete CVE Assessment Details: Currently, the `detail` field is missing for approximately 81% of entries, rendering reports unreliable for auditing. These changes ensure that the rationale for a "Patched" or "Unpatched" assessment is properly recorded, allowing for a clear distinction between version-based assessments and missing data.
>
> 2. Runtime Warnings: Corrects four instances where debug calls were missing the required log level parameter. This change eliminates the runtime warnings that currently trigger during every CVE scan.

I appreciate that you trimed down your previous try to cleanup CVE
checking code[0]. But I still feel like it is too intrusive for stable
inclusion.

Can you please provide examples of some CVEs having "Incomplete CVE
Assessment Details:" so I can understand the problem?

> Testing:
> - Applied cleanly to the current `scarthgap` HEAD.
> - Verified via a full CVE scan.
> - Confirmed that all existing CVE statuses are preserved with no regressions observed.

Can you provide output (log+json) both before/after to verify this
claim?

Thanks!

[0]: https://lore.kernel.org/openembedded-core/20260220053443.3006180-1-hetpat@cisco.com/#r

> Het Patel (4):
>   cve-check: encode affected product/vendor in CVE_STATUS
>   cve-check: annotate CVEs during analysis
>   cve-check-map: add new statuses
>   cve-check: fix debug message
>
>  meta/classes/cve-check.bbclass | 246 +++++++++++++++++++++--------------------
>  meta/conf/cve-check-map.conf   |   9 +
>  meta/lib/oe/cve_check.py       |  74 +++++++++---
>  3 files changed, 197 insertions(+), 132 deletions(-)


--
Yoann Congal
Smile ECS


[-- Attachment #2: Type: text/html, Size: 4726 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [OE-core] [scarthgap] [PATCH v1 0/4] cve-check: fix incorrect CVE assessments and runtime warnings - cover letter
  2026-03-18 12:57   ` Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
@ 2026-03-18 13:10     ` Yoann Congal
  2026-03-18 13:15       ` Yoann Congal
  0 siblings, 1 reply; 13+ messages in thread
From: Yoann Congal @ 2026-03-18 13:10 UTC (permalink / raw)
  To: Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco),
	openembedded-core@lists.openembedded.org
  Cc: xe-linux-external(mailer list), Viral Chavda (vchavda)

On Wed Mar 18, 2026 at 1:57 PM CET, Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco) wrote:
> Hi Yoann,
>
> I will share the new series of patches, which includes a few additional ones. I will attach the corresponding output files to that.

Hmmm, I wrote that I felt that the series was too intrusive and now you
want to add more patches? Are you sure this is the right direction?
(I'm trying to prevent you from losing time to something that could
ultimately be unmergable...)

Regards,

>
> Best regards,
> Het
> ________________________________
> From: Yoann Congal <yoann.congal@smile.fr>
> Sent: Wednesday, March 18, 2026 4:37 PM
> To: Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco) <hetpat@cisco.com>; openembedded-core@lists.openembedded.org <openembedded-core@lists.openembedded.org>
> Cc: xe-linux-external(mailer list) <xe-linux-external@cisco.com>; Viral Chavda (vchavda) <vchavda@cisco.com>
> Subject: Re: [OE-core] [scarthgap] [PATCH v1 0/4] cve-check: fix incorrect CVE assessments and runtime warnings - cover letter
>
> Hello,
>
> On Wed Mar 18, 2026 at 6:39 AM CET, Het Patel via lists.openembedded.org wrote:
>> From: Het Patel <hetpat@cisco.com>
>>
>> The patches address the following bugs:
>>
>> 1. Incomplete CVE Assessment Details: Currently, the `detail` field is missing for approximately 81% of entries, rendering reports unreliable for auditing. These changes ensure that the rationale for a "Patched" or "Unpatched" assessment is properly recorded, allowing for a clear distinction between version-based assessments and missing data.
>>
>> 2. Runtime Warnings: Corrects four instances where debug calls were missing the required log level parameter. This change eliminates the runtime warnings that currently trigger during every CVE scan.
>
> I appreciate that you trimed down your previous try to cleanup CVE
> checking code[0]. But I still feel like it is too intrusive for stable
> inclusion.
>
> Can you please provide examples of some CVEs having "Incomplete CVE
> Assessment Details:" so I can understand the problem?
>
>> Testing:
>> - Applied cleanly to the current `scarthgap` HEAD.
>> - Verified via a full CVE scan.
>> - Confirmed that all existing CVE statuses are preserved with no regressions observed.
>
> Can you provide output (log+json) both before/after to verify this
> claim?
>
> Thanks!
>
> [0]: https://lore.kernel.org/openembedded-core/20260220053443.3006180-1-hetpat@cisco.com/#r
>
>> Het Patel (4):
>>   cve-check: encode affected product/vendor in CVE_STATUS
>>   cve-check: annotate CVEs during analysis
>>   cve-check-map: add new statuses
>>   cve-check: fix debug message
>>
>>  meta/classes/cve-check.bbclass | 246 +++++++++++++++++++++--------------------
>>  meta/conf/cve-check-map.conf   |   9 +
>>  meta/lib/oe/cve_check.py       |  74 +++++++++---
>>  3 files changed, 197 insertions(+), 132 deletions(-)
>
>
> --
> Yoann Congal
> Smile ECS


-- 
Yoann Congal
Smile ECS



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [OE-core] [scarthgap] [PATCH v1 0/4] cve-check: fix incorrect CVE assessments and runtime warnings - cover letter
  2026-03-18 13:10     ` Yoann Congal
@ 2026-03-18 13:15       ` Yoann Congal
  0 siblings, 0 replies; 13+ messages in thread
From: Yoann Congal @ 2026-03-18 13:15 UTC (permalink / raw)
  To: Yoann Congal,
	Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco),
	openembedded-core@lists.openembedded.org
  Cc: xe-linux-external(mailer list), Viral Chavda (vchavda)

On Wed Mar 18, 2026 at 2:10 PM CET, Yoann Congal wrote:
> On Wed Mar 18, 2026 at 1:57 PM CET, Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco) wrote:
>> Hi Yoann,
>>
>> I will share the new series of patches, which includes a few additional ones. I will attach the corresponding output files to that.
>
> Hmmm, I wrote that I felt that the series was too intrusive and now you
> want to add more patches? Are you sure this is the right direction?

Oh, I see now that you are talking about patches from Peter
suggestion. The series might still be too intrusive but it will be more
coherent. Got it.

> (I'm trying to prevent you from losing time to something that could
> ultimately be unmergable...)
>
> Regards,
>
>>
>> Best regards,
>> Het
>> ________________________________
>> From: Yoann Congal <yoann.congal@smile.fr>
>> Sent: Wednesday, March 18, 2026 4:37 PM
>> To: Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco) <hetpat@cisco.com>; openembedded-core@lists.openembedded.org <openembedded-core@lists.openembedded.org>
>> Cc: xe-linux-external(mailer list) <xe-linux-external@cisco.com>; Viral Chavda (vchavda) <vchavda@cisco.com>
>> Subject: Re: [OE-core] [scarthgap] [PATCH v1 0/4] cve-check: fix incorrect CVE assessments and runtime warnings - cover letter
>>
>> Hello,
>>
>> On Wed Mar 18, 2026 at 6:39 AM CET, Het Patel via lists.openembedded.org wrote:
>>> From: Het Patel <hetpat@cisco.com>
>>>
>>> The patches address the following bugs:
>>>
>>> 1. Incomplete CVE Assessment Details: Currently, the `detail` field is missing for approximately 81% of entries, rendering reports unreliable for auditing. These changes ensure that the rationale for a "Patched" or "Unpatched" assessment is properly recorded, allowing for a clear distinction between version-based assessments and missing data.
>>>
>>> 2. Runtime Warnings: Corrects four instances where debug calls were missing the required log level parameter. This change eliminates the runtime warnings that currently trigger during every CVE scan.
>>
>> I appreciate that you trimed down your previous try to cleanup CVE
>> checking code[0]. But I still feel like it is too intrusive for stable
>> inclusion.
>>
>> Can you please provide examples of some CVEs having "Incomplete CVE
>> Assessment Details:" so I can understand the problem?
>>
>>> Testing:
>>> - Applied cleanly to the current `scarthgap` HEAD.
>>> - Verified via a full CVE scan.
>>> - Confirmed that all existing CVE statuses are preserved with no regressions observed.
>>
>> Can you provide output (log+json) both before/after to verify this
>> claim?
>>
>> Thanks!
>>
>> [0]: https://lore.kernel.org/openembedded-core/20260220053443.3006180-1-hetpat@cisco.com/#r
>>
>>> Het Patel (4):
>>>   cve-check: encode affected product/vendor in CVE_STATUS
>>>   cve-check: annotate CVEs during analysis
>>>   cve-check-map: add new statuses
>>>   cve-check: fix debug message
>>>
>>>  meta/classes/cve-check.bbclass | 246 +++++++++++++++++++++--------------------
>>>  meta/conf/cve-check-map.conf   |   9 +
>>>  meta/lib/oe/cve_check.py       |  74 +++++++++---
>>>  3 files changed, 197 insertions(+), 132 deletions(-)
>>
>>
>> --
>> Yoann Congal
>> Smile ECS


-- 
Yoann Congal
Smile ECS



^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2026-03-18 13:15 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-18  5:39 [OE-core] [scarthgap] [PATCH v1 0/4] cve-check: fix incorrect CVE assessments and runtime warnings - cover letter Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-03-18  5:39 ` [OE-core] [scarthgap] [PATCH v1 1/4] cve-check: encode affected product/vendor in CVE_STATUS Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-03-18  7:31   ` Marko, Peter (FT D EU SK BFS1)
2026-03-18 12:54     ` Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-03-18  5:39 ` [OE-core] [scarthgap] [PATCH v1 2/4] cve-check: annotate CVEs during analysis Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-03-18  7:38   ` Marko, Peter (FT D EU SK BFS1)
2026-03-18 12:55     ` Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-03-18  5:39 ` [OE-core] [scarthgap] [PATCH v1 3/4] cve-check-map: add new statuses Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-03-18  5:39 ` [OE-core] [scarthgap] [PATCH v1 4/4] cve-check: fix debug message Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-03-18 11:07 ` [OE-core] [scarthgap] [PATCH v1 0/4] cve-check: fix incorrect CVE assessments and runtime warnings - cover letter Yoann Congal
2026-03-18 12:57   ` Het Patel -X (hetpat - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-03-18 13:10     ` Yoann Congal
2026-03-18 13:15       ` Yoann Congal

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox