summaryrefslogtreecommitdiffstats
path: root/meta/classes/cve-check.bbclass
diff options
context:
space:
mode:
Diffstat (limited to 'meta/classes/cve-check.bbclass')
-rw-r--r--meta/classes/cve-check.bbclass51
1 files changed, 40 insertions, 11 deletions
diff --git a/meta/classes/cve-check.bbclass b/meta/classes/cve-check.bbclass
index f574f5daa4..78516d0bb6 100644
--- a/meta/classes/cve-check.bbclass
+++ b/meta/classes/cve-check.bbclass
@@ -55,6 +55,9 @@ CVE_CHECK_FORMAT_TEXT ??= "1"
55# Provide JSON output 55# Provide JSON output
56CVE_CHECK_FORMAT_JSON ??= "1" 56CVE_CHECK_FORMAT_JSON ??= "1"
57 57
58# Check for packages without CVEs (no issues or missing product name)
59CVE_CHECK_COVERAGE ??= "1"
60
58# Skip CVE Check for packages (PN) 61# Skip CVE Check for packages (PN)
59CVE_CHECK_SKIP_RECIPE ?= "" 62CVE_CHECK_SKIP_RECIPE ?= ""
60 63
@@ -114,10 +117,10 @@ python do_cve_check () {
114 patched_cves = get_patched_cves(d) 117 patched_cves = get_patched_cves(d)
115 except FileNotFoundError: 118 except FileNotFoundError:
116 bb.fatal("Failure in searching patches") 119 bb.fatal("Failure in searching patches")
117 ignored, patched, unpatched = check_cves(d, patched_cves) 120 ignored, patched, unpatched, status = check_cves(d, patched_cves)
118 if patched or unpatched: 121 if patched or unpatched or (d.getVar("CVE_CHECK_COVERAGE") == "1" and status):
119 cve_data = get_cve_info(d, patched + unpatched) 122 cve_data = get_cve_info(d, patched + unpatched)
120 cve_write_data(d, patched, unpatched, ignored, cve_data) 123 cve_write_data(d, patched, unpatched, ignored, cve_data, status)
121 else: 124 else:
122 bb.note("No CVE database found, skipping CVE check") 125 bb.note("No CVE database found, skipping CVE check")
123 126
@@ -207,17 +210,19 @@ def check_cves(d, patched_cves):
207 suffix = d.getVar("CVE_VERSION_SUFFIX") 210 suffix = d.getVar("CVE_VERSION_SUFFIX")
208 211
209 cves_unpatched = [] 212 cves_unpatched = []
213 cves_status = []
214 cves_in_recipe = False
210 # CVE_PRODUCT can contain more than one product (eg. curl/libcurl) 215 # CVE_PRODUCT can contain more than one product (eg. curl/libcurl)
211 products = d.getVar("CVE_PRODUCT").split() 216 products = d.getVar("CVE_PRODUCT").split()
212 # If this has been unset then we're not scanning for CVEs here (for example, image recipes) 217 # If this has been unset then we're not scanning for CVEs here (for example, image recipes)
213 if not products: 218 if not products:
214 return ([], [], []) 219 return ([], [], [], {})
215 pv = d.getVar("CVE_VERSION").split("+git")[0] 220 pv = d.getVar("CVE_VERSION").split("+git")[0]
216 221
217 # If the recipe has been skipped/ignored we return empty lists 222 # If the recipe has been skipped/ignored we return empty lists
218 if pn in d.getVar("CVE_CHECK_SKIP_RECIPE").split(): 223 if pn in d.getVar("CVE_CHECK_SKIP_RECIPE").split():
219 bb.note("Recipe has been skipped by cve-check") 224 bb.note("Recipe has been skipped by cve-check")
220 return ([], [], []) 225 return ([], [], [], [])
221 226
222 cve_ignore = d.getVar("CVE_CHECK_IGNORE").split() 227 cve_ignore = d.getVar("CVE_CHECK_IGNORE").split()
223 228
@@ -227,6 +232,7 @@ def check_cves(d, patched_cves):
227 232
228 # For each of the known product names (e.g. curl has CPEs using curl and libcurl)... 233 # For each of the known product names (e.g. curl has CPEs using curl and libcurl)...
229 for product in products: 234 for product in products:
235 cves_in_product = False
230 if ":" in product: 236 if ":" in product:
231 vendor, product = product.split(":", 1) 237 vendor, product = product.split(":", 1)
232 else: 238 else:
@@ -244,6 +250,11 @@ def check_cves(d, patched_cves):
244 elif cve in patched_cves: 250 elif cve in patched_cves:
245 bb.note("%s has been patched" % (cve)) 251 bb.note("%s has been patched" % (cve))
246 continue 252 continue
253 # Write status once only for each product
254 if not cves_in_product:
255 cves_status.append([product, True])
256 cves_in_product = True
257 cves_in_recipe = True
247 258
248 vulnerable = False 259 vulnerable = False
249 for row in conn.execute("SELECT * FROM PRODUCTS WHERE ID IS ? AND PRODUCT IS ? AND VENDOR LIKE ?", (cve, product, vendor)): 260 for row in conn.execute("SELECT * FROM PRODUCTS WHERE ID IS ? AND PRODUCT IS ? AND VENDOR LIKE ?", (cve, product, vendor)):
@@ -290,9 +301,16 @@ def check_cves(d, patched_cves):
290 # TODO: not patched but not vulnerable 301 # TODO: not patched but not vulnerable
291 patched_cves.add(cve) 302 patched_cves.add(cve)
292 303
304 if not cves_in_product:
305 bb.note("No CVE records found for product %s, pn %s" % (product, pn))
306 cves_status.append([product, False])
307
293 conn.close() 308 conn.close()
294 309
295 return (list(cve_ignore), list(patched_cves), cves_unpatched) 310 if not cves_in_recipe:
311 bb.note("No CVE records for products in recipe %s" % (pn))
312
313 return (list(cve_ignore), list(patched_cves), cves_unpatched, cves_status)
296 314
297def get_cve_info(d, cves): 315def get_cve_info(d, cves):
298 """ 316 """
@@ -323,7 +341,6 @@ def cve_write_data_text(d, patched, unpatched, ignored, cve_data):
323 CVE manifest if enabled. 341 CVE manifest if enabled.
324 """ 342 """
325 343
326
327 cve_file = d.getVar("CVE_CHECK_LOG") 344 cve_file = d.getVar("CVE_CHECK_LOG")
328 fdir_name = d.getVar("FILE_DIRNAME") 345 fdir_name = d.getVar("FILE_DIRNAME")
329 layer = fdir_name.split("/")[-3] 346 layer = fdir_name.split("/")[-3]
@@ -337,6 +354,10 @@ def cve_write_data_text(d, patched, unpatched, ignored, cve_data):
337 if include_layers and layer not in include_layers: 354 if include_layers and layer not in include_layers:
338 return 355 return
339 356
357 # Early exit, the text format does not report packages without CVEs
358 if not patched+unpatched:
359 return
360
340 nvd_link = "https://nvd.nist.gov/vuln/detail/" 361 nvd_link = "https://nvd.nist.gov/vuln/detail/"
341 write_string = "" 362 write_string = ""
342 unpatched_cves = [] 363 unpatched_cves = []
@@ -414,7 +435,7 @@ def cve_check_write_json_output(d, output, direct_file, deploy_file, manifest_fi
414 with open(index_path, "a+") as f: 435 with open(index_path, "a+") as f:
415 f.write("%s\n" % fragment_path) 436 f.write("%s\n" % fragment_path)
416 437
417def cve_write_data_json(d, patched, unpatched, ignored, cve_data): 438def cve_write_data_json(d, patched, unpatched, ignored, cve_data, cve_status):
418 """ 439 """
419 Prepare CVE data for the JSON format, then write it. 440 Prepare CVE data for the JSON format, then write it.
420 """ 441 """
@@ -436,11 +457,19 @@ def cve_write_data_json(d, patched, unpatched, ignored, cve_data):
436 457
437 unpatched_cves = [] 458 unpatched_cves = []
438 459
460 product_data = []
461 for s in cve_status:
462 p = {"product": s[0], "cvesInRecord": "Yes"}
463 if s[1] == False:
464 p["cvesInRecord"] = "No"
465 product_data.append(p)
466
439 package_version = "%s%s" % (d.getVar("EXTENDPE"), d.getVar("PV")) 467 package_version = "%s%s" % (d.getVar("EXTENDPE"), d.getVar("PV"))
440 package_data = { 468 package_data = {
441 "name" : d.getVar("PN"), 469 "name" : d.getVar("PN"),
442 "layer" : layer, 470 "layer" : layer,
443 "version" : package_version 471 "version" : package_version,
472 "products": product_data
444 } 473 }
445 cve_list = [] 474 cve_list = []
446 475
@@ -479,7 +508,7 @@ def cve_write_data_json(d, patched, unpatched, ignored, cve_data):
479 508
480 cve_check_write_json_output(d, output, direct_file, deploy_file, manifest_file) 509 cve_check_write_json_output(d, output, direct_file, deploy_file, manifest_file)
481 510
482def cve_write_data(d, patched, unpatched, ignored, cve_data): 511def cve_write_data(d, patched, unpatched, ignored, cve_data, status):
483 """ 512 """
484 Write CVE data in each enabled format. 513 Write CVE data in each enabled format.
485 """ 514 """
@@ -487,4 +516,4 @@ def cve_write_data(d, patched, unpatched, ignored, cve_data):
487 if d.getVar("CVE_CHECK_FORMAT_TEXT") == "1": 516 if d.getVar("CVE_CHECK_FORMAT_TEXT") == "1":
488 cve_write_data_text(d, patched, unpatched, ignored, cve_data) 517 cve_write_data_text(d, patched, unpatched, ignored, cve_data)
489 if d.getVar("CVE_CHECK_FORMAT_JSON") == "1": 518 if d.getVar("CVE_CHECK_FORMAT_JSON") == "1":
490 cve_write_data_json(d, patched, unpatched, ignored, cve_data) 519 cve_write_data_json(d, patched, unpatched, ignored, cve_data, status)