From fafa3e4e293faabc0d38a714eb88a25252936a99 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Fri, 24 Jul 2020 17:43:53 +0200 Subject: [PATCH] support/scripts/cve-checker: add a per configuration CVE checker This scripts takes as entry on stdin a JSON description of the package used for a given configuration. This description is the one generated by "make show-info". The script generates the list of all the packages used and if they are affected by a CVE. The output is either a JSON or an HTML file similar to the one generated by pkg-stats. Signed-off-by: Gregory CLEMENT Tested-by: Matthew Weber = Signed-off-by: Thomas Petazzoni --- support/scripts/cve-checker | 192 ++++++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100755 support/scripts/cve-checker diff --git a/support/scripts/cve-checker b/support/scripts/cve-checker new file mode 100755 index 0000000000..2bfe2d8543 --- /dev/null +++ b/support/scripts/cve-checker @@ -0,0 +1,192 @@ +#!/usr/bin/env python + +# Copyright (C) 2009 by Thomas Petazzoni +# Copyright (C) 2020 by Gregory CLEMENT +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import argparse +import datetime +import os +import json +import sys +import cve as cvecheck + +class Package: + def __init__(self, name, version, ignored_cves): + self.name = name + self.version = version + self.cves = list() + self.ignored_cves = ignored_cves + +def check_package_cves(nvd_path, packages): + if not os.path.isdir(nvd_path): + os.makedirs(nvd_path) + + for cve in cvecheck.CVE.read_nvd_dir(nvd_path): + for pkg_name in cve.pkg_names: + pkg = packages.get(pkg_name, '') + if pkg and cve.affects(pkg.name, pkg.version, pkg.ignored_cves) == cve.CVE_AFFECTS: + pkg.cves.append(cve.identifier) + +html_header = """ + + + +CVE status for Buildroot configuration + + +

+""" + + +html_footer = """ + + + +""" + + +def dump_html_pkg(f, pkg): + f.write(" \n") + f.write(" %s\n" % pkg.name) + + # Current version + if len(pkg.version) > 20: + version = pkg.version[:20] + "..." + else: + version = pkg.version + f.write(" %s\n" % version) + + # CVEs + td_class = ["centered"] + if len(pkg.cves) == 0: + td_class.append("correct") + else: + td_class.append("wrong") + f.write(" \n" % " ".join(td_class)) + for cve in pkg.cves: + f.write(" %s
\n" % (cve, cve)) + f.write(" \n") + + f.write(" \n") + + +def dump_html_all_pkgs(f, packages): + f.write(""" + + + + + + +""") + for pkg in packages: + dump_html_pkg(f, pkg) + f.write("
PackageVersionCVEs
") + + +def dump_html_gen_info(f, date): + f.write("

Generated on %s

\n" % (str(date))) + + +def dump_html(packages, date, output): + with open(output, 'w') as f: + f.write(html_header) + dump_html_all_pkgs(f, packages) + dump_html_gen_info(f, date) + f.write(html_footer) + + +def dump_json(packages, date, output): + # Format packages as a dictionnary instead of a list + pkgs = { + pkg.name: { + "version": pkg.version, + "cves": pkg.cves, + } for pkg in packages + } + # The actual structure to dump, add date to it + final = {'packages': pkgs, + 'date': str(date)} + with open(output, 'w') as f: + json.dump(final, f, indent=2, separators=(',', ': ')) + f.write('\n') + + +def resolvepath(path): + return os.path.abspath(os.path.expanduser(path)) + + +def parse_args(): + parser = argparse.ArgumentParser() + output = parser.add_argument_group('output', 'Output file(s)') + output.add_argument('--html', dest='html', type=resolvepath, + help='HTML output file') + output.add_argument('--json', dest='json', type=resolvepath, + help='JSON output file') + parser.add_argument('--nvd-path', dest='nvd_path', + help='Path to the local NVD database',type=resolvepath, + required=True) + args = parser.parse_args() + if not args.html and not args.json: + parser.error('at least one of --html or --json (or both) is required') + return args + + +def __main__(): + packages = list() + content = json.load(sys.stdin) + for item in content: + pkg = content[item] + p = Package(item, pkg.get('version', ''), pkg.get('ignore_cves', '')) + packages.append(p) + + args = parse_args() + date = datetime.datetime.utcnow() + + print("Checking packages CVEs") + check_package_cves(args.nvd_path, {p.name: p for p in packages}) + + if args.html: + print("Write HTML") + dump_html(packages, date, args.html) + if args.json: + print("Write JSON") + dump_json(packages, date, args.json) + +__main__() -- 2.30.2