diff options
| author | Andrej Valek <andrej.valek@siemens.com> | 2023-06-23 13:14:56 +0200 |
|---|---|---|
| committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2023-07-19 23:25:01 +0100 |
| commit | be9883a92bad0fe4c1e9c7302c93dea4ac680f8c (patch) | |
| tree | 6d9d35acbb91f98016956168b4ea90f9b9ce0764 /meta/classes | |
| parent | ebb8b39463cef3c3d0f90f054c433b2f5256cb1a (diff) | |
| download | poky-be9883a92bad0fe4c1e9c7302c93dea4ac680f8c.tar.gz | |
cve-check: add option to add additional patched CVEs
- Replace CVE_CHECK_IGNORE with CVE_STATUS to be more flexible.
The CVE_STATUS should contain an information about status wich
is decoded in 3 items:
- generic status: "Ignored", "Patched" or "Unpatched"
- more detailed status enum
- description: free text describing reason for status
Examples of usage:
CVE_STATUS[CVE-1234-0001] = "not-applicable-platform: Issue only applies on Windows"
CVE_STATUS[CVE-1234-0002] = "fixed-version: Fixed externally"
CVE_CHECK_STATUSMAP[not-applicable-platform] = "Ignored"
CVE_CHECK_STATUSMAP[fixed-version] = "Patched"
(From OE-Core rev: 34f682a24b7075b12ec308154b937ad118d69fe5)
Signed-off-by: Andrej Valek <andrej.valek@siemens.com>
Signed-off-by: Peter Marko <peter.marko@siemens.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/classes')
| -rw-r--r-- | meta/classes/cve-check.bbclass | 81 |
1 files changed, 68 insertions, 13 deletions
diff --git a/meta/classes/cve-check.bbclass b/meta/classes/cve-check.bbclass index f7abaf4f0c..c1f1ea0fd6 100644 --- a/meta/classes/cve-check.bbclass +++ b/meta/classes/cve-check.bbclass | |||
| @@ -70,12 +70,28 @@ CVE_CHECK_COVERAGE ??= "1" | |||
| 70 | # Skip CVE Check for packages (PN) | 70 | # Skip CVE Check for packages (PN) |
| 71 | CVE_CHECK_SKIP_RECIPE ?= "" | 71 | CVE_CHECK_SKIP_RECIPE ?= "" |
| 72 | 72 | ||
| 73 | # Ingore the check for a given list of CVEs. If a CVE is found, | 73 | # Replace NVD DB check status for a given CVE. Each of CVE has to be mentioned |
| 74 | # then it is considered patched. The value is a string containing | 74 | # separately with optional detail and description for this status. |
| 75 | # space separated CVE values: | ||
| 76 | # | 75 | # |
| 77 | # CVE_CHECK_IGNORE = 'CVE-2014-2524 CVE-2018-1234' | 76 | # CVE_STATUS[CVE-1234-0001] = "not-applicable-platform: Issue only applies on Windows" |
| 77 | # CVE_STATUS[CVE-1234-0002] = "fixed-version: Fixed externally" | ||
| 78 | # | 78 | # |
| 79 | # Settings the same status and reason for multiple CVEs is possible | ||
| 80 | # via CVE_STATUS_GROUPS variable. | ||
| 81 | # | ||
| 82 | # CVE_STATUS_GROUPS = "CVE_STATUS_WIN CVE_STATUS_PATCHED" | ||
| 83 | # | ||
| 84 | # CVE_STATUS_WIN = "CVE-1234-0001 CVE-1234-0003" | ||
| 85 | # CVE_STATUS_WIN[status] = "not-applicable-platform: Issue only applies on Windows" | ||
| 86 | # CVE_STATUS_PATCHED = "CVE-1234-0002 CVE-1234-0004" | ||
| 87 | # CVE_STATUS_PATCHED[status] = "fixed-version: Fixed externally" | ||
| 88 | # | ||
| 89 | # All possible CVE statuses could be found in cve-check-map.conf | ||
| 90 | # CVE_CHECK_STATUSMAP[not-applicable-platform] = "Ignored" | ||
| 91 | # CVE_CHECK_STATUSMAP[fixed-version] = "Patched" | ||
| 92 | # | ||
| 93 | # CVE_CHECK_IGNORE is deprecated and CVE_STATUS has to be used instead. | ||
| 94 | # Keep CVE_CHECK_IGNORE until other layers migrate to new variables | ||
| 79 | CVE_CHECK_IGNORE ?= "" | 95 | CVE_CHECK_IGNORE ?= "" |
| 80 | 96 | ||
| 81 | # Layers to be excluded | 97 | # Layers to be excluded |
| @@ -88,6 +104,24 @@ CVE_CHECK_LAYER_INCLUDELIST ??= "" | |||
| 88 | # set to "alphabetical" for version using single alphabetical character as increment release | 104 | # set to "alphabetical" for version using single alphabetical character as increment release |
| 89 | CVE_VERSION_SUFFIX ??= "" | 105 | CVE_VERSION_SUFFIX ??= "" |
| 90 | 106 | ||
| 107 | python () { | ||
| 108 | # Fallback all CVEs from CVE_CHECK_IGNORE to CVE_STATUS | ||
| 109 | cve_check_ignore = d.getVar("CVE_CHECK_IGNORE") | ||
| 110 | if cve_check_ignore: | ||
| 111 | bb.warn("CVE_CHECK_IGNORE is deprecated in favor of CVE_STATUS") | ||
| 112 | for cve in (d.getVar("CVE_CHECK_IGNORE") or "").split(): | ||
| 113 | d.setVarFlag("CVE_STATUS", cve, "ignored") | ||
| 114 | |||
| 115 | # Process CVE_STATUS_GROUPS to set multiple statuses and optional detail or description at once | ||
| 116 | for cve_status_group in (d.getVar("CVE_STATUS_GROUPS") or "").split(): | ||
| 117 | cve_group = d.getVar(cve_status_group) | ||
| 118 | if cve_group is not None: | ||
| 119 | for cve in cve_group.split(): | ||
| 120 | d.setVarFlag("CVE_STATUS", cve, d.getVarFlag(cve_status_group, "status")) | ||
| 121 | else: | ||
| 122 | bb.warn("CVE_STATUS_GROUPS contains undefined variable %s" % cve_status_group) | ||
| 123 | } | ||
| 124 | |||
| 91 | def generate_json_report(d, out_path, link_path): | 125 | def generate_json_report(d, out_path, link_path): |
| 92 | if os.path.exists(d.getVar("CVE_CHECK_SUMMARY_INDEX_PATH")): | 126 | if os.path.exists(d.getVar("CVE_CHECK_SUMMARY_INDEX_PATH")): |
| 93 | import json | 127 | import json |
| @@ -260,7 +294,7 @@ def check_cves(d, patched_cves): | |||
| 260 | """ | 294 | """ |
| 261 | Connect to the NVD database and find unpatched cves. | 295 | Connect to the NVD database and find unpatched cves. |
| 262 | """ | 296 | """ |
| 263 | from oe.cve_check import Version, convert_cve_version | 297 | from oe.cve_check import Version, convert_cve_version, decode_cve_status |
| 264 | 298 | ||
| 265 | pn = d.getVar("PN") | 299 | pn = d.getVar("PN") |
| 266 | real_pv = d.getVar("PV") | 300 | real_pv = d.getVar("PV") |
| @@ -282,7 +316,12 @@ def check_cves(d, patched_cves): | |||
| 282 | bb.note("Recipe has been skipped by cve-check") | 316 | bb.note("Recipe has been skipped by cve-check") |
| 283 | return ([], [], [], []) | 317 | return ([], [], [], []) |
| 284 | 318 | ||
| 285 | cve_ignore = d.getVar("CVE_CHECK_IGNORE").split() | 319 | # Convert CVE_STATUS into ignored CVEs and check validity |
| 320 | cve_ignore = [] | ||
| 321 | for cve in (d.getVarFlags("CVE_STATUS") or {}): | ||
| 322 | decoded_status, _, _ = decode_cve_status(d, cve) | ||
| 323 | if decoded_status == "Ignored": | ||
| 324 | cve_ignore.append(cve) | ||
| 286 | 325 | ||
| 287 | import sqlite3 | 326 | import sqlite3 |
| 288 | db_file = d.expand("file:${CVE_CHECK_DB_FILE}?mode=ro") | 327 | db_file = d.expand("file:${CVE_CHECK_DB_FILE}?mode=ro") |
| @@ -413,6 +452,8 @@ def cve_write_data_text(d, patched, unpatched, ignored, cve_data): | |||
| 413 | CVE manifest if enabled. | 452 | CVE manifest if enabled. |
| 414 | """ | 453 | """ |
| 415 | 454 | ||
| 455 | from oe.cve_check import decode_cve_status | ||
| 456 | |||
| 416 | cve_file = d.getVar("CVE_CHECK_LOG") | 457 | cve_file = d.getVar("CVE_CHECK_LOG") |
| 417 | fdir_name = d.getVar("FILE_DIRNAME") | 458 | fdir_name = d.getVar("FILE_DIRNAME") |
| 418 | layer = fdir_name.split("/")[-3] | 459 | layer = fdir_name.split("/")[-3] |
| @@ -441,20 +482,27 @@ def cve_write_data_text(d, patched, unpatched, ignored, cve_data): | |||
| 441 | is_patched = cve in patched | 482 | is_patched = cve in patched |
| 442 | is_ignored = cve in ignored | 483 | is_ignored = cve in ignored |
| 443 | 484 | ||
| 485 | status = "Unpatched" | ||
| 444 | if (is_patched or is_ignored) and not report_all: | 486 | if (is_patched or is_ignored) and not report_all: |
| 445 | continue | 487 | continue |
| 488 | if is_ignored: | ||
| 489 | status = "Ignored" | ||
| 490 | elif is_patched: | ||
| 491 | status = "Patched" | ||
| 492 | else: | ||
| 493 | # default value of status is Unpatched | ||
| 494 | unpatched_cves.append(cve) | ||
| 446 | 495 | ||
| 447 | write_string += "LAYER: %s\n" % layer | 496 | write_string += "LAYER: %s\n" % layer |
| 448 | write_string += "PACKAGE NAME: %s\n" % d.getVar("PN") | 497 | write_string += "PACKAGE NAME: %s\n" % d.getVar("PN") |
| 449 | write_string += "PACKAGE VERSION: %s%s\n" % (d.getVar("EXTENDPE"), d.getVar("PV")) | 498 | write_string += "PACKAGE VERSION: %s%s\n" % (d.getVar("EXTENDPE"), d.getVar("PV")) |
| 450 | write_string += "CVE: %s\n" % cve | 499 | write_string += "CVE: %s\n" % cve |
| 451 | if is_ignored: | 500 | write_string += "CVE STATUS: %s\n" % status |
| 452 | write_string += "CVE STATUS: Ignored\n" | 501 | _, detail, description = decode_cve_status(d, cve) |
| 453 | elif is_patched: | 502 | if detail: |
| 454 | write_string += "CVE STATUS: Patched\n" | 503 | write_string += "CVE DETAIL: %s\n" % detail |
| 455 | else: | 504 | if description: |
| 456 | unpatched_cves.append(cve) | 505 | write_string += "CVE DESCRIPTION: %s\n" % description |
| 457 | write_string += "CVE STATUS: Unpatched\n" | ||
| 458 | write_string += "CVE SUMMARY: %s\n" % cve_data[cve]["summary"] | 506 | write_string += "CVE SUMMARY: %s\n" % cve_data[cve]["summary"] |
| 459 | write_string += "CVSS v2 BASE SCORE: %s\n" % cve_data[cve]["scorev2"] | 507 | write_string += "CVSS v2 BASE SCORE: %s\n" % cve_data[cve]["scorev2"] |
| 460 | write_string += "CVSS v3 BASE SCORE: %s\n" % cve_data[cve]["scorev3"] | 508 | write_string += "CVSS v3 BASE SCORE: %s\n" % cve_data[cve]["scorev3"] |
| @@ -516,6 +564,8 @@ def cve_write_data_json(d, patched, unpatched, ignored, cve_data, cve_status): | |||
| 516 | Prepare CVE data for the JSON format, then write it. | 564 | Prepare CVE data for the JSON format, then write it. |
| 517 | """ | 565 | """ |
| 518 | 566 | ||
| 567 | from oe.cve_check import decode_cve_status | ||
| 568 | |||
| 519 | output = {"version":"1", "package": []} | 569 | output = {"version":"1", "package": []} |
| 520 | nvd_link = "https://nvd.nist.gov/vuln/detail/" | 570 | nvd_link = "https://nvd.nist.gov/vuln/detail/" |
| 521 | 571 | ||
| @@ -576,6 +626,11 @@ def cve_write_data_json(d, patched, unpatched, ignored, cve_data, cve_status): | |||
| 576 | "status" : status, | 626 | "status" : status, |
| 577 | "link": issue_link | 627 | "link": issue_link |
| 578 | } | 628 | } |
| 629 | _, detail, description = decode_cve_status(d, cve) | ||
| 630 | if detail: | ||
| 631 | cve_item["detail"] = detail | ||
| 632 | if description: | ||
| 633 | cve_item["description"] = description | ||
| 579 | cve_list.append(cve_item) | 634 | cve_list.append(cve_item) |
| 580 | 635 | ||
| 581 | package_data["issue"] = cve_list | 636 | package_data["issue"] = cve_list |
