summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrii Bordunov via Openembedded-core <openembedded-core@lists.openembedded.org>2018-10-10 19:25:11 +0300
committerArmin Kuster <akuster808@gmail.com>2018-10-22 15:25:29 +0100
commit575d75d26b7a12fda5985c6546b0f6d1f9533ba7 (patch)
treeb51e486efee25e9f915b25de42a7b537ebee8bdc
parent60c05c299e2c00d5e4a2d21cbe358d3c8b075878 (diff)
downloadopenembedded-core-contrib-akuster/cve_checker2.tar.gz
openembedded-core-contrib-akuster/cve_checker2.tar.bz2
openembedded-core-contrib-akuster/cve_checker2.zip
cve-report.bbclass: add classakuster/cve_checker2
Implements "report_cve" and "report_patched" tasks. "report_patched" prepares image manifest with patched CVE info. "report_cve" runs cvert-* scripts to generate kernel and package CVE reports. You can configure it to set report filenames, reuse NVD feeds, stop after manifest generation and ignore specific classes, like native, nativesdk, etc. Signed-off-by: grygorii tertychnyi <gtertych@cisco.com> Signed-off-by: Armin Kuster <akuster808@gmail.com>
-rw-r--r--meta/classes/cve-report.bbclass216
1 files changed, 216 insertions, 0 deletions
diff --git a/meta/classes/cve-report.bbclass b/meta/classes/cve-report.bbclass
new file mode 100644
index 0000000000..35d58d0821
--- /dev/null
+++ b/meta/classes/cve-report.bbclass
@@ -0,0 +1,216 @@
+# Class to inherit when you want to generate a CVE reports.
+#
+# Generates package list file and package CVE report.
+#
+# Example:
+# echo 'INHERIT += "cve-report"' >> conf/local.conf
+# bitbake -c report_cve core-image-minimal
+#
+# Variables to be passed to "cvert-*" scripts:
+#
+# CVE_REPORT_MODE[foss]
+# Path to the CVE FOSS report to be generated.
+#
+# CVE_REPORT_MODE[restore]
+# Path to the CVE dump data file.
+#
+# E.g. for multiple MACHINEs:
+# (1) generate CVE dump:
+# cvert-update --store /path/to/cvedump $TEMP/nvdfeed
+# (2) for mach in $(get_machine_list); do
+# (source oe-init-build-env "build-$mach";
+# echo 'CVE_REPORT_MODE[restore] = "/path/to/cvedump"' >> conf/local.conf;
+# echo 'CVE_REPORT_MODE[foss] = "/path/to/report-foss-'${mach}'"' >> conf/local.conf;
+# MACHINE=$mach bitbake -c report_cve core-image-minimal)
+# done
+#
+# CVE_REPORT_MODE[offline]
+# Either "0" or "1". Offline mode ("--offline" parameter for cvert-* scripts).
+#
+# CVE_REPORT_MODE[feeddir]
+# Path to the NVD feed directory.
+#
+# CVE_REPORT_MODE[packagelist]
+# Path to the package list file to be generated.
+#
+# CVE_REPORT_MODE[packageonly]
+# Either "0" or "1". Generate package list file, then stop.
+#
+# CVE_REPORT_MODE[blacklist]
+# Ignore specific class.
+#
+
+CVE_REPORT_MODE[foss] ?= "${LOG_DIR}/cvert/report-foss.txt"
+CVE_REPORT_MODE[offline] ?= "0"
+CVE_REPORT_MODE[feeddir] ?= "${LOG_DIR}/nvdfeeds"
+CVE_REPORT_MODE[packagelist] ?= "${LOG_DIR}/cvert/package.lst"
+CVE_REPORT_MODE[packageonly] ?= "0"
+CVE_REPORT_MODE[blacklist] ?= "native,nativesdk,cross,crosssdk,cross-canadian,packagegroup,image"
+
+CVE_PRODUCT ??= "${BPN}"
+CVE_VERSION ??= "${PV}"
+
+addhandler generate_report_handler
+generate_report_handler[eventmask] = "bb.event.BuildCompleted"
+
+def cvert_update(d):
+ """Update NVD storage and prepare CVE dump"""
+
+ import tempfile
+ import subprocess
+
+ bb.utils.export_proxies(d)
+
+ dump = os.path.join(d.getVar("LOG_DIR"), "cvedump")
+
+ bb.note("Updating CVE database: %s" % dump)
+
+ cmd = [
+ "cvert-update",
+ "--store", dump,
+ "--debug",
+ d.getVarFlag("CVE_REPORT_MODE", "feeddir")
+ ]
+
+ if d.getVarFlag("CVE_REPORT_MODE", "offline") != "0":
+ cmd.append("--offline")
+
+ try:
+ bb.debug(2, "Call '%s'" % " ".join(cmd))
+ output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode()
+ bb.debug(2, "Output: %s" % output)
+ except subprocess.CalledProcessError as e:
+ bb.error("Failed to run cvert-update: '%s'\n%s: %s" % (" ".join(cmd), e, e.output))
+
+ return dump
+
+# copied from cve-check.bbclass
+def get_patches_cves(d):
+ """Get patches that solve CVEs using the "CVE: " tag"""
+
+ import re
+
+ pn = d.getVar("PN")
+ cve_match = re.compile("CVE:( CVE\-\d{4}\-\d+)+")
+
+ # Matches last CVE-1234-211432 in the file name, also if written
+ # with small letters. Not supporting multiple CVE id's in a single
+ # file name.
+ cve_file_name_match = re.compile(".*([Cc][Vv][Ee]\-\d{4}\-\d+)")
+
+ patched_cves = set()
+ bb.debug(2, "Looking for patches that solves CVEs for %s" % pn)
+ for url in src_patches(d):
+ patch_file = bb.fetch.decodeurl(url)[2]
+
+ # Check patch file name for CVE ID
+ fname_match = cve_file_name_match.search(patch_file)
+ if fname_match:
+ cve = fname_match.group(1).upper()
+ patched_cves.add(cve)
+ bb.debug(2, "Found CVE %s from patch file name %s" % (cve, patch_file))
+
+ with open(patch_file, "r", encoding="utf-8") as f:
+ try:
+ patch_text = f.read()
+ except UnicodeDecodeError:
+ bb.debug(1, "Failed to read patch %s using UTF-8 encoding"
+ " trying with iso8859-1" % patch_file)
+ f.close()
+ with open(patch_file, "r", encoding="iso8859-1") as f:
+ patch_text = f.read()
+
+ # Search for one or more "CVE: " lines
+ text_match = False
+ for match in cve_match.finditer(patch_text):
+ # Get only the CVEs without the "CVE: " tag
+ cves = patch_text[match.start()+5:match.end()]
+ for cve in cves.split():
+ bb.debug(2, "Patch %s solves %s" % (patch_file, cve))
+ patched_cves.add(cve)
+ text_match = True
+
+ if not fname_match and not text_match:
+ bb.debug(2, "Patch %s doesn't solve CVEs" % patch_file)
+
+ return patched_cves
+
+
+python generate_report_handler() {
+ if d.getVarFlag("CVE_REPORT_MODE", "packageonly") != "0":
+ return
+
+ import subprocess
+
+ restore = d.getVarFlag("CVE_REPORT_MODE", "restore")
+
+ if not restore:
+ restore = cvert_update(d)
+
+ if os.path.exists(d.getVarFlag("CVE_REPORT_MODE", "packagelist")):
+ report_foss = d.getVarFlag("CVE_REPORT_MODE", "foss")
+
+ bb.note("Generating CVE FOSS report: %s" % report_foss)
+
+ cmd = [
+ "cvert-foss",
+ "--restore", restore,
+ "--output", report_foss,
+ d.getVarFlag("CVE_REPORT_MODE", "packagelist")
+ ]
+
+ try:
+ bb.debug(2, "Call '%s'" % " ".join(cmd))
+ output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode()
+ bb.debug(2, "Output: %s" % output)
+ except subprocess.CalledProcessError as e:
+ bb.error("Failed to run cvert-foss: '%s'\n%s: %s" % (" ".join(cmd), e, e.output))
+}
+
+addhandler build_started
+build_started[eventmask] = "bb.event.BuildStarted"
+
+python build_started() {
+ packagelist = d.getVarFlag("CVE_REPORT_MODE", "packagelist")
+ bb.utils.remove(packagelist)
+ bb.utils.mkdirhier(os.path.dirname(packagelist))
+ bb.note("Package list: ", packagelist)
+}
+
+addtask do_report_cve after do_report_patched
+
+do_report_cve[recrdeptask] = "do_report_cve do_report_patched"
+do_report_cve[recideptask] = "do_${BB_DEFAULT_TASK}"
+do_report_cve[nostamp] = "1"
+
+do_report_cve() {
+ :
+}
+
+python do_report_patched() {
+ if not d.getVar("SRC_URI"):
+ return
+
+ cve_product = d.getVar("CVE_PRODUCT")
+
+ if not cve_product:
+ return
+
+ cve_version = d.getVar("CVE_VERSION")
+ patched_cves = get_patches_cves(d)
+
+ with open(d.getVarFlag("CVE_REPORT_MODE", "packagelist"), "a") as fil:
+ fil.write("%s,%s,%s\n" % (cve_product, cve_version, " ".join(patched_cves)))
+ bb.debug(2, "Append to package-list: '%s,%s,%s'" % (cve_product, cve_version, " ".join(patched_cves)))
+}
+
+addtask do_report_patched after do_unpack before do_build
+
+do_report_patched[nostamp] = "1"
+
+python() {
+ for b in d.getVarFlag("CVE_REPORT_MODE", "blacklist").split(","):
+ if bb.data.inherits_class(b, d):
+ bb.build.deltask("do_report_patched", d)
+ break
+}