#!/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("
Package Version CVEs
") 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__()