diff mbox series

[scarthgap,v1,1/4] cve-check: encode affected product/vendor in CVE_STATUS

Message ID 20260318053906.26606-2-hetpat@cisco.com
State New
Headers show
Series cve-check: fix incorrect CVE assessments and runtime warnings - cover letter | expand

Commit Message

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(-)

Comments

Marko, Peter (FT D EU SK BFS1) March 18, 2026, 7:31 a.m. UTC | #1
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
Hi Peter,

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

Regards,
Het
diff mbox series

Patch

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