diff options
author | Marta Rybczynska <rybczynska@gmail.com> | 2024-08-14 07:30:35 +0200 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2024-08-20 14:12:40 +0100 |
commit | bf34db143956294d64998beb3a83f46c1e39d9d9 (patch) | |
tree | ca71d629cab42168c69584c132ea362bb790c4db /meta | |
parent | 326b4303eaf198b7a463d3e6e5037565c22e4823 (diff) | |
download | poky-bf34db143956294d64998beb3a83f46c1e39d9d9.tar.gz |
cve-check: encode affected product/vendor in CVE_STATUS
CVE_STATUS contains assesment of a given CVE, but until now it didn't have
include the affected vendor/product. In the case of a global system include,
that CVE_STATUS was visible in all recipes.
This patch allows encoding of affected product/vendor to each CVE_STATUS
assessment, also for groups. We can then filter them later and use only
CVEs that correspond to the recipe.
This is going to be used in meta/conf/distro/include/cve-extra-exclusions.inc
and similar places.
(From OE-Core rev: abca80a716e92fc18d3085aba1a15f4bac72379c)
Signed-off-by: Marta Rybczynska <marta.rybczynska@syslinbit.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta')
-rw-r--r-- | meta/classes/cve-check.bbclass | 24 | ||||
-rw-r--r-- | meta/lib/oe/cve_check.py | 39 | ||||
-rw-r--r-- | meta/lib/oe/spdx30_tasks.py | 11 |
3 files changed, 48 insertions, 26 deletions
diff --git a/meta/classes/cve-check.bbclass b/meta/classes/cve-check.bbclass index c946de29a4..bc35a1c53c 100644 --- a/meta/classes/cve-check.bbclass +++ b/meta/classes/cve-check.bbclass | |||
@@ -324,8 +324,8 @@ def check_cves(d, patched_cves): | |||
324 | # Convert CVE_STATUS into ignored CVEs and check validity | 324 | # Convert CVE_STATUS into ignored CVEs and check validity |
325 | cve_ignore = [] | 325 | cve_ignore = [] |
326 | for cve in (d.getVarFlags("CVE_STATUS") or {}): | 326 | for cve in (d.getVarFlags("CVE_STATUS") or {}): |
327 | decoded_status, _, _ = decode_cve_status(d, cve) | 327 | decoded_status = decode_cve_status(d, cve) |
328 | if decoded_status == "Ignored": | 328 | if 'mapping' in decoded_status and decoded_status['mapping'] == "Ignored": |
329 | cve_ignore.append(cve) | 329 | cve_ignore.append(cve) |
330 | 330 | ||
331 | import sqlite3 | 331 | import sqlite3 |
@@ -507,11 +507,11 @@ def cve_write_data_text(d, patched, unpatched, ignored, cve_data): | |||
507 | write_string += "PACKAGE VERSION: %s%s\n" % (d.getVar("EXTENDPE"), d.getVar("PV")) | 507 | write_string += "PACKAGE VERSION: %s%s\n" % (d.getVar("EXTENDPE"), d.getVar("PV")) |
508 | write_string += "CVE: %s\n" % cve | 508 | write_string += "CVE: %s\n" % cve |
509 | write_string += "CVE STATUS: %s\n" % status | 509 | write_string += "CVE STATUS: %s\n" % status |
510 | _, detail, description = decode_cve_status(d, cve) | 510 | status_details = decode_cve_status(d, cve) |
511 | if detail: | 511 | if 'detail' in status_details: |
512 | write_string += "CVE DETAIL: %s\n" % detail | 512 | write_string += "CVE DETAIL: %s\n" % status_details['detail'] |
513 | if description: | 513 | if 'description' in status_details: |
514 | write_string += "CVE DESCRIPTION: %s\n" % description | 514 | write_string += "CVE DESCRIPTION: %s\n" % status_details['description'] |
515 | write_string += "CVE SUMMARY: %s\n" % cve_data[cve]["summary"] | 515 | write_string += "CVE SUMMARY: %s\n" % cve_data[cve]["summary"] |
516 | write_string += "CVSS v2 BASE SCORE: %s\n" % cve_data[cve]["scorev2"] | 516 | write_string += "CVSS v2 BASE SCORE: %s\n" % cve_data[cve]["scorev2"] |
517 | write_string += "CVSS v3 BASE SCORE: %s\n" % cve_data[cve]["scorev3"] | 517 | write_string += "CVSS v3 BASE SCORE: %s\n" % cve_data[cve]["scorev3"] |
@@ -637,11 +637,11 @@ def cve_write_data_json(d, patched, unpatched, ignored, cve_data, cve_status): | |||
637 | "status" : status, | 637 | "status" : status, |
638 | "link": issue_link | 638 | "link": issue_link |
639 | } | 639 | } |
640 | _, detail, description = decode_cve_status(d, cve) | 640 | status_details = decode_cve_status(d, cve) |
641 | if detail: | 641 | if 'detail' in status_details: |
642 | cve_item["detail"] = detail | 642 | cve_item["detail"] = status_details['detail'] |
643 | if description: | 643 | if 'description' in status_details: |
644 | cve_item["description"] = description | 644 | cve_item["description"] = status_details['description'] |
645 | cve_list.append(cve_item) | 645 | cve_list.append(cve_item) |
646 | 646 | ||
647 | package_data["issue"] = cve_list | 647 | package_data["issue"] = cve_list |
diff --git a/meta/lib/oe/cve_check.py b/meta/lib/oe/cve_check.py index ed5c714cb8..5edd34a2d9 100644 --- a/meta/lib/oe/cve_check.py +++ b/meta/lib/oe/cve_check.py | |||
@@ -132,8 +132,8 @@ def get_patched_cves(d): | |||
132 | 132 | ||
133 | # Search for additional patched CVEs | 133 | # Search for additional patched CVEs |
134 | for cve in (d.getVarFlags("CVE_STATUS") or {}): | 134 | for cve in (d.getVarFlags("CVE_STATUS") or {}): |
135 | decoded_status, _, _ = decode_cve_status(d, cve) | 135 | decoded_status = decode_cve_status(d, cve) |
136 | if decoded_status == "Patched": | 136 | if 'mapping' in decoded_status and decoded_status['mapping'] == "Patched": |
137 | bb.debug(2, "CVE %s is additionally patched" % cve) | 137 | bb.debug(2, "CVE %s is additionally patched" % cve) |
138 | patched_cves.add(cve) | 138 | patched_cves.add(cve) |
139 | 139 | ||
@@ -227,19 +227,40 @@ def convert_cve_version(version): | |||
227 | 227 | ||
228 | def decode_cve_status(d, cve): | 228 | def decode_cve_status(d, cve): |
229 | """ | 229 | """ |
230 | Convert CVE_STATUS into status, detail and description. | 230 | Convert CVE_STATUS into status, vendor, product, detail and description. |
231 | """ | 231 | """ |
232 | status = d.getVarFlag("CVE_STATUS", cve) | 232 | status = d.getVarFlag("CVE_STATUS", cve) |
233 | if not status: | 233 | if not status: |
234 | return ("", "", "") | 234 | return {} |
235 | |||
236 | status_split = status.split(':', 5) | ||
237 | status_out = {} | ||
238 | status_out["detail"] = status_split[0] | ||
239 | product = "*" | ||
240 | vendor = "*" | ||
241 | description = "" | ||
242 | if len(status_split) >= 4 and status_split[1].strip() == "cpe": | ||
243 | # Both vendor and product are mandatory if cpe: present, the syntax is then: | ||
244 | # detail: cpe:vendor:product:description | ||
245 | vendor = status_split[2].strip() | ||
246 | product = status_split[3].strip() | ||
247 | description = status_split[4].strip() | ||
248 | elif len(status_split) >= 2 and status_split[1].strip() == "cpe": | ||
249 | # Malformed CPE | ||
250 | bb.warn('Invalid CPE information for CVE_STATUS[%s] = "%s", not setting CPE' % (detail, cve, status)) | ||
251 | else: | ||
252 | # Other case: no CPE, the syntax is then: | ||
253 | # detail: description | ||
254 | description = status_split[len(status_split)-1].strip() if (len(status_split) > 1) else "" | ||
235 | 255 | ||
236 | status_split = status.split(':', 1) | 256 | status_out["vendor"] = vendor |
237 | detail = status_split[0] | 257 | status_out["product"] = product |
238 | description = status_split[1].strip() if (len(status_split) > 1) else "" | 258 | status_out["description"] = description |
239 | 259 | ||
240 | status_mapping = d.getVarFlag("CVE_CHECK_STATUSMAP", detail) | 260 | status_mapping = d.getVarFlag("CVE_CHECK_STATUSMAP", status_out['detail']) |
241 | if status_mapping is None: | 261 | if status_mapping is None: |
242 | bb.warn('Invalid detail "%s" for CVE_STATUS[%s] = "%s", fallback to Unpatched' % (detail, cve, status)) | 262 | bb.warn('Invalid detail "%s" for CVE_STATUS[%s] = "%s", fallback to Unpatched' % (detail, cve, status)) |
243 | status_mapping = "Unpatched" | 263 | status_mapping = "Unpatched" |
264 | status_out["mapping"] = status_mapping | ||
244 | 265 | ||
245 | return (status_mapping, detail, description) | 266 | return status_out |
diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py index 03dc47db02..4864d6252a 100644 --- a/meta/lib/oe/spdx30_tasks.py +++ b/meta/lib/oe/spdx30_tasks.py | |||
@@ -488,21 +488,22 @@ def create_spdx(d): | |||
488 | cve_by_status = {} | 488 | cve_by_status = {} |
489 | if include_vex != "none": | 489 | if include_vex != "none": |
490 | for cve in d.getVarFlags("CVE_STATUS") or {}: | 490 | for cve in d.getVarFlags("CVE_STATUS") or {}: |
491 | status, detail, description = oe.cve_check.decode_cve_status(d, cve) | 491 | decoded_status = oe.cve_check.decode_cve_status(d, cve) |
492 | 492 | ||
493 | # If this CVE is fixed upstream, skip it unless all CVEs are | 493 | # If this CVE is fixed upstream, skip it unless all CVEs are |
494 | # specified. | 494 | # specified. |
495 | if include_vex != "all" and detail in ( | 495 | if include_vex != "all" and 'detail' in decoded_status and \ |
496 | decoded_status['detail'] in ( | ||
496 | "fixed-version", | 497 | "fixed-version", |
497 | "cpe-stable-backport", | 498 | "cpe-stable-backport", |
498 | ): | 499 | ): |
499 | bb.debug(1, "Skipping %s since it is already fixed upstream" % cve) | 500 | bb.debug(1, "Skipping %s since it is already fixed upstream" % cve) |
500 | continue | 501 | continue |
501 | 502 | ||
502 | cve_by_status.setdefault(status, {})[cve] = ( | 503 | cve_by_status.setdefault(decoded_status['mapping'], {})[cve] = ( |
503 | build_objset.new_cve_vuln(cve), | 504 | build_objset.new_cve_vuln(cve), |
504 | detail, | 505 | decoded_status['detail'], |
505 | description, | 506 | decoded_status['description'], |
506 | ) | 507 | ) |
507 | 508 | ||
508 | cpe_ids = oe.cve_check.get_cpe_ids(d.getVar("CVE_PRODUCT"), d.getVar("CVE_VERSION")) | 509 | cpe_ids = oe.cve_check.get_cpe_ids(d.getVar("CVE_PRODUCT"), d.getVar("CVE_VERSION")) |