[OE-core] [RFC PATCH 2/2] vuln-cve_image: rootfs manifest about vulnerability
Toshikazu Nakayama
toshikazu-n at nec.com
Mon Mar 9 07:35:09 UTC 2020
A rootfs post command function vuln_cve_make_manifests() generates
some manifests about CVE_REPORTS list variable where files are generated
in per package task. A vuln_cve_make_manifests() gather these files about
installed packages in image from sstate directory to deploy manifest files
specified in CVE_MANIFEST to CVE_MANIFEST_DIR.
If CVE_MANIFEST_POPULATES is valid, vuln_cve_make_manifests() populate
manifest with "No vulnerability task support packages" which are installed
but not inherit vuln-cve and populate package contents with name, version
and installed package names.
And for the image recipe itself, prepare VULNFUNC_IMAGE_CLASS plugin which
may append summary header as image manifest such as CVE database freshness
which is inserted at the top of manifest files.
Signed-off-by: Toshikazu Nakayama <toshikazu-n at nec.com>
---
meta/classes/vuln-cve.bbclass | 18 +++++-
meta/classes/vuln-cve_image.bbclass | 111 ++++++++++++++++++++++++++++++++++++
2 files changed, 127 insertions(+), 2 deletions(-)
create mode 100644 meta/classes/vuln-cve_image.bbclass
diff --git a/meta/classes/vuln-cve.bbclass b/meta/classes/vuln-cve.bbclass
index 0c7b78c..f916aa8 100644
--- a/meta/classes/vuln-cve.bbclass
+++ b/meta/classes/vuln-cve.bbclass
@@ -171,12 +171,24 @@ VULNFUNC_JUDGE_CVE = 'list'
VULNFUNC_JUDGE_CVE = ""
VULNFUNC_REPORT_CVE[type] = 'list'
VULNFUNC_REPORT_CVE = ""
+VULNFUNC_IMAGE_CLASS[type] = 'list'
+VULNFUNC_IMAGE_CLASS = ""
python do_vulnerability() {
+ g = globals()
+ manifest = d.getVar('CVE_MANIFEST_DIR', True)
+ if manifest:
+ # inherit vuln-cve_image from image recipe
+ image = d.getVar('IMAGE_BASENAME', True)
+ destdir = os.path.join(d.getVar('VULNSTATEDIR', True), image)
+ bb.utils.mkdirhier(destdir)
+ for func in d.getVar('VULNFUNC_IMAGE_CLASS', True).split() or "":
+ if func in g:
+ g[func](d, destdir)
+ return
+
pn = d.getVar('PN', True)
destdir = os.path.join(d.getVar('VULNSTATEDIR', True), pn)
bb.utils.mkdirhier(destdir)
- g = globals()
-
cvelist = []
# Gather potential CVE list by using CPE matching
for scan in (d.getVar('VULNFUNC_SCAN_CVE', True) or "").split():
@@ -272,6 +284,8 @@ do_vulnerability[sstate-outputdirs] = "${VULN_CVE_DIRECTORY}"
do_vulnerability[dir] = "${VULNSTATEDIR}/${PN}"
do_vulnerability[cleandirs] = "${VULNSTATEDIR}"
do_vulnerability[nostamp] = "1"
+# Raising for global "INHERIT += vuln-cve" variable usage.
+IMAGE_CLASSES_append = " vuln-cve_image"
python do_vulnerability_setscene() {
sstate_setscene(d)
diff --git a/meta/classes/vuln-cve_image.bbclass b/meta/classes/vuln-cve_image.bbclass
new file mode 100644
index 0000000..46cfdb3
--- /dev/null
+++ b/meta/classes/vuln-cve_image.bbclass
@@ -0,0 +1,111 @@
+# This class deploy CVE manifest for installed packages in rootfs
+CVE_REPORTS[type] = 'list'
+CVE_CREATE_MANIFEST ??= "1"
+CVE_REPORTS ?= "cve.summary cve.patchlist"
+CVE_MANIFEST[cve.summary] = "${IMAGE_NAME}${IMAGE_NAME_SUFFIX}.cve.sumary"
+CVE_MANIFEST[cve.patchlist] = "${IMAGE_NAME}${IMAGE_NAME_SUFFIX}.cve.patchlist"
+CVE_MANIFEST_DIR ?= "${DEPLOY_DIR_IMAGE}"
+CVE_MANIFEST_POPULATES ??= ""
+
+# If image build without INHERIT usage but inherit vuln-cve_image in recipe.
+inherit vuln-cve
+python vuln_cve_make_manifests() {
+ vulnstatedir = d.getVar('VULN_CVE_DIRECTORY', True)
+ destdir = d.getVar('CVE_MANIFEST_DIR', True)
+ bb.utils.mkdirhier(destdir)
+
+ from oe.rootfs import image_list_installed_packages
+ pkg_type = d.getVar('PACKAGE_CLASSES', True).replace("package_", "").split()[0]
+ pkg_dic = {}
+ notask = []
+ def is_notask(sstatedir):
+ if not os.path.exists(sstatedir):
+ return True
+ try:
+ import time
+ from datetime import datetime
+ elapsed = time.time() - os.path.getctime(sstatedir)
+ if elapsed >= 5 * 60 * 60:
+ # If package quit to inherit vuln-cve but not cleansstate yet,
+ # rootfs task can not detect it immediately.
+ # As results, manifest includes such package unfortunately.
+ #
+ # If sstate-dir stamp elapsed more than 5 hours from created,
+ # forced clean sstate-dir to exclude it from manifest.
+ bb.warn("%s got staled about vulnerability sstate, "
+ "force to exclude from manifest" % pn)
+ bb.utils.remove(sstatedir, recurse=True)
+ return True
+ except OSError:
+ pass
+ return False
+
+ for pkg in image_list_installed_packages(d):
+ pkg_info = os.path.join(d.getVar('PKGDATA_DIR', True),
+ 'runtime-reverse', pkg)
+ pkgdata = oe.packagedata.read_pkgdatafile(pkg_info)
+ pkg_name = os.path.basename(os.readlink(pkg_info))
+ pn = pkgdata['PN']
+ if is_notask(os.path.join(vulnstatedir, pn)):
+ if not pn in notask:
+ notask.append(pn)
+ continue
+ elif pn in pkg_dic:
+ if pkg_name in pkg_dic[pn][pkg_type]:
+ continue
+ pkg_dic[pn][pkg_type].append(pkg_name)
+ continue
+ pkg_dic[pn] = {}
+ pkg_dic[pn]['pv'] = pkgdata['PV']
+ pkg_dic[pn][pkg_type] = [ pkg_name ]
+ pkg_dic[pn]['ssdir'] = os.path.join(vulnstatedir, pn)
+
+ from datetime import datetime
+ time_now = "%s" % datetime.now()
+ image = d.getVar('IMAGE_BASENAME', True)
+ image_sstate = os.path.join(vulnstatedir, image)
+ link_name = d.getVar("IMAGE_LINK_NAME")
+ do_populates = d.getVar('CVE_MANIFEST_POPULATES', True)
+ for rep in d.getVar('CVE_REPORTS', True).split():
+ manifest = d.getVarFlag('CVE_MANIFEST', rep, True)
+ manifest = os.path.join(destdir, manifest)
+ manifest_link = os.path.join(destdir, "%s.%s" % (link_name, rep))
+ if os.path.exists(os.path.realpath(manifest_link)):
+ bb.utils.remove(os.path.realpath(manifest_link))
+ bb.utils.remove(manifest_link)
+ # Header for this image
+ desc = []
+ if (os.path.exists(os.path.join(image_sstate, rep))):
+ # Insert private header if prepared for this manifest.
+ with open(os.path.join(image_sstate, rep), "r") as r:
+ desc.append(r.read())
+ else:
+ desc.append("%s: generated at %s\n" % (image, time_now))
+ if do_populates and len(notask) > 0:
+ notask = sorted(notask)
+ desc.append("\n")
+ desc.append("No vulnerability task support packages\n")
+ import textwrap
+ excludes = textwrap.wrap("[%s]" % ', '.join(notask), 76)
+ desc.append(" %s\n" % '\n '.join(excludes))
+ with open(manifest, 'w') as m:
+ m.write("%s\n" % ''.join(desc))
+ desc.clear()
+ for pn in sorted(pkg_dic):
+ write_msg = ""
+ if do_populates:
+ write_msg += "%s <%s> %s(%s)\n" % (pn, pkg_dic[pn]['pv'], pkg_type,
+ ', '.join(pkg_dic[pn][pkg_type]))
+ if os.path.exists(os.path.join(pkg_dic[pn]['ssdir'], rep)):
+ with open(os.path.join(pkg_dic[pn]['ssdir'], rep), "r") as r:
+ write_msg += r.read()
+ if not write_msg:
+ continue
+ desc.append(write_msg)
+ with open(manifest, 'a') as m:
+ m.write("%s" % '\n'.join(desc))
+ os.symlink(os.path.basename(manifest), manifest_link)
+ bb.plain("Image CVE report (%s) stored in: %s" % (rep, manifest))
+}
+ROOTFS_POSTPROCESS_COMMAND_prepend = "${@'vuln_cve_make_manifests; ' if d.getVar('CVE_CREATE_MANIFEST') == '1' else ''}"
+do_rootfs[recrdeptask] += "${@'do_vulnerability' if d.getVar('CVE_CREATE_MANIFEST') == '1' else ''}"
--
2.7.4
More information about the Openembedded-core
mailing list