diff mbox series

[v2] cve-check: add option to add additional patched CVEs

Message ID 20230517054138.33459-1-andrej.valek@siemens.com
State New
Headers show
Series [v2] cve-check: add option to add additional patched CVEs | expand

Commit Message

Andrej Valek May 17, 2023, 5:41 a.m. UTC
- Replace CVE_CHECK_IGNORE with CVE_STATUS + [CVE_STATUS_REASONING] to be
more flexible. CVE_STATUS should contains flag for each CVE with accepted
values "Ignored" or "Not applicable". It allows to add a status for CVEs
which could be fixed externally.
- Optional CVE_STATUS_REASONING flag variable could contains a reason
why the CVE status was used. It will be added in csv/json report like
a new "reason" entry.
- All listed CVEs in CVE_CHECK_IGNORE are copied to CVE_STATUS with
value "Ignored" like a fallback.

Example of usage:
CVE_STATUS[CVE-1234-0001] = "Not applicable" or "Ignored"
CVE_STATUS[CVE-1234-0002] = "Not applicable"
CVE_STATUS_REASONING[CVE-1234-0002] = "Issue only applies on windows"

Signed-off-by: Andrej Valek <andrej.valek@siemens.com>
---
 meta/classes/cve-check.bbclass | 30 +++++++++++++++++++++++++-----
 meta/lib/oe/cve_check.py       |  6 ++++++
 2 files changed, 31 insertions(+), 5 deletions(-)

Comments

Mikko Rapeli May 17, 2023, 11:08 a.m. UTC | #1
Hi,

On Wed, May 17, 2023 at 07:41:38AM +0200, Andrej Valek via lists.openembedded.org wrote:
> - Replace CVE_CHECK_IGNORE with CVE_STATUS + [CVE_STATUS_REASONING] to be
> more flexible. CVE_STATUS should contains flag for each CVE with accepted
> values "Ignored" or "Not applicable". It allows to add a status for CVEs
> which could be fixed externally.
> - Optional CVE_STATUS_REASONING flag variable could contains a reason
> why the CVE status was used. It will be added in csv/json report like
> a new "reason" entry.
> - All listed CVEs in CVE_CHECK_IGNORE are copied to CVE_STATUS with
> value "Ignored" like a fallback.
> 
> Example of usage:
> CVE_STATUS[CVE-1234-0001] = "Not applicable" or "Ignored"
> CVE_STATUS[CVE-1234-0002] = "Not applicable"
> CVE_STATUS_REASONING[CVE-1234-0002] = "Issue only applies on windows"

Looks good to me but would you add testing into
meta/lib/oeqa/selftest/cases/cve_check.py ?

And once merged update documentation in
documentation/dev-manual/vulnerabilities.rst,
documentation/ref-manual/classes.rst and
documentation/ref-manual/variables.rst ;)

Thanks,

-Mikko

