# To have tab on html file generated CVE_IMAGE_CONTENT_WITH_TAB ?= "1" # Configure on BSP side this var if you expect the summary to be generated ENABLE_IMAGE_CVE_SUMMARY ??= "0" ST_CVE_SUMMARY_DIR ?= "${WORKDIR}/cve-summary/" ST_CVE_SUMMARY_DEPLOYDIR ?= "${DEPLOY_DIR}/images/${MACHINE}" ST_CVE_SUMMARY_NAME ?= "${IMAGE_NAME}-cve_content.html" ST_CVE_SUMMARY_LINK_NAME ?= "${IMAGE_LINK_NAME}-cve_content.html" ST_MAIN_COMPONENTS ?= "" def cve_create_summary(d): import re import json tab = d.expand("${CVE_IMAGE_CONTENT_WITH_TAB}") ref_image_name = d.expand("${IMAGE_LINK_NAME}") deploy_image_dir = d.expand("${DEPLOY_DIR_IMAGE}") temp_deploy_image_dir = d.expand("${IMGDEPLOYDIR}") cve_deploy_dir = d.expand("${DEPLOY_DIR}/cve") cve_summary_deploydir = d.getVar('ST_CVE_SUMMARY_DIR') cve_summary_name = d.getVar('ST_CVE_SUMMARY_NAME') cve_summary_link = d.getVar('ST_CVE_SUMMARY_LINK_NAME') components_list = d.getVar('ST_MAIN_COMPONENTS') datetime = d.getVar('DATETIME') if tab.startswith("1"): with_tab = 1 else: with_tab = None def private_open(filename): result = None if os.path.exists(filename): try: with open(filename, "r") as lic: result = lic.readlines() except IOError: bb.warn("IMG LIC SUM: Cannot open file %s" % (filename)) result = "" except: bb.warn("IMG LIC SUM: Error with file %s" % (filename)) result = "" else: bb.warn("IMG LIC SUM: File does not exist with open file %s" % (filename)) result = "" return result class HTMLSummaryfile(): ''' format definition ''' bold = "font-weight: bold; background-color: #cccccc;" red = "background-color: #ff0000;" center_format = "align: center;" border_format = "border: 1;" wrap_format = "" wrap_red_format = "background-color: #ff0000;" blue = "background-color: #0000ff;" green = "background-color: #00ff00;" opened_file = None def openfile(self, file_name): self.opened_file = open(file_name, 'w') def closefile(self): self.opened_file.close() def startTable(self, style=None, classes=None): if style: if classes: self.opened_file.write("\n" % (style, classes)) else: self.opened_file.write("
\n" % style) else: if classes: self.opened_file.write("
\n" % classes) else: self.opened_file.write("
\n") def stopTable(self): self.opened_file.write("
\n") def startRow(self, style=None): self.opened_file.write("\n") def stopRow(self, style=None): self.opened_file.write("\n") def startColumn(self, style=None): if style: self.opened_file.write("\n") else: self.opened_file.write("\n") def stopColumn(self, style=None): self.opened_file.write("\n") def addColumnHeaderContent(self, content, style=None): if style: self.opened_file.write("%s\n" % (style, content)) else: self.opened_file.write("%s\n" % content) def addColumnContent(self, content, style=None): if style: self.opened_file.write("%s\n" % (style, content)) else: self.opened_file.write("%s\n" % content) def addColumnURLOUTContent(self, content, url, style=None): if style: self.opened_file.write("%s\n" % (style, url, content)) else: self.opened_file.write("%s\n" % (url, content)) def addColumnEmptyContent(self, style=None): if style: self.opened_file.write("
\n" % style) else: self.opened_file.write("
\n") def addNewLine(self): self.opened_file.write("
\n") def addContent(self, content): self.opened_file.write(content) def addURLContent(self, content, url): self.opened_file.write("%s\n" %(url, content)) def startBlock(self): self.opened_file.write("\n") def addTitle(self, title): self.opened_file.write("

{}

\n".format(title)) def addAnchor(self, anchor): self.opened_file.write("\n" % anchor) def startDiv(self, anchor, title, style=None): self.opened_file.write("
\n" % anchor) self.opened_file.write("

%s

