summaryrefslogtreecommitdiffstats
path: root/meta/classes/cve-check.bbclass
diff options
context:
space:
mode:
authorMarta Rybczynska <rybczynska@gmail.com>2022-06-15 15:20:11 +0200
committerRichard Purdie <richard.purdie@linuxfoundation.org>2022-06-17 17:57:44 +0100
commit112299ddae36e51acd802f908e8526cf48c59a49 (patch)
tree2eb938052138eac1278d80bb1f1daa28a8260f48 /meta/classes/cve-check.bbclass
parentbd879982fa551d8d16d1d9d5c5261085509ea926 (diff)
downloadpoky-112299ddae36e51acd802f908e8526cf48c59a49.tar.gz
cve-check: add support for Ignored CVEs
Ignored CVEs aren't patched, but do not apply in our configuration for some reason. Up till now they were only partially supported and reported as "Patched". This patch adds separate reporting of Ignored CVEs. The variable CVE_CHECK_REPORT_PATCHED now manages reporting of both patched and ignored CVEs. (From OE-Core rev: c773102d4828fc4ddd1024f6115d577e23f1afe4) Signed-off-by: Marta Rybczynska <marta.rybczynska@huawei.com> Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/classes/cve-check.bbclass')
-rw-r--r--meta/classes/cve-check.bbclass43
1 files changed, 30 insertions, 13 deletions
diff --git a/meta/classes/cve-check.bbclass b/meta/classes/cve-check.bbclass
index 1b4910f737..50b9247f46 100644
--- a/meta/classes/cve-check.bbclass
+++ b/meta/classes/cve-check.bbclass
@@ -47,7 +47,9 @@ CVE_CHECK_MANIFEST_JSON ?= "${DEPLOY_DIR_IMAGE}/${IMAGE_NAME}${IMAGE_NAME_SUFFIX
47CVE_CHECK_COPY_FILES ??= "1" 47CVE_CHECK_COPY_FILES ??= "1"
48CVE_CHECK_CREATE_MANIFEST ??= "1" 48CVE_CHECK_CREATE_MANIFEST ??= "1"
49 49
50# Report Patched or Ignored CVEs
50CVE_CHECK_REPORT_PATCHED ??= "1" 51CVE_CHECK_REPORT_PATCHED ??= "1"
52
51CVE_CHECK_SHOW_WARNINGS ??= "1" 53CVE_CHECK_SHOW_WARNINGS ??= "1"
52 54
53# Provide text output 55# Provide text output
@@ -144,7 +146,7 @@ python do_cve_check () {
144 bb.fatal("Failure in searching patches") 146 bb.fatal("Failure in searching patches")
145 ignored, patched, unpatched, status = check_cves(d, patched_cves) 147 ignored, patched, unpatched, status = check_cves(d, patched_cves)
146 if patched or unpatched or (d.getVar("CVE_CHECK_COVERAGE") == "1" and status): 148 if patched or unpatched or (d.getVar("CVE_CHECK_COVERAGE") == "1" and status):
147 cve_data = get_cve_info(d, patched + unpatched) 149 cve_data = get_cve_info(d, patched + unpatched + ignored)
148 cve_write_data(d, patched, unpatched, ignored, cve_data, status) 150 cve_write_data(d, patched, unpatched, ignored, cve_data, status)
149 else: 151 else:
150 bb.note("No CVE database found, skipping CVE check") 152 bb.note("No CVE database found, skipping CVE check")
@@ -258,6 +260,7 @@ def check_cves(d, patched_cves):
258 suffix = d.getVar("CVE_VERSION_SUFFIX") 260 suffix = d.getVar("CVE_VERSION_SUFFIX")
259 261
260 cves_unpatched = [] 262 cves_unpatched = []
263 cves_ignored = []
261 cves_status = [] 264 cves_status = []
262 cves_in_recipe = False 265 cves_in_recipe = False
263 # CVE_PRODUCT can contain more than one product (eg. curl/libcurl) 266 # CVE_PRODUCT can contain more than one product (eg. curl/libcurl)
@@ -291,9 +294,8 @@ def check_cves(d, patched_cves):
291 cve = cverow[0] 294 cve = cverow[0]
292 295
293 if cve in cve_ignore: 296 if cve in cve_ignore:
294 bb.note("%s-%s has been ignored for %s" % (product, pv, cve)) 297 bb.note("%s-%s ignores %s" % (product, pv, cve))
295 # TODO: this should be in the report as 'ignored' 298 cves_ignored.append(cve)
296 patched_cves.add(cve)
297 continue 299 continue
298 elif cve in patched_cves: 300 elif cve in patched_cves:
299 bb.note("%s has been patched" % (cve)) 301 bb.note("%s has been patched" % (cve))
@@ -305,9 +307,13 @@ def check_cves(d, patched_cves):
305 cves_in_recipe = True 307 cves_in_recipe = True
306 308
307 vulnerable = False 309 vulnerable = False
310 ignored = False
311
308 for row in conn.execute("SELECT * FROM PRODUCTS WHERE ID IS ? AND PRODUCT IS ? AND VENDOR LIKE ?", (cve, product, vendor)): 312 for row in conn.execute("SELECT * FROM PRODUCTS WHERE ID IS ? AND PRODUCT IS ? AND VENDOR LIKE ?", (cve, product, vendor)):
309 (_, _, _, version_start, operator_start, version_end, operator_end) = row 313 (_, _, _, version_start, operator_start, version_end, operator_end) = row
310 #bb.debug(2, "Evaluating row " + str(row)) 314 #bb.debug(2, "Evaluating row " + str(row))
315 if cve in cve_ignore:
316 ignored = True
311 317
312 if (operator_start == '=' and pv == version_start) or version_start == '-': 318 if (operator_start == '=' and pv == version_start) or version_start == '-':
313 vulnerable = True 319 vulnerable = True
@@ -340,13 +346,16 @@ def check_cves(d, patched_cves):
340 vulnerable = vulnerable_start or vulnerable_end 346 vulnerable = vulnerable_start or vulnerable_end
341 347
342 if vulnerable: 348 if vulnerable:
343 bb.note("%s-%s is vulnerable to %s" % (pn, real_pv, cve)) 349 if ignored:
344 cves_unpatched.append(cve) 350 bb.note("%s is ignored in %s-%s" % (cve, pn, real_pv))
351 cves_ignored.append(cve)
352 else:
353 bb.note("%s-%s is vulnerable to %s" % (pn, real_pv, cve))
354 cves_unpatched.append(cve)
345 break 355 break
346 356
347 if not vulnerable: 357 if not vulnerable:
348 bb.note("%s-%s is not vulnerable to %s" % (pn, real_pv, cve)) 358 bb.note("%s-%s is not vulnerable to %s" % (pn, real_pv, cve))
349 # TODO: not patched but not vulnerable
350 patched_cves.add(cve) 359 patched_cves.add(cve)
351 360
352 if not cves_in_product: 361 if not cves_in_product:
@@ -358,7 +367,7 @@ def check_cves(d, patched_cves):
358 if not cves_in_recipe: 367 if not cves_in_recipe:
359 bb.note("No CVE records for products in recipe %s" % (pn)) 368 bb.note("No CVE records for products in recipe %s" % (pn))
360 369
361 return (list(cve_ignore), list(patched_cves), cves_unpatched, cves_status) 370 return (list(cves_ignored), list(patched_cves), cves_unpatched, cves_status)
362 371
363def get_cve_info(d, cves): 372def get_cve_info(d, cves):
364 """ 373 """
@@ -396,6 +405,8 @@ def cve_write_data_text(d, patched, unpatched, ignored, cve_data):
396 include_layers = d.getVar("CVE_CHECK_LAYER_INCLUDELIST").split() 405 include_layers = d.getVar("CVE_CHECK_LAYER_INCLUDELIST").split()
397 exclude_layers = d.getVar("CVE_CHECK_LAYER_EXCLUDELIST").split() 406 exclude_layers = d.getVar("CVE_CHECK_LAYER_EXCLUDELIST").split()
398 407
408 report_all = d.getVar("CVE_CHECK_REPORT_PATCHED") == "1"
409
399 if exclude_layers and layer in exclude_layers: 410 if exclude_layers and layer in exclude_layers:
400 return 411 return
401 412
@@ -403,7 +414,7 @@ def cve_write_data_text(d, patched, unpatched, ignored, cve_data):
403 return 414 return
404 415
405 # Early exit, the text format does not report packages without CVEs 416 # Early exit, the text format does not report packages without CVEs
406 if not patched+unpatched: 417 if not patched+unpatched+ignored:
407 return 418 return
408 419
409 nvd_link = "https://nvd.nist.gov/vuln/detail/" 420 nvd_link = "https://nvd.nist.gov/vuln/detail/"
@@ -413,13 +424,16 @@ def cve_write_data_text(d, patched, unpatched, ignored, cve_data):
413 424
414 for cve in sorted(cve_data): 425 for cve in sorted(cve_data):
415 is_patched = cve in patched 426 is_patched = cve in patched
416 if is_patched and (d.getVar("CVE_CHECK_REPORT_PATCHED") != "1"): 427 is_ignored = cve in ignored
428
429 if (is_patched or is_ignored) and not report_all:
417 continue 430 continue
431
418 write_string += "LAYER: %s\n" % layer 432 write_string += "LAYER: %s\n" % layer
419 write_string += "PACKAGE NAME: %s\n" % d.getVar("PN") 433 write_string += "PACKAGE NAME: %s\n" % d.getVar("PN")
420 write_string += "PACKAGE VERSION: %s%s\n" % (d.getVar("EXTENDPE"), d.getVar("PV")) 434 write_string += "PACKAGE VERSION: %s%s\n" % (d.getVar("EXTENDPE"), d.getVar("PV"))
421 write_string += "CVE: %s\n" % cve 435 write_string += "CVE: %s\n" % cve
422 if cve in ignored: 436 if is_ignored:
423 write_string += "CVE STATUS: Ignored\n" 437 write_string += "CVE STATUS: Ignored\n"
424 elif is_patched: 438 elif is_patched:
425 write_string += "CVE STATUS: Patched\n" 439 write_string += "CVE STATUS: Patched\n"
@@ -496,6 +510,8 @@ def cve_write_data_json(d, patched, unpatched, ignored, cve_data, cve_status):
496 include_layers = d.getVar("CVE_CHECK_LAYER_INCLUDELIST").split() 510 include_layers = d.getVar("CVE_CHECK_LAYER_INCLUDELIST").split()
497 exclude_layers = d.getVar("CVE_CHECK_LAYER_EXCLUDELIST").split() 511 exclude_layers = d.getVar("CVE_CHECK_LAYER_EXCLUDELIST").split()
498 512
513 report_all = d.getVar("CVE_CHECK_REPORT_PATCHED") == "1"
514
499 if exclude_layers and layer in exclude_layers: 515 if exclude_layers and layer in exclude_layers:
500 return 516 return
501 517
@@ -522,10 +538,11 @@ def cve_write_data_json(d, patched, unpatched, ignored, cve_data, cve_status):
522 538
523 for cve in sorted(cve_data): 539 for cve in sorted(cve_data):
524 is_patched = cve in patched 540 is_patched = cve in patched
541 is_ignored = cve in ignored
525 status = "Unpatched" 542 status = "Unpatched"
526 if is_patched and (d.getVar("CVE_CHECK_REPORT_PATCHED") != "1"): 543 if (is_patched or is_ignored) and not report_all:
527 continue 544 continue
528 if cve in ignored: 545 if is_ignored:
529 status = "Ignored" 546 status = "Ignored"
530 elif is_patched: 547 elif is_patched:
531 status = "Patched" 548 status = "Patched"