summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarta Rybczynska <rybczynska@gmail.com>2022-06-23 19:42:29 +0200
committerRichard Purdie <richard.purdie@linuxfoundation.org>2022-07-08 08:27:20 +0100
commit6e79d96c6ded192e0a51d8ceed67b772a7b650c9 (patch)
treea7ae0d61bfb0992e7c977a40a80e820a8a3c334e
parent31b4392e6e9546cb76346edaed6c938fc578e370 (diff)
downloadpoky-6e79d96c6ded192e0a51d8ceed67b772a7b650c9.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: 14b3c0ca46a0aa97565a24b7a5116306237d7cfe) 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> (cherry-picked from c773102d4828fc4ddd1024f6115d577e23f1afe4) Signed-off-by: Steve Sakoman <steve@sakoman.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r--meta/classes/cve-check.bbclass41
1 files changed, 29 insertions, 12 deletions
diff --git a/meta/classes/cve-check.bbclass b/meta/classes/cve-check.bbclass
index 894cebaaa4..d0f6970db8 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/Whitelisted 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
@@ -142,7 +144,7 @@ python do_cve_check () {
142 bb.fatal("Failure in searching patches") 144 bb.fatal("Failure in searching patches")
143 whitelisted, patched, unpatched, status = check_cves(d, patched_cves) 145 whitelisted, patched, unpatched, status = check_cves(d, patched_cves)
144 if patched or unpatched or (d.getVar("CVE_CHECK_COVERAGE") == "1" and status): 146 if patched or unpatched or (d.getVar("CVE_CHECK_COVERAGE") == "1" and status):
145 cve_data = get_cve_info(d, patched + unpatched) 147 cve_data = get_cve_info(d, patched + unpatched + whitelisted)
146 cve_write_data(d, patched, unpatched, whitelisted, cve_data, status) 148 cve_write_data(d, patched, unpatched, whitelisted, cve_data, status)
147 else: 149 else:
148 bb.note("No CVE database found, skipping CVE check") 150 bb.note("No CVE database found, skipping CVE check")
@@ -315,6 +317,7 @@ def check_cves(d, patched_cves):
315 suffix = d.getVar("CVE_VERSION_SUFFIX") 317 suffix = d.getVar("CVE_VERSION_SUFFIX")
316 318
317 cves_unpatched = [] 319 cves_unpatched = []
320 cves_ignored = []
318 cves_status = [] 321 cves_status = []
319 cves_in_recipe = False 322 cves_in_recipe = False
320 # CVE_PRODUCT can contain more than one product (eg. curl/libcurl) 323 # CVE_PRODUCT can contain more than one product (eg. curl/libcurl)
@@ -349,8 +352,7 @@ def check_cves(d, patched_cves):
349 352
350 if cve in cve_whitelist: 353 if cve in cve_whitelist:
351 bb.note("%s-%s has been whitelisted for %s" % (product, pv, cve)) 354 bb.note("%s-%s has been whitelisted for %s" % (product, pv, cve))
352 # TODO: this should be in the report as 'whitelisted' 355 cves_ignored.append(cve)
353 patched_cves.add(cve)
354 continue 356 continue
355 elif cve in patched_cves: 357 elif cve in patched_cves:
356 bb.note("%s has been patched" % (cve)) 358 bb.note("%s has been patched" % (cve))
@@ -362,9 +364,13 @@ def check_cves(d, patched_cves):
362 cves_in_recipe = True 364 cves_in_recipe = True
363 365
364 vulnerable = False 366 vulnerable = False
367 ignored = False
368
365 for row in conn.execute("SELECT * FROM PRODUCTS WHERE ID IS ? AND PRODUCT IS ? AND VENDOR LIKE ?", (cve, product, vendor)): 369 for row in conn.execute("SELECT * FROM PRODUCTS WHERE ID IS ? AND PRODUCT IS ? AND VENDOR LIKE ?", (cve, product, vendor)):
366 (_, _, _, version_start, operator_start, version_end, operator_end) = row 370 (_, _, _, version_start, operator_start, version_end, operator_end) = row
367 #bb.debug(2, "Evaluating row " + str(row)) 371 #bb.debug(2, "Evaluating row " + str(row))
372 if cve in cve_whitelist:
373 ignored = True
368 374
369 if (operator_start == '=' and pv == version_start) or version_start == '-': 375 if (operator_start == '=' and pv == version_start) or version_start == '-':
370 vulnerable = True 376 vulnerable = True
@@ -397,13 +403,16 @@ def check_cves(d, patched_cves):
397 vulnerable = vulnerable_start or vulnerable_end 403 vulnerable = vulnerable_start or vulnerable_end
398 404
399 if vulnerable: 405 if vulnerable:
400 bb.note("%s-%s is vulnerable to %s" % (pn, real_pv, cve)) 406 if ignored:
401 cves_unpatched.append(cve) 407 bb.note("%s is ignored in %s-%s" % (cve, pn, real_pv))
408 cves_ignored.append(cve)
409 else:
410 bb.note("%s-%s is vulnerable to %s" % (pn, real_pv, cve))
411 cves_unpatched.append(cve)
402 break 412 break
403 413
404 if not vulnerable: 414 if not vulnerable:
405 bb.note("%s-%s is not vulnerable to %s" % (pn, real_pv, cve)) 415 bb.note("%s-%s is not vulnerable to %s" % (pn, real_pv, cve))
406 # TODO: not patched but not vulnerable
407 patched_cves.add(cve) 416 patched_cves.add(cve)
408 417
409 if not cves_in_product: 418 if not cves_in_product:
@@ -412,7 +421,7 @@ def check_cves(d, patched_cves):
412 421
413 conn.close() 422 conn.close()
414 423
415 return (list(cve_whitelist), list(patched_cves), cves_unpatched, cves_status) 424 return (list(cves_ignored), list(patched_cves), cves_unpatched, cves_status)
416 425
417def get_cve_info(d, cves): 426def get_cve_info(d, cves):
418 """ 427 """
@@ -450,6 +459,8 @@ def cve_write_data_text(d, patched, unpatched, whitelisted, cve_data):
450 include_layers = d.getVar("CVE_CHECK_LAYER_INCLUDELIST").split() 459 include_layers = d.getVar("CVE_CHECK_LAYER_INCLUDELIST").split()
451 exclude_layers = d.getVar("CVE_CHECK_LAYER_EXCLUDELIST").split() 460 exclude_layers = d.getVar("CVE_CHECK_LAYER_EXCLUDELIST").split()
452 461
462 report_all = d.getVar("CVE_CHECK_REPORT_PATCHED") == "1"
463
453 if exclude_layers and layer in exclude_layers: 464 if exclude_layers and layer in exclude_layers:
454 return 465 return
455 466
@@ -457,7 +468,7 @@ def cve_write_data_text(d, patched, unpatched, whitelisted, cve_data):
457 return 468 return
458 469
459 # Early exit, the text format does not report packages without CVEs 470 # Early exit, the text format does not report packages without CVEs
460 if not patched+unpatched: 471 if not patched+unpatched+whitelisted:
461 return 472 return
462 473
463 nvd_link = "https://nvd.nist.gov/vuln/detail/" 474 nvd_link = "https://nvd.nist.gov/vuln/detail/"
@@ -467,13 +478,16 @@ def cve_write_data_text(d, patched, unpatched, whitelisted, cve_data):
467 478
468 for cve in sorted(cve_data): 479 for cve in sorted(cve_data):
469 is_patched = cve in patched 480 is_patched = cve in patched
470 if is_patched and (d.getVar("CVE_CHECK_REPORT_PATCHED") != "1"): 481 is_ignored = cve in whitelisted
482
483 if (is_patched or is_ignored) and not report_all:
471 continue 484 continue
485
472 write_string += "LAYER: %s\n" % layer 486 write_string += "LAYER: %s\n" % layer
473 write_string += "PACKAGE NAME: %s\n" % d.getVar("PN") 487 write_string += "PACKAGE NAME: %s\n" % d.getVar("PN")
474 write_string += "PACKAGE VERSION: %s%s\n" % (d.getVar("EXTENDPE"), d.getVar("PV")) 488 write_string += "PACKAGE VERSION: %s%s\n" % (d.getVar("EXTENDPE"), d.getVar("PV"))
475 write_string += "CVE: %s\n" % cve 489 write_string += "CVE: %s\n" % cve
476 if cve in whitelisted: 490 if is_ignored:
477 write_string += "CVE STATUS: Whitelisted\n" 491 write_string += "CVE STATUS: Whitelisted\n"
478 elif is_patched: 492 elif is_patched:
479 write_string += "CVE STATUS: Patched\n" 493 write_string += "CVE STATUS: Patched\n"
@@ -550,6 +564,8 @@ def cve_write_data_json(d, patched, unpatched, ignored, cve_data, cve_status):
550 include_layers = d.getVar("CVE_CHECK_LAYER_INCLUDELIST").split() 564 include_layers = d.getVar("CVE_CHECK_LAYER_INCLUDELIST").split()
551 exclude_layers = d.getVar("CVE_CHECK_LAYER_EXCLUDELIST").split() 565 exclude_layers = d.getVar("CVE_CHECK_LAYER_EXCLUDELIST").split()
552 566
567 report_all = d.getVar("CVE_CHECK_REPORT_PATCHED") == "1"
568
553 if exclude_layers and layer in exclude_layers: 569 if exclude_layers and layer in exclude_layers:
554 return 570 return
555 571
@@ -576,10 +592,11 @@ def cve_write_data_json(d, patched, unpatched, ignored, cve_data, cve_status):
576 592
577 for cve in sorted(cve_data): 593 for cve in sorted(cve_data):
578 is_patched = cve in patched 594 is_patched = cve in patched
595 is_ignored = cve in ignored
579 status = "Unpatched" 596 status = "Unpatched"
580 if is_patched and (d.getVar("CVE_CHECK_REPORT_PATCHED") != "1"): 597 if (is_patched or is_ignored) and not report_all:
581 continue 598 continue
582 if cve in ignored: 599 if is_ignored:
583 status = "Ignored" 600 status = "Ignored"
584 elif is_patched: 601 elif is_patched:
585 status = "Patched" 602 status = "Patched"