> Signed-off-by: Andrej Valek <andrej.valek@siemens.com>
> ---
>  meta/classes/cve-check.bbclass | 30 +++++++++++++++++++++++++-----
>  meta/lib/oe/cve_check.py       |  6 ++++++
>  2 files changed, 31 insertions(+), 5 deletions(-)
> 
> diff --git a/meta/classes/cve-check.bbclass b/meta/classes/cve-check.bbclass
> index bd9e7e7445c..e081095037c 100644
> --- a/meta/classes/cve-check.bbclass
> +++ b/meta/classes/cve-check.bbclass
> @@ -70,13 +70,17 @@ CVE_CHECK_COVERAGE ??= "1"
>  # Skip CVE Check for packages (PN)
>  CVE_CHECK_SKIP_RECIPE ?= ""
>  
> -# Ingore the check for a given list of CVEs. If a CVE is found,
> -# then it is considered patched. The value is a string containing
> -# space separated CVE values:
> +# Ignore the check for a given CVE. Each of CVE has to be mentioned
> +# separately with optional reason, why it has to ignored.
>  #
> -# CVE_CHECK_IGNORE = 'CVE-2014-2524 CVE-2018-1234'
> +# CVE_STATUS[CVE-1234-0001] = "Not applicable" or "Ignored"
> +# CVE_STATUS[CVE-1234-0002] = "Ignored"
> +# CVE_STATUS_REASONING[CVE-1234-0002] = "Issue only applies on windows"
>  #
> +# CVE_CHECK_IGNORE is depracated and CVE_STATUS has to be used instead.
> +# Keep CVE_CHECK_IGNORE like a fallback.
>  CVE_CHECK_IGNORE ?= ""
> +CVE_STATUS ?= ""
>  
>  # Layers to be excluded
>  CVE_CHECK_LAYER_EXCLUDELIST ??= ""
> @@ -88,6 +92,12 @@ CVE_CHECK_LAYER_INCLUDELIST ??= ""
>  # set to "alphabetical" for version using single alphabetical character as increment release
>  CVE_VERSION_SUFFIX ??= ""
>  
> +python () {
> +    # Fallback all CVEs from CVE_CHECK_IGNORE to CVE_STATUS
> +    for cve in d.getVar("CVE_CHECK_IGNORE").split():
> +        d.setVarFlags("CVE_STATUS", {cve: "Ignored"})
> +}
> +
>  def generate_json_report(d, out_path, link_path):
>      if os.path.exists(d.getVar("CVE_CHECK_SUMMARY_INDEX_PATH")):
>          import json
> @@ -282,7 +292,11 @@ def check_cves(d, patched_cves):
>          bb.note("Recipe has been skipped by cve-check")
>          return ([], [], [], [])
>  
> -    cve_ignore = d.getVar("CVE_CHECK_IGNORE").split()
> +    # Convert CVE_STATUS into ignored CVEs
> +    cve_ignore = []
> +    for cve, status in (d.getVarFlags("CVE_STATUS") or {}).items():
> +        if status in ["Not applicable", "Ignored"]:
> +            cve_ignore.append(cve)
>  
>      import sqlite3
>      db_file = d.expand("file:${CVE_CHECK_DB_FILE}?mode=ro")
> @@ -455,6 +469,9 @@ def cve_write_data_text(d, patched, unpatched, ignored, cve_data):
>          else:
>              unpatched_cves.append(cve)
>              write_string += "CVE STATUS: Unpatched\n"
> +        has_reason = d.getVarFlag("CVE_STATUS_REASONING", cve)
> +        if has_reason:
> +            write_string += "CVE REASON: %s\n" % has_reason
>          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"]
> @@ -576,6 +593,9 @@ def cve_write_data_json(d, patched, unpatched, ignored, cve_data, cve_status):
>              "status" : status,
>              "link": issue_link
>          }
> +        has_reason = d.getVarFlag("CVE_STATUS_REASONING", cve)
> +        if has_reason:
> +            cve_item["reason"] = has_reason
>          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 dbaa0b373a3..f47dd9920ef 100644
> --- a/meta/lib/oe/cve_check.py
> +++ b/meta/lib/oe/cve_check.py
> @@ -130,6 +130,12 @@ def get_patched_cves(d):
>          if not fname_match and not text_match:
>              bb.debug(2, "Patch %s doesn't solve CVEs" % patch_file)
>  
> +    # Search for additional patched CVEs
> +    for cve, status in (d.getVarFlags("CVE_STATUS") or {}).items():
> +        if status == "Patched":
> +            bb.debug(2, "CVE %s is additionally patched" % cve)
> +            patched_cves.add(cve)
> +
>      return patched_cves
>  
>  
> -- 
> 2.40.1
> 

> 
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#181444): https://lists.openembedded.org/g/openembedded-core/message/181444
> Mute This Topic: https://lists.openembedded.org/mt/98943046/7159507
> Group Owner: openembedded-core+owner@lists.openembedded.org
> Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [mikko.rapeli@linaro.org]
> -=-=-=-=-=-=-=-=-=-=-=-
>
diff mbox series

Patch

