diff options
-rw-r--r-- | meta/classes/cve-check.bbclass | 43 |
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 | |||
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 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 |
@@ -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 | ||
363 | def get_cve_info(d, cves): | 372 | def 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" |