diff options
Diffstat (limited to 'meta/classes/cve-check.bbclass')
-rw-r--r-- | meta/classes/cve-check.bbclass | 41 |
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 | |||
47 | CVE_CHECK_COPY_FILES ??= "1" | 47 | CVE_CHECK_COPY_FILES ??= "1" |
48 | CVE_CHECK_CREATE_MANIFEST ??= "1" | 48 | CVE_CHECK_CREATE_MANIFEST ??= "1" |
49 | 49 | ||
50 | # Report Patched or Ignored/Whitelisted CVEs | ||
50 | CVE_CHECK_REPORT_PATCHED ??= "1" | 51 | CVE_CHECK_REPORT_PATCHED ??= "1" |
52 | |||
51 | CVE_CHECK_SHOW_WARNINGS ??= "1" | 53 | CVE_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 | ||
417 | def get_cve_info(d, cves): | 426 | def 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" |