diff options
author | Marta Rybczynska <rybczynska@gmail.com> | 2022-06-15 15:20:11 +0200 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2022-06-17 17:57:44 +0100 |
commit | 112299ddae36e51acd802f908e8526cf48c59a49 (patch) | |
tree | 2eb938052138eac1278d80bb1f1daa28a8260f48 /meta | |
parent | bd879982fa551d8d16d1d9d5c5261085509ea926 (diff) | |
download | poky-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')
-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" |