summaryrefslogtreecommitdiffstats
path: root/meta/lib
diff options
context:
space:
mode:
authorMarta Rybczynska <rybczynska@gmail.com>2024-08-14 07:30:37 +0200
committerRichard Purdie <richard.purdie@linuxfoundation.org>2024-08-20 14:12:40 +0100
commitfb3f440b7d808d4e29b6ab90e75313d5cf516c36 (patch)
treed11a4884bc55f516c2e2dc2c139998b5cdd039a7 /meta/lib
parentebc872441686e09708a23b0ee1d6d865481fbc09 (diff)
downloadpoky-fb3f440b7d808d4e29b6ab90e75313d5cf516c36.tar.gz
cve-check: annotate CVEs during analysis
Add status information for each CVE under analysis. Previously the information passed between different function of the cve-check class included only tables of patched, unpatched, ignored vulnerabilities and the general status of the recipe. The VEX work requires more information, and we need to pass them between different functions, so that it can be enriched as the analysis progresses. Instead of multiple tables, use a single one with annotations for each CVE encountered. For example, a patched CVE will have: {"abbrev-status": "Patched", "status": "version-not-in-range"} abbrev-status contains the general status (Patched, Unpatched, Ignored and Unknown that will be added in the VEX code) status contains more detailed information that can come from CVE_STATUS and the analysis. Additional fields of the annotation include for example the name of the patch file fixing a given CVE. We also use the annotation in CVE_STATUS to filter out entries that do not apply to the given recipe (From OE-Core rev: 452e605b55ad61c08f4af7089a5a9c576ca28f7d) Signed-off-by: Marta Rybczynska <marta.rybczynska@syslinbit.com> Signed-off-by: Samantha Jalabert <samantha.jalabert@syslinbit.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/lib')
-rw-r--r--meta/lib/oe/cve_check.py35
1 files changed, 29 insertions, 6 deletions
diff --git a/meta/lib/oe/cve_check.py b/meta/lib/oe/cve_check.py
index 5edd34a2d9..487f30dc25 100644
--- a/meta/lib/oe/cve_check.py
+++ b/meta/lib/oe/cve_check.py
@@ -88,7 +88,7 @@ def get_patched_cves(d):
88 # (cve_match regular expression) 88 # (cve_match regular expression)
89 cve_file_name_match = re.compile(r".*(CVE-\d{4}-\d+)", re.IGNORECASE) 89 cve_file_name_match = re.compile(r".*(CVE-\d{4}-\d+)", re.IGNORECASE)
90 90
91 patched_cves = set() 91 patched_cves = {}
92 patches = oe.patch.src_patches(d) 92 patches = oe.patch.src_patches(d)
93 bb.debug(2, "Scanning %d patches for CVEs" % len(patches)) 93 bb.debug(2, "Scanning %d patches for CVEs" % len(patches))
94 for url in patches: 94 for url in patches:
@@ -98,7 +98,7 @@ def get_patched_cves(d):
98 fname_match = cve_file_name_match.search(patch_file) 98 fname_match = cve_file_name_match.search(patch_file)
99 if fname_match: 99 if fname_match:
100 cve = fname_match.group(1).upper() 100 cve = fname_match.group(1).upper()
101 patched_cves.add(cve) 101 patched_cves[cve] = {"abbrev-status": "Patched", "status": "fix-file-included", "resource": patch_file}
102 bb.debug(2, "Found %s from patch file name %s" % (cve, patch_file)) 102 bb.debug(2, "Found %s from patch file name %s" % (cve, patch_file))
103 103
104 # Remote patches won't be present and compressed patches won't be 104 # Remote patches won't be present and compressed patches won't be
@@ -124,7 +124,7 @@ def get_patched_cves(d):
124 cves = patch_text[match.start()+5:match.end()] 124 cves = patch_text[match.start()+5:match.end()]
125 for cve in cves.split(): 125 for cve in cves.split():
126 bb.debug(2, "Patch %s solves %s" % (patch_file, cve)) 126 bb.debug(2, "Patch %s solves %s" % (patch_file, cve))
127 patched_cves.add(cve) 127 patched_cves[cve] = {"abbrev-status": "Patched", "status": "fix-file-included", "resource": patch_file}
128 text_match = True 128 text_match = True
129 129
130 if not fname_match and not text_match: 130 if not fname_match and not text_match:
@@ -133,9 +133,15 @@ def get_patched_cves(d):
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 'mapping' in decoded_status and decoded_status['mapping'] == "Patched": 136 products = d.getVar("CVE_PRODUCT")
137 bb.debug(2, "CVE %s is additionally patched" % cve) 137 if has_cve_product_match(decoded_status, products) == True:
138 patched_cves.add(cve) 138 patched_cves[cve] = {
139 "abbrev-status": decoded_status["mapping"],
140 "status": decoded_status["detail"],
141 "justification": decoded_status["description"],
142 "affected-vendor": decoded_status["vendor"],
143 "affected-product": decoded_status["product"]
144 }
139 145
140 return patched_cves 146 return patched_cves
141 147
@@ -264,3 +270,20 @@ def decode_cve_status(d, cve):
264 status_out["mapping"] = status_mapping 270 status_out["mapping"] = status_mapping
265 271
266 return status_out 272 return status_out
273
274def has_cve_product_match(detailed_status, products):
275 """
276 Check product/vendor match between detailed_status from decode_cve_status and a string of
277 products (like from CVE_PRODUCT)
278 """
279 for product in products.split():
280 vendor = "*"
281 if ":" in product:
282 vendor, product = product.split(":", 1)
283
284 if (vendor == detailed_status["vendor"] or detailed_status["vendor"] == "*") and \
285 (product == detailed_status["product"] or detailed_status["product"] == "*"):
286 return True
287
288 #if no match, return False
289 return False