- Replace CVE_CHECK_IGNORE with CVE_STATUS + [CVE_STATUS_DETAIL] + [CVE_STATUS_DESCRIPTION] to be more flexible. CVE_STATUS should contain flag for each CVE with accepted values "Ignored", "Patched" or "Unpatched". It allows to add a status for each CVEs. - Optional CVE_STATUS_DEATAIL flag variable may contain a detailed status. Possible options for each status: - Patched - fixed-version, backported-patch, cpe-stable-backport or other - Unpatched - vulnerable-investigating or other - Ignored - cpe-incorrect, not-applicable-platform, upstream-wontfix not-applicable-config, not-affected or other - Optional CVE_STATUS_DESCRIPTION flag variable may contain a reason why the CVE status was used. Both optionals will be added in csv/json report like a new "detail" an "description" entries - Settings the same status and reason for multiple CVEs is possible via CVE_STATUS_GROUPS variable. - All listed CVEs in CVE_CHECK_IGNORE are copied to CVE_STATUS with value "Ignored" like a fallback.
Examples of usage: CVE_STATUS[CVE-1234-0001] = "Ignored" # or "Patched" or "Unpatched" CVE_STATUS[CVE-1234-0002] = "Ignored" CVE_STATUS_DETAIL[CVE-1234-0002] = "not-applicable-platform" CVE_STATUS_DESCRIPTION[CVE-1234-0002] = "Issue only applies on Windows" CVE_STATUS_GROUPS = "CVE_STATUS_WIN CVE_STATUS_PATCHED" CVE_STATUS_WIN = "CVE-1234-0001 CVE-1234-0002" CVE_STATUS_WIN[status] = "Ignored" CVE_STATUS_DETAIL[detail] = "not-applicable-platform" CVE_STATUS_WIN[description] = "Issue only applies on Windows" CVE_STATUS_PATCHED = "CVE-1234-0003 CVE-1234-0004" CVE_STATUS_PATCHED[status] = "Patched" CVE_STATUS_DETAIL[detail] = "fixed-version" CVE_STATUS_PATCHED[description] = "Fixed externally" Signed-off-by: Andrej Valek <[email protected]> Signed-off-by: Peter Marko <[email protected]> --- meta/classes/cve-check.bbclass | 89 +++++++++++++++++++++++++++++----- meta/lib/oe/cve_check.py | 6 +++ 2 files changed, 83 insertions(+), 12 deletions(-) diff --git a/meta/classes/cve-check.bbclass b/meta/classes/cve-check.bbclass index bd9e7e7445..62676ba5bc 100644 --- a/meta/classes/cve-check.bbclass +++ b/meta/classes/cve-check.bbclass @@ -70,12 +70,16 @@ 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: +# Replace NVD DB check status for a given CVE. Each of CVE has to be mentioned +# separately with optional detail and description for this status. # -# CVE_CHECK_IGNORE = 'CVE-2014-2524 CVE-2018-1234' +# CVE_STATUS[CVE-1234-0001] = "Ignored" # or "Patched" or "Unpatched" +# CVE_STATUS[CVE-1234-0002] = "Ignored" +# CVE_STATUS_DETAIL[CVE-1234-0002] = "not-applicable-platform" +# CVE_STATUS_DESCRIPTION[CVE-1234-0002] = "Issue only applies on Windows" # +# CVE_CHECK_IGNORE is deprecated and CVE_STATUS has to be used instead. +# Keep CVE_CHECK_IGNORE until other layers migrate to new variables CVE_CHECK_IGNORE ?= "" # Layers to be excluded @@ -88,6 +92,47 @@ 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 + cve_check_ignore = d.getVar("CVE_CHECK_IGNORE") + if cve_check_ignore: + bb.warn("CVE_CHECK_IGNORE is deprecated in favor of CVE_STATUS") + set_cves_statuses(d, d.getVar("CVE_CHECK_IGNORE"), "Ignored") + + # Process CVE_STATUS_GROUPS to set multiple statuses and optional detail or description at once + for cve_status_group in (d.getVar("CVE_STATUS_GROUPS") or "").split(): + cve_group = d.getVar(cve_status_group) + if cve_group is not None: + set_cves_statuses(d, cve_group, + d.getVarFlag(cve_status_group, "status"), + d.getVarFlag(cve_status_group, "detail"), + d.getVarFlag(cve_status_group, "description")) + else: + bb.warn("CVE_STATUS_GROUPS contains undefined variable %s" % cve_status_group) +} + +def set_cves_statuses(d, cves, status, detail="", description=""): + for cve in cves.split(): + d.setVarFlag("CVE_STATUS", cve, status) + d.setVarFlag("CVE_STATUS_DETAIL", cve, detail) + d.setVarFlag("CVE_STATUS_DESCRIPTION", cve, description) + +def get_cve_detail(d, cve, status): + detail = d.getVarFlag("CVE_STATUS_DETAIL", cve) + if detail is not None: + if status == "Patched": + if detail in ["fixed-version", "backported-patch", "cpe-stable-backport", "other"]: + return detail + elif status == "Unpatched": + if detail in ["vulnerable-investigating", "other"]: + return detail + else: + if detail in ["cpe-incorrect", "not-applicable-platform", "upstream-wontfix", + "not-applicable-config", "not-affected", "other"]: + return detail + bb.warn('Invalid detail %s for CVE_STATUS[%s] = "%s"' % (detail, cve, status)) + return "" + def generate_json_report(d, out_path, link_path): if os.path.exists(d.getVar("CVE_CHECK_SUMMARY_INDEX_PATH")): import json @@ -282,7 +327,13 @@ 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 and check validity + cve_ignore = [] + for cve, status in (d.getVarFlags("CVE_STATUS") or {}).items(): + if status == "Ignored": + cve_ignore.append(cve) + elif status not in ["Patched", "Unpatched"]: + bb.error("Unsupported status %s in CVE_STATUS[%s]" % (status, cve)) import sqlite3 db_file = d.expand("file:${CVE_CHECK_DB_FILE}?mode=ro") @@ -441,20 +492,28 @@ def cve_write_data_text(d, patched, unpatched, ignored, cve_data): is_patched = cve in patched is_ignored = cve in ignored + status = "Unpatched" if (is_patched or is_ignored) and not report_all: 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 - if is_ignored: - write_string += "CVE STATUS: Ignored\n" - elif is_patched: - write_string += "CVE STATUS: Patched\n" - else: - unpatched_cves.append(cve) - write_string += "CVE STATUS: Unpatched\n" + write_string += "CVE STATUS: %s\n" % status + detail = get_cve_detail(d, cve, status) + if detail: + write_string += "CVE DETAIL: %s\n" % detail + description = d.getVarFlag("CVE_STATUS_DESCRIPTION", cve) + if description: + write_string += "CVE DESCRIPTION: %s\n" % 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"] @@ -576,6 +635,12 @@ def cve_write_data_json(d, patched, unpatched, ignored, cve_data, cve_status): "status" : status, "link": issue_link } + detail = get_cve_detail(d, cve, status) + if detail: + cve_item["detail"] = detail + description = d.getVarFlag("CVE_STATUS_DESCRIPTION", cve) + if description: + cve_item["description"] = 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 dbaa0b373a..f47dd9920e 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 (#182665): https://lists.openembedded.org/g/openembedded-core/message/182665 Mute This Topic: https://lists.openembedded.org/mt/99481049/21656 Group Owner: [email protected] Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [[email protected]] -=-=-=-=-=-=-=-=-=-=-=-
