summaryrefslogtreecommitdiffstats
path: root/meta/classes
diff options
context:
space:
mode:
authorAndrej Valek <andrej.valek@siemens.com>2023-06-23 13:14:56 +0200
committerRichard Purdie <richard.purdie@linuxfoundation.org>2023-07-19 23:25:01 +0100
commitbe9883a92bad0fe4c1e9c7302c93dea4ac680f8c (patch)
tree6d9d35acbb91f98016956168b4ea90f9b9ce0764 /meta/classes
parentebb8b39463cef3c3d0f90f054c433b2f5256cb1a (diff)
downloadpoky-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.bbclass81
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)
71CVE_CHECK_SKIP_RECIPE ?= "" 71CVE_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
79CVE_CHECK_IGNORE ?= "" 95CVE_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
89CVE_VERSION_SUFFIX ??= "" 105CVE_VERSION_SUFFIX ??= ""
90 106
107python () {
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
91def generate_json_report(d, out_path, link_path): 125def 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