diff --git a/meta/classes/cve-check.bbclass b/meta/classes/cve-check.bbclass
index bd9e7e7445c..e081095037c 100644
--- a/meta/classes/cve-check.bbclass
+++ b/meta/classes/cve-check.bbclass
@@ -70,13 +70,17 @@  CVE_CHECK_COVERAGE ??= "1"
 # Skip CVE Check for packages (PN)
 CVE_CHECK_SKIP_RECIPE ?= ""
 
-# Ingore the check for a given list of CVEs. If a CVE is found,
-# then it is considered patched. The value is a string containing
-# space separated CVE values:
+# Ignore the check for a given CVE. Each of CVE has to be mentioned
+# separately with optional reason, why it has to ignored.
 #
-# CVE_CHECK_IGNORE = 'CVE-2014-2524 CVE-2018-1234'
+# CVE_STATUS[CVE-1234-0001] = "Not applicable" or "Ignored"
+# CVE_STATUS[CVE-1234-0002] = "Ignored"
+# CVE_STATUS_REASONING[CVE-1234-0002] = "Issue only applies on windows"
 #
+# CVE_CHECK_IGNORE is depracated and CVE_STATUS has to be used instead.
+# Keep CVE_CHECK_IGNORE like a fallback.
 CVE_CHECK_IGNORE ?= ""
+CVE_STATUS ?= ""
 
 # Layers to be excluded
 CVE_CHECK_LAYER_EXCLUDELIST ??= ""
@@ -88,6 +92,12 @@  CVE_CHECK_LAYER_INCLUDELIST ??= ""
 # set to "alphabetical" for version using single alphabetical character as increment release
 CVE_VERSION_SUFFIX ??= ""
 
+python () {
+    # Fallback all CVEs from CVE_CHECK_IGNORE to CVE_STATUS
+    for cve in d.getVar("CVE_CHECK_IGNORE").split():
+        d.setVarFlags("CVE_STATUS", {cve: "Ignored"})
+}
+
 def generate_json_report(d, out_path, link_path):
     if os.path.exists(d.getVar("CVE_CHECK_SUMMARY_INDEX_PATH")):
         import json
@@ -282,7 +292,11 @@  def check_cves(d, patched_cves):
         bb.note("Recipe has been skipped by cve-check")
         return ([], [], [], [])
 
-    cve_ignore = d.getVar("CVE_CHECK_IGNORE").split()
+    # Convert CVE_STATUS into ignored CVEs
+    cve_ignore = []
+    for cve, status in (d.getVarFlags("CVE_STATUS") or {}).items():
+        if status in ["Not applicable", "Ignored"]:
+            cve_ignore.append(cve)
 
     import sqlite3
     db_file = d.expand("file:${CVE_CHECK_DB_FILE}?mode=ro")
@@ -455,6 +469,9 @@  def cve_write_data_text(d, patched, unpatched, ignored, cve_data):
         else:
             unpatched_cves.append(cve)
             write_string += "CVE STATUS: Unpatched\n"
+        has_reason = d.getVarFlag("CVE_STATUS_REASONING", cve)
+        if has_reason:
+            write_string += "CVE REASON: %s\n" % has_reason
         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"]
@@ -576,6 +593,9 @@  def cve_write_data_json(d, patched, unpatched, ignored, cve_data, cve_status):
             "status" : status,
             "link": issue_link
         }
+        has_reason = d.getVarFlag("CVE_STATUS_REASONING", cve)
+        if has_reason:
+            cve_item["reason"] = has_reason
         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 dbaa0b373a3..f47dd9920ef 100644
--- a/meta/lib/oe/cve_check.py
+++ b/meta/lib/oe/cve_check.py
@@ -130,6 +130,12 @@  def get_patched_cves(d):
         if not fname_match and not text_match:
             bb.debug(2, "Patch %s doesn't solve CVEs" % patch_file)
 
+    # Search for additional patched CVEs
+    for cve, status in (d.getVarFlags("CVE_STATUS") or {}).items():
+        if status == "Patched":
+            bb.debug(2, "CVE %s is additionally patched" % cve)
+            patched_cves.add(cve)
+
     return patched_cves