\n" % title) def stopDiv(self): self.opened_file.write("
\n") def beginHtml(self): self.opened_file.write("\n") self.opened_file.write('\n') self.opened_file.write(" \n") self.opened_file.write("\n") def endHtml(self): self.opened_file.write("\n") def beginBody(self, tab=None): self.opened_file.write("\n") self.opened_file.write("
\n") def addApplicationTab(self): if tab: self.opened_file.write(' \n') self.opened_file.write(' \n') self.opened_file.write(' \n') self.opened_file.write("\n") def endBody(self, tab=None): if tab: self.opened_file.write('\n') else: self.opened_file.write('\n') self.opened_file.write("\n") def score_v2_table(score): ''' source : https://nvd.nist.gov/vuln-metrics/cvss ''' if float(score) < 4.0: return " {} Low ".format(score) elif float(score) < 7.0: return " {} Medium ".format(score) else: return " {} Hight ".format(score) def score_v3_table(score): ''' source : https://www.cert-ist.com/public/fr/SO_detail?code=cvss%20v3&format=html ''' if float(score) == 0.0: return " {} None ".format(score) elif float(score) < 4.0: return " {} Low ".format(score) elif float(score) < 7.0: return " {} Medium ".format(score) elif float(score) < 9.0: return " {} High ".format(score) else: return " {} Critical ".format(score) def generate_image_cve_sheet(html): html.startDiv("images", "Image CVE / CVS") html.addAnchor("images") # link html.addTitle("List of components on image:") html.startTable(style="width: 80%; border: 1px solid black;") html.startRow() html.addContent("") count=0 col=0 with open(os.path.join(deploy_image_dir, "{}.json".format(ref_image_name))) as image_file: json_component = json.load(image_file) for i in json_component['package']: issue=i['issue'] if len(issue) > 0: html.addContent("
  • {}
  • \n".format(i['name'], i['name'])) count+=1 if count > 15: count = 0 if col > 5: col = 0 html.addContent("") else: html.addContent("") col+=1 html.addContent("") html.stopRow() html.stopTable() html.addNewLine() html.addNewLine() html.startTable(style="border: 1px solid black;") with open(os.path.join(deploy_image_dir, ref_image_name + ".json")) as image_file: json_component = json.load(image_file) for i in json_component['package']: issue=i['issue'] if len(issue) > 0: html.startRow() html.addColumnHeaderContent("Component Name [top]".format(i['name']), style="font-weight: bold; background-color: #cccccc;") html.addColumnHeaderContent("Version", style="font-weight: bold; background-color: #cccccc;") html.addColumnHeaderContent("CVE Ref Name", style="font-weight: bold; background-color: #cccccc;") html.stopRow() html.startRow() html.addColumnContent(i['name']) html.addColumnContent(i['version']) html.addColumnContent(i['products'][0]['product']) html.stopRow() html.startRow() html.addColumnHeaderContent("Issues", style="font-weight: bold; background-color: #cccccc;") html.addContent("") for iss in issue: classe = '' if iss['status'].find("Unpatched") < 0: classe += 'patched ' if float(iss['scorev3']) < 7.0 : classe += 'scorev3 ' if float(iss['scorev2']) < 7.0 : classe += 'scorev2 ' if len(classe) > 0: html.startTable(style='border: 1px solid black; width=100%;', classes=classe) else: html.startTable(style='border: 1px solid black; width=100%;') html.startRow() html.addColumnContent(iss['id'], style="width: 10%;") if iss['status'].find("Unpatched") > -1: html.addColumnContent(iss['status'], style="background-color: red;") else: html.addColumnContent(iss['status']) html.stopRow() html.startRow() html.addColumnContent("") html.addColumnContent(iss['summary']) html.stopRow() html.startRow() html.addColumnContent("") html.addColumnContent("ScoreV2 = {} ScoreV3 = {}".format(score_v2_table(iss['scorev2']), score_v3_table(iss['scorev3']))) html.stopRow() html.startRow() html.addColumnContent("") html.addColumnContent("Link: NVD or CVE.org".format(iss['link'], iss['id'])) html.stopRow() html.stopTable() html.addContent("") html.stopRow() html.startRow() html.stopRow() html.startRow(style="bgcolor: gray;") html.stopRow() html.startRow() html.stopRow() html.stopTable() html.stopDiv() def generate_statistics_sheet(html, components): general_MACHINE = d.getVar("MACHINE") # Make sure to remove any DISTRO append to IMAGE_BASENAME for short display general_IMAGE = re.sub(r'-%s$' % d.getVar('DISTRO'), '', d.getVar("IMAGE_BASENAME")) general_DISTRO = d.getVar("DISTRO") general_DISTRO_VERSION = d.getVar("DISTRO_VERSION") general_DISTRO_CODENAME = d.getVar("DISTRO_CODENAME") html.startDiv("statistic", "Main Statistics") html.addAnchor("statistic") html.addTitle("Number of CVE/CVS:") html.startTable() html.startRow() html.addColumnHeaderContent("Component Name", style="font-weight: bold; background-color: #cccccc;") html.addColumnHeaderContent("Issues", style="font-weight: bold; background-color: #cccccc;") html.addColumnHeaderContent("Issues Patched", style="font-weight: bold; background-color: #cccccc;") html.addColumnHeaderContent("Issues Unpatched", style="font-weight: bold; background-color: #cccccc;") html.stopRow() data = [] for c in components.split(','): cvs = 0 patched = 0 unpatched = 0 file_name = os.path.join(cve_deploy_dir, "%s_cve.json" % c.lstrip().rstrip()) try: with open(os.path.join(cve_deploy_dir, file_name)) as component_file: json_component = json.load(component_file) for i in json_component['package']: issue=i['issue'] cvs = len(issue) for err in issue: if err['status'].find("Patched") > -1: patched+= 1 elif err['status'].find("Unpatched") > -1: unpatched+= 1 data.append([i['name'], cvs, patched, unpatched]) except FileNotFoundError: bb.note("file (%s) are not present" % os.path.join(cve_deploy_dir, "%s_cve.json" % c.lstrip().rstrip())) for i in data: html.startRow() html.addColumnContent(i[0], style="width: 10%;") html.addColumnContent(i[1], style="width: 10%; text-align: center;") html.addColumnContent(i[2], style="text-align: center;") html.addColumnContent(i[3], style="text-align: center;") html.stopRow() html.stopTable() html.addNewLine() html.addNewLine() # image count with open( os.path.join(deploy_image_dir, "{}.json".format(ref_image_name))) as image_file: json_component = json.load(image_file) cvs = 0 package=0 patched = 0 unpatched = 0 for i in json_component['package']: package += 1 issue=i['issue'] cvs += len(issue) for err in issue: if err['status'].find("Patched") > -1: patched += 1 elif err['status'].find("Unpatched") > -1: unpatched += 1 # package, cvs, patched, unpatched html.addTitle("Number of CVE/CVS for image:") html.startTable() html.startRow() html.addColumnHeaderContent("Image Name", style="font-weight: bold; background-color: #cccccc;") html.addColumnHeaderContent("Packages", style="font-weight: bold; background-color: #cccccc;") html.addColumnHeaderContent("Issues", style="font-weight: bold; background-color: #cccccc;") html.addColumnHeaderContent("Issues Patched", style="font-weight: bold; background-color: #cccccc;") html.addColumnHeaderContent("Issues Unpatched", style="font-weight: bold; background-color: #cccccc;") html.stopRow() html.startRow() html.addColumnContent(ref_image_name, style="width: 25%; ") html.addColumnContent(package, style="width: 10%; text-align: center;") html.addColumnContent(cvs, style="text-align: center;") html.addColumnContent(patched, style="text-align: center;") html.addColumnContent(unpatched, style="text-align: center;") html.stopRow() html.stopTable() html.stopDiv() def generate_components_content_sheet(html, components): html.startDiv("component", "Main Components CVE / CVS") html.addAnchor("component") html.addTitle("List of components:") # link for c in components.split(","): html.addContent("
  • {}
  • \n".format(c, c)) html.addNewLine() html.addNewLine() for name in components.split(","): html.addContent("

    Component: {} [top]

    \n".format(name)) html.addContent("\n".format(name)) html.startTable(style="border: 1px solid black;") html.startRow() html.addColumnHeaderContent("Component Name", style="font-weight: bold; background-color: #cccccc;") html.addColumnContent(name) html.stopRow() try: with open(os.path.join(cve_deploy_dir, "%s_cve.json" % name.lstrip().rstrip())) as component_file: json_component = json.load(component_file) for i in json_component['package']: issue=i['issue'] html.startRow() html.addColumnHeaderContent("Version", style="font-weight: bold; background-color: #cccccc;") html.addColumnContent(i['version']) html.stopRow() html.startRow() html.addColumnHeaderContent("CVE Ref Name", style="font-weight: bold; background-color: #cccccc;") html.addColumnContent(i['products'][0]['product']) html.stopRow() html.startRow() html.addColumnHeaderContent("Issues", style="font-weight: bold; background-color: #cccccc;") html.startColumn() for iss in issue: classe = '' if iss['status'].find("Unpatched") < 0: classe += 'patched ' if float(iss['scorev3']) < 7.0 : classe += 'scorev3 ' if float(iss['scorev2']) < 7.0 : classe += 'scorev2 ' if len(classe) > 0: html.startTable(style='border: 1px solid black; width=100%;', classes=classe) else: html.startTable(style='border: 1px solid black; width=100%;') html.startRow() html.addColumnContent(iss['id'], style="width: 10%;") if iss['status'].find("Unpatched") > -1: html.addColumnContent(iss['status'], style="background-color: red;") else: html.addColumnContent(iss['status']) html.stopRow() html.startRow() html.addColumnContent("") html.addColumnContent(iss['summary']) html.stopRow() html.startRow() html.addColumnContent("") html.addColumnContent("ScoreV2 = {}; ScoreV3 = {}".format(score_v2_table(iss['scorev2']), score_v3_table(iss['scorev3']))) html.stopRow() html.startRow() html.addColumnContent("") html.addColumnContent("Link: NVD or CVE.org".format(iss['link'], iss['id'])) html.stopRow() # empty line for separation html.startRow(style="bgcolor: gray;") html.stopRow() html.stopTable() html.stopColumn() i = None except FileNotFoundError: bb.note("file (%s) are not present" % os.path.join(cve_deploy_dir, "%s_cve.json" % name.lstrip().rstrip())) html.stopTable() html.stopDiv() # Create license summary file cve_summary_name_path = os.path.join(cve_summary_deploydir, cve_summary_name) print("generate ", cve_summary_name_path) html = HTMLSummaryfile() html.openfile(cve_summary_name_path) html.beginHtml() html.beginBody(with_tab) html.addTitle("Image Name: {}
    Generation Date: {}".format(ref_image_name, datetime)) html.addContent("
    See Only 'Unpatched' CVE
    ") html.addNewLine() html.addContent("
    See Only 'ScoreV3 > 7' (only)
    ") html.addNewLine() html.addContent("
    See Only 'ScoreV2 > 7' (only)
    ") html.addNewLine() html.addApplicationTab() ''' generate first page: statistics cve / cvs information''' generate_statistics_sheet(html, components_list) ''' generate image content ''' generate_components_content_sheet(html, components_list) ''' generate license spdx reference ''' generate_image_cve_sheet(html) html.endBody(with_tab) html.endHtml() html.closefile() # Create link cve_summary_link_path = os.path.join(cve_summary_deploydir, cve_summary_link) if os.path.exists(cve_summary_name_path): bb.note("Creating symlink: %s -> %s" % (cve_summary_link_path, cve_summary_name)) if os.path.islink(cve_summary_link_path): os.remove(cve_summary_link_path) os.symlink(cve_summary_name, cve_summary_link_path) else: bb.note("Skipping symlink, source does not exist: %s -> %s" % (cve_summary_link_path, cve_summary_name)) python do_st_write_cve_create_summary() { bb.note("---> ENABLE_IMAGE_CVE_SUMMARY %s" % (d.getVar('ENABLE_IMAGE_CVE_SUMMARY'))) if d.getVar('ENABLE_IMAGE_CVE_SUMMARY') == "1": if not d.getVar('CVE_CHECK_MANIFEST') is None: cve_create_summary(d) else: return } addtask st_write_cve_create_summary before do_build after do_image_complete do_st_write_cve_create_summary[dirs] = "${ST_CVE_SUMMARY_DIR} ${IMGDEPLOYDIR}" SSTATETASKS += "do_st_write_cve_create_summary" do_st_write_cve_create_summary[cleandirs] = "${ST_CVE_SUMMARY_DIR}" do_st_write_cve_create_summary[sstate-inputdirs] = "${ST_CVE_SUMMARY_DIR}" do_st_write_cve_create_summary[sstate-outputdirs] = "${ST_CVE_SUMMARY_DEPLOYDIR}/" python do_st_write_cve_create_summary_setscene () { sstate_setscene(d) } addtask do_st_write_cve_create_summary_setscene #excluded from basehash signature calculation cve_create_summary[vardepsexclude] += "DATETIME"