1*4882a593Smuzhiyun#!/usr/bin/env python3 2*4882a593Smuzhiyun 3*4882a593Smuzhiyun# Copyright (C) 2009 by Thomas Petazzoni <thomas.petazzoni@free-electrons.com> 4*4882a593Smuzhiyun# 5*4882a593Smuzhiyun# This program is free software; you can redistribute it and/or modify 6*4882a593Smuzhiyun# it under the terms of the GNU General Public License as published by 7*4882a593Smuzhiyun# the Free Software Foundation; either version 2 of the License, or 8*4882a593Smuzhiyun# (at your option) any later version. 9*4882a593Smuzhiyun# 10*4882a593Smuzhiyun# This program is distributed in the hope that it will be useful, 11*4882a593Smuzhiyun# but WITHOUT ANY WARRANTY; without even the implied warranty of 12*4882a593Smuzhiyun# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13*4882a593Smuzhiyun# General Public License for more details. 14*4882a593Smuzhiyun# 15*4882a593Smuzhiyun# You should have received a copy of the GNU General Public License 16*4882a593Smuzhiyun# along with this program; if not, write to the Free Software 17*4882a593Smuzhiyun# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18*4882a593Smuzhiyun 19*4882a593Smuzhiyunimport aiohttp 20*4882a593Smuzhiyunimport argparse 21*4882a593Smuzhiyunimport asyncio 22*4882a593Smuzhiyunimport datetime 23*4882a593Smuzhiyunimport fnmatch 24*4882a593Smuzhiyunimport os 25*4882a593Smuzhiyunfrom collections import defaultdict 26*4882a593Smuzhiyunimport re 27*4882a593Smuzhiyunimport subprocess 28*4882a593Smuzhiyunimport json 29*4882a593Smuzhiyunimport sys 30*4882a593Smuzhiyun 31*4882a593Smuzhiyunbrpath = os.path.normpath(os.path.join(os.path.dirname(__file__), "..", "..")) 32*4882a593Smuzhiyun 33*4882a593Smuzhiyunsys.path.append(os.path.join(brpath, "utils")) 34*4882a593Smuzhiyunfrom getdeveloperlib import parse_developers # noqa: E402 35*4882a593Smuzhiyunfrom cpedb import CPEDB # noqa: E402 36*4882a593Smuzhiyun 37*4882a593SmuzhiyunINFRA_RE = re.compile(r"\$\(eval \$\(([a-z-]*)-package\)\)") 38*4882a593SmuzhiyunURL_RE = re.compile(r"\s*https?://\S*\s*$") 39*4882a593Smuzhiyun 40*4882a593SmuzhiyunRM_API_STATUS_ERROR = 1 41*4882a593SmuzhiyunRM_API_STATUS_FOUND_BY_DISTRO = 2 42*4882a593SmuzhiyunRM_API_STATUS_FOUND_BY_PATTERN = 3 43*4882a593SmuzhiyunRM_API_STATUS_NOT_FOUND = 4 44*4882a593Smuzhiyun 45*4882a593Smuzhiyun 46*4882a593Smuzhiyunclass Defconfig: 47*4882a593Smuzhiyun def __init__(self, name, path): 48*4882a593Smuzhiyun self.name = name 49*4882a593Smuzhiyun self.path = path 50*4882a593Smuzhiyun self.developers = None 51*4882a593Smuzhiyun 52*4882a593Smuzhiyun def set_developers(self, developers): 53*4882a593Smuzhiyun """ 54*4882a593Smuzhiyun Fills in the .developers field 55*4882a593Smuzhiyun """ 56*4882a593Smuzhiyun self.developers = [ 57*4882a593Smuzhiyun developer.name 58*4882a593Smuzhiyun for developer in developers 59*4882a593Smuzhiyun if developer.hasfile(self.path) 60*4882a593Smuzhiyun ] 61*4882a593Smuzhiyun 62*4882a593Smuzhiyun 63*4882a593Smuzhiyundef get_defconfig_list(): 64*4882a593Smuzhiyun """ 65*4882a593Smuzhiyun Builds the list of Buildroot defconfigs, returning a list of Defconfig 66*4882a593Smuzhiyun objects. 67*4882a593Smuzhiyun """ 68*4882a593Smuzhiyun return [ 69*4882a593Smuzhiyun Defconfig(name[:-len('_defconfig')], os.path.join('configs', name)) 70*4882a593Smuzhiyun for name in os.listdir(os.path.join(brpath, 'configs')) 71*4882a593Smuzhiyun if name.endswith('_defconfig') 72*4882a593Smuzhiyun ] 73*4882a593Smuzhiyun 74*4882a593Smuzhiyun 75*4882a593Smuzhiyunclass Package: 76*4882a593Smuzhiyun all_licenses = dict() 77*4882a593Smuzhiyun all_license_files = list() 78*4882a593Smuzhiyun all_versions = dict() 79*4882a593Smuzhiyun all_ignored_cves = dict() 80*4882a593Smuzhiyun all_cpeids = dict() 81*4882a593Smuzhiyun # This is the list of all possible checks. Add new checks to this list so 82*4882a593Smuzhiyun # a tool that post-processeds the json output knows the checks before 83*4882a593Smuzhiyun # iterating over the packages. 84*4882a593Smuzhiyun status_checks = ['cve', 'developers', 'hash', 'license', 85*4882a593Smuzhiyun 'license-files', 'patches', 'pkg-check', 'url', 'version'] 86*4882a593Smuzhiyun 87*4882a593Smuzhiyun def __init__(self, name, path): 88*4882a593Smuzhiyun self.name = name 89*4882a593Smuzhiyun self.path = path 90*4882a593Smuzhiyun self.pkg_path = os.path.dirname(path) 91*4882a593Smuzhiyun self.infras = None 92*4882a593Smuzhiyun self.license = None 93*4882a593Smuzhiyun self.has_license = False 94*4882a593Smuzhiyun self.has_license_files = False 95*4882a593Smuzhiyun self.has_hash = False 96*4882a593Smuzhiyun self.patch_files = [] 97*4882a593Smuzhiyun self.warnings = 0 98*4882a593Smuzhiyun self.current_version = None 99*4882a593Smuzhiyun self.url = None 100*4882a593Smuzhiyun self.url_worker = None 101*4882a593Smuzhiyun self.cpeid = None 102*4882a593Smuzhiyun self.cves = list() 103*4882a593Smuzhiyun self.ignored_cves = list() 104*4882a593Smuzhiyun self.latest_version = {'status': RM_API_STATUS_ERROR, 'version': None, 'id': None} 105*4882a593Smuzhiyun self.status = {} 106*4882a593Smuzhiyun 107*4882a593Smuzhiyun def pkgvar(self): 108*4882a593Smuzhiyun return self.name.upper().replace("-", "_") 109*4882a593Smuzhiyun 110*4882a593Smuzhiyun def set_url(self): 111*4882a593Smuzhiyun """ 112*4882a593Smuzhiyun Fills in the .url field 113*4882a593Smuzhiyun """ 114*4882a593Smuzhiyun self.status['url'] = ("warning", "no Config.in") 115*4882a593Smuzhiyun pkgdir = os.path.dirname(os.path.join(brpath, self.path)) 116*4882a593Smuzhiyun for filename in os.listdir(pkgdir): 117*4882a593Smuzhiyun if fnmatch.fnmatch(filename, 'Config.*'): 118*4882a593Smuzhiyun fp = open(os.path.join(pkgdir, filename), "r") 119*4882a593Smuzhiyun for config_line in fp: 120*4882a593Smuzhiyun if URL_RE.match(config_line): 121*4882a593Smuzhiyun self.url = config_line.strip() 122*4882a593Smuzhiyun self.status['url'] = ("ok", "found") 123*4882a593Smuzhiyun fp.close() 124*4882a593Smuzhiyun return 125*4882a593Smuzhiyun self.status['url'] = ("error", "missing") 126*4882a593Smuzhiyun fp.close() 127*4882a593Smuzhiyun 128*4882a593Smuzhiyun @property 129*4882a593Smuzhiyun def patch_count(self): 130*4882a593Smuzhiyun return len(self.patch_files) 131*4882a593Smuzhiyun 132*4882a593Smuzhiyun @property 133*4882a593Smuzhiyun def has_valid_infra(self): 134*4882a593Smuzhiyun if self.infras is None: 135*4882a593Smuzhiyun return False 136*4882a593Smuzhiyun return len(self.infras) > 0 137*4882a593Smuzhiyun 138*4882a593Smuzhiyun @property 139*4882a593Smuzhiyun def is_actual_package(self): 140*4882a593Smuzhiyun try: 141*4882a593Smuzhiyun if not self.has_valid_infra: 142*4882a593Smuzhiyun return False 143*4882a593Smuzhiyun if self.infras[0][1] == 'virtual': 144*4882a593Smuzhiyun return False 145*4882a593Smuzhiyun except IndexError: 146*4882a593Smuzhiyun return False 147*4882a593Smuzhiyun return True 148*4882a593Smuzhiyun 149*4882a593Smuzhiyun def set_infra(self): 150*4882a593Smuzhiyun """ 151*4882a593Smuzhiyun Fills in the .infras field 152*4882a593Smuzhiyun """ 153*4882a593Smuzhiyun self.infras = list() 154*4882a593Smuzhiyun with open(os.path.join(brpath, self.path), 'r') as f: 155*4882a593Smuzhiyun lines = f.readlines() 156*4882a593Smuzhiyun for line in lines: 157*4882a593Smuzhiyun match = INFRA_RE.match(line) 158*4882a593Smuzhiyun if not match: 159*4882a593Smuzhiyun continue 160*4882a593Smuzhiyun infra = match.group(1) 161*4882a593Smuzhiyun if infra.startswith("host-"): 162*4882a593Smuzhiyun self.infras.append(("host", infra[5:])) 163*4882a593Smuzhiyun else: 164*4882a593Smuzhiyun self.infras.append(("target", infra)) 165*4882a593Smuzhiyun 166*4882a593Smuzhiyun def set_license(self): 167*4882a593Smuzhiyun """ 168*4882a593Smuzhiyun Fills in the .status['license'] and .status['license-files'] fields 169*4882a593Smuzhiyun """ 170*4882a593Smuzhiyun if not self.is_actual_package: 171*4882a593Smuzhiyun self.status['license'] = ("na", "no valid package infra") 172*4882a593Smuzhiyun self.status['license-files'] = ("na", "no valid package infra") 173*4882a593Smuzhiyun return 174*4882a593Smuzhiyun 175*4882a593Smuzhiyun var = self.pkgvar() 176*4882a593Smuzhiyun self.status['license'] = ("error", "missing") 177*4882a593Smuzhiyun self.status['license-files'] = ("error", "missing") 178*4882a593Smuzhiyun if var in self.all_licenses: 179*4882a593Smuzhiyun self.license = self.all_licenses[var] 180*4882a593Smuzhiyun self.status['license'] = ("ok", "found") 181*4882a593Smuzhiyun if var in self.all_license_files: 182*4882a593Smuzhiyun self.status['license-files'] = ("ok", "found") 183*4882a593Smuzhiyun 184*4882a593Smuzhiyun def set_hash_info(self): 185*4882a593Smuzhiyun """ 186*4882a593Smuzhiyun Fills in the .status['hash'] field 187*4882a593Smuzhiyun """ 188*4882a593Smuzhiyun if not self.is_actual_package: 189*4882a593Smuzhiyun self.status['hash'] = ("na", "no valid package infra") 190*4882a593Smuzhiyun self.status['hash-license'] = ("na", "no valid package infra") 191*4882a593Smuzhiyun return 192*4882a593Smuzhiyun 193*4882a593Smuzhiyun hashpath = self.path.replace(".mk", ".hash") 194*4882a593Smuzhiyun if os.path.exists(os.path.join(brpath, hashpath)): 195*4882a593Smuzhiyun self.status['hash'] = ("ok", "found") 196*4882a593Smuzhiyun else: 197*4882a593Smuzhiyun self.status['hash'] = ("error", "missing") 198*4882a593Smuzhiyun 199*4882a593Smuzhiyun def set_patch_count(self): 200*4882a593Smuzhiyun """ 201*4882a593Smuzhiyun Fills in the .patch_count, .patch_files and .status['patches'] fields 202*4882a593Smuzhiyun """ 203*4882a593Smuzhiyun if not self.is_actual_package: 204*4882a593Smuzhiyun self.status['patches'] = ("na", "no valid package infra") 205*4882a593Smuzhiyun return 206*4882a593Smuzhiyun 207*4882a593Smuzhiyun pkgdir = os.path.dirname(os.path.join(brpath, self.path)) 208*4882a593Smuzhiyun for subdir, _, _ in os.walk(pkgdir): 209*4882a593Smuzhiyun self.patch_files = fnmatch.filter(os.listdir(subdir), '*.patch') 210*4882a593Smuzhiyun 211*4882a593Smuzhiyun if self.patch_count == 0: 212*4882a593Smuzhiyun self.status['patches'] = ("ok", "no patches") 213*4882a593Smuzhiyun elif self.patch_count < 5: 214*4882a593Smuzhiyun self.status['patches'] = ("warning", "some patches") 215*4882a593Smuzhiyun else: 216*4882a593Smuzhiyun self.status['patches'] = ("error", "lots of patches") 217*4882a593Smuzhiyun 218*4882a593Smuzhiyun def set_current_version(self): 219*4882a593Smuzhiyun """ 220*4882a593Smuzhiyun Fills in the .current_version field 221*4882a593Smuzhiyun """ 222*4882a593Smuzhiyun var = self.pkgvar() 223*4882a593Smuzhiyun if var in self.all_versions: 224*4882a593Smuzhiyun self.current_version = self.all_versions[var] 225*4882a593Smuzhiyun 226*4882a593Smuzhiyun def set_cpeid(self): 227*4882a593Smuzhiyun """ 228*4882a593Smuzhiyun Fills in the .cpeid field 229*4882a593Smuzhiyun """ 230*4882a593Smuzhiyun var = self.pkgvar() 231*4882a593Smuzhiyun if not self.is_actual_package: 232*4882a593Smuzhiyun self.status['cpe'] = ("na", "N/A - virtual pkg") 233*4882a593Smuzhiyun return 234*4882a593Smuzhiyun if not self.current_version: 235*4882a593Smuzhiyun self.status['cpe'] = ("na", "no version information available") 236*4882a593Smuzhiyun return 237*4882a593Smuzhiyun 238*4882a593Smuzhiyun if var in self.all_cpeids: 239*4882a593Smuzhiyun self.cpeid = self.all_cpeids[var] 240*4882a593Smuzhiyun # Set a preliminary status, it might be overridden by check_package_cpes() 241*4882a593Smuzhiyun self.status['cpe'] = ("warning", "not checked against CPE dictionnary") 242*4882a593Smuzhiyun else: 243*4882a593Smuzhiyun self.status['cpe'] = ("error", "no verified CPE identifier") 244*4882a593Smuzhiyun 245*4882a593Smuzhiyun def set_check_package_warnings(self): 246*4882a593Smuzhiyun """ 247*4882a593Smuzhiyun Fills in the .warnings and .status['pkg-check'] fields 248*4882a593Smuzhiyun """ 249*4882a593Smuzhiyun cmd = [os.path.join(brpath, "utils/check-package")] 250*4882a593Smuzhiyun pkgdir = os.path.dirname(os.path.join(brpath, self.path)) 251*4882a593Smuzhiyun self.status['pkg-check'] = ("error", "Missing") 252*4882a593Smuzhiyun for root, dirs, files in os.walk(pkgdir): 253*4882a593Smuzhiyun for f in files: 254*4882a593Smuzhiyun if f.endswith(".mk") or f.endswith(".hash") or f == "Config.in" or f == "Config.in.host": 255*4882a593Smuzhiyun cmd.append(os.path.join(root, f)) 256*4882a593Smuzhiyun o = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[1] 257*4882a593Smuzhiyun lines = o.splitlines() 258*4882a593Smuzhiyun for line in lines: 259*4882a593Smuzhiyun m = re.match("^([0-9]*) warnings generated", line.decode()) 260*4882a593Smuzhiyun if m: 261*4882a593Smuzhiyun self.warnings = int(m.group(1)) 262*4882a593Smuzhiyun if self.warnings == 0: 263*4882a593Smuzhiyun self.status['pkg-check'] = ("ok", "no warnings") 264*4882a593Smuzhiyun else: 265*4882a593Smuzhiyun self.status['pkg-check'] = ("error", "{} warnings".format(self.warnings)) 266*4882a593Smuzhiyun return 267*4882a593Smuzhiyun 268*4882a593Smuzhiyun def set_ignored_cves(self): 269*4882a593Smuzhiyun """ 270*4882a593Smuzhiyun Give the list of CVEs ignored by the package 271*4882a593Smuzhiyun """ 272*4882a593Smuzhiyun self.ignored_cves = list(self.all_ignored_cves.get(self.pkgvar(), [])) 273*4882a593Smuzhiyun 274*4882a593Smuzhiyun def set_developers(self, developers): 275*4882a593Smuzhiyun """ 276*4882a593Smuzhiyun Fills in the .developers and .status['developers'] field 277*4882a593Smuzhiyun """ 278*4882a593Smuzhiyun self.developers = [ 279*4882a593Smuzhiyun dev.name 280*4882a593Smuzhiyun for dev in developers 281*4882a593Smuzhiyun if dev.hasfile(self.path) 282*4882a593Smuzhiyun ] 283*4882a593Smuzhiyun 284*4882a593Smuzhiyun if self.developers: 285*4882a593Smuzhiyun self.status['developers'] = ("ok", "{} developers".format(len(self.developers))) 286*4882a593Smuzhiyun else: 287*4882a593Smuzhiyun self.status['developers'] = ("warning", "no developers") 288*4882a593Smuzhiyun 289*4882a593Smuzhiyun def is_status_ok(self, name): 290*4882a593Smuzhiyun return name in self.status and self.status[name][0] == 'ok' 291*4882a593Smuzhiyun 292*4882a593Smuzhiyun def is_status_error(self, name): 293*4882a593Smuzhiyun return name in self.status and self.status[name][0] == 'error' 294*4882a593Smuzhiyun 295*4882a593Smuzhiyun def is_status_na(self, name): 296*4882a593Smuzhiyun return name in self.status and self.status[name][0] == 'na' 297*4882a593Smuzhiyun 298*4882a593Smuzhiyun def __eq__(self, other): 299*4882a593Smuzhiyun return self.path == other.path 300*4882a593Smuzhiyun 301*4882a593Smuzhiyun def __lt__(self, other): 302*4882a593Smuzhiyun return self.path < other.path 303*4882a593Smuzhiyun 304*4882a593Smuzhiyun def __str__(self): 305*4882a593Smuzhiyun return "%s (path='%s', license='%s', license_files='%s', hash='%s', patches=%d)" % \ 306*4882a593Smuzhiyun (self.name, self.path, self.is_status_ok('license'), 307*4882a593Smuzhiyun self.is_status_ok('license-files'), self.status['hash'], self.patch_count) 308*4882a593Smuzhiyun 309*4882a593Smuzhiyun 310*4882a593Smuzhiyundef get_pkglist(npackages, package_list): 311*4882a593Smuzhiyun """ 312*4882a593Smuzhiyun Builds the list of Buildroot packages, returning a list of Package 313*4882a593Smuzhiyun objects. Only the .name and .path fields of the Package object are 314*4882a593Smuzhiyun initialized. 315*4882a593Smuzhiyun 316*4882a593Smuzhiyun npackages: limit to N packages 317*4882a593Smuzhiyun package_list: limit to those packages in this list 318*4882a593Smuzhiyun """ 319*4882a593Smuzhiyun WALK_USEFUL_SUBDIRS = ["boot", "linux", "package", "toolchain"] 320*4882a593Smuzhiyun WALK_EXCLUDES = ["boot/common.mk", 321*4882a593Smuzhiyun "linux/linux-ext-.*.mk", 322*4882a593Smuzhiyun "package/freescale-imx/freescale-imx.mk", 323*4882a593Smuzhiyun "package/gcc/gcc.mk", 324*4882a593Smuzhiyun "package/gstreamer/gstreamer.mk", 325*4882a593Smuzhiyun "package/gstreamer1/gstreamer1.mk", 326*4882a593Smuzhiyun "package/gtk2-themes/gtk2-themes.mk", 327*4882a593Smuzhiyun "package/matchbox/matchbox.mk", 328*4882a593Smuzhiyun "package/opengl/opengl.mk", 329*4882a593Smuzhiyun "package/qt5/qt5.mk", 330*4882a593Smuzhiyun "package/x11r7/x11r7.mk", 331*4882a593Smuzhiyun "package/doc-asciidoc.mk", 332*4882a593Smuzhiyun "package/pkg-.*.mk", 333*4882a593Smuzhiyun "toolchain/toolchain-external/pkg-toolchain-external.mk", 334*4882a593Smuzhiyun "toolchain/toolchain-external/toolchain-external.mk", 335*4882a593Smuzhiyun "toolchain/toolchain.mk", 336*4882a593Smuzhiyun "toolchain/helpers.mk", 337*4882a593Smuzhiyun "toolchain/toolchain-wrapper.mk"] 338*4882a593Smuzhiyun packages = list() 339*4882a593Smuzhiyun count = 0 340*4882a593Smuzhiyun for root, dirs, files in os.walk(brpath): 341*4882a593Smuzhiyun root = os.path.relpath(root, brpath) 342*4882a593Smuzhiyun rootdir = root.split("/") 343*4882a593Smuzhiyun if len(rootdir) < 1: 344*4882a593Smuzhiyun continue 345*4882a593Smuzhiyun if rootdir[0] not in WALK_USEFUL_SUBDIRS: 346*4882a593Smuzhiyun continue 347*4882a593Smuzhiyun for f in files: 348*4882a593Smuzhiyun if not f.endswith(".mk"): 349*4882a593Smuzhiyun continue 350*4882a593Smuzhiyun # Strip ending ".mk" 351*4882a593Smuzhiyun pkgname = f[:-3] 352*4882a593Smuzhiyun if package_list and pkgname not in package_list: 353*4882a593Smuzhiyun continue 354*4882a593Smuzhiyun pkgpath = os.path.join(root, f) 355*4882a593Smuzhiyun skip = False 356*4882a593Smuzhiyun for exclude in WALK_EXCLUDES: 357*4882a593Smuzhiyun if re.match(exclude, pkgpath): 358*4882a593Smuzhiyun skip = True 359*4882a593Smuzhiyun continue 360*4882a593Smuzhiyun if skip: 361*4882a593Smuzhiyun continue 362*4882a593Smuzhiyun p = Package(pkgname, pkgpath) 363*4882a593Smuzhiyun packages.append(p) 364*4882a593Smuzhiyun count += 1 365*4882a593Smuzhiyun if npackages and count == npackages: 366*4882a593Smuzhiyun return packages 367*4882a593Smuzhiyun return packages 368*4882a593Smuzhiyun 369*4882a593Smuzhiyun 370*4882a593Smuzhiyundef get_config_packages(): 371*4882a593Smuzhiyun cmd = ["make", "--no-print-directory", "show-info"] 372*4882a593Smuzhiyun js = json.loads(subprocess.check_output(cmd)) 373*4882a593Smuzhiyun return set([v["name"] for v in js.values()]) 374*4882a593Smuzhiyun 375*4882a593Smuzhiyun 376*4882a593Smuzhiyundef package_init_make_info(): 377*4882a593Smuzhiyun # Fetch all variables at once 378*4882a593Smuzhiyun variables = subprocess.check_output(["make", "BR2_HAVE_DOT_CONFIG=y", "-s", "printvars", 379*4882a593Smuzhiyun "VARS=%_LICENSE %_LICENSE_FILES %_VERSION %_IGNORE_CVES %_CPE_ID"]) 380*4882a593Smuzhiyun variable_list = variables.decode().splitlines() 381*4882a593Smuzhiyun 382*4882a593Smuzhiyun # We process first the host package VERSION, and then the target 383*4882a593Smuzhiyun # package VERSION. This means that if a package exists in both 384*4882a593Smuzhiyun # target and host variants, with different values (eg. version 385*4882a593Smuzhiyun # numbers (unlikely)), we'll report the target one. 386*4882a593Smuzhiyun variable_list = [x[5:] for x in variable_list if x.startswith("HOST_")] + \ 387*4882a593Smuzhiyun [x for x in variable_list if not x.startswith("HOST_")] 388*4882a593Smuzhiyun 389*4882a593Smuzhiyun for item in variable_list: 390*4882a593Smuzhiyun # Get variable name and value 391*4882a593Smuzhiyun pkgvar, value = item.split("=", maxsplit=1) 392*4882a593Smuzhiyun 393*4882a593Smuzhiyun # Strip the suffix according to the variable 394*4882a593Smuzhiyun if pkgvar.endswith("_LICENSE"): 395*4882a593Smuzhiyun # If value is "unknown", no license details available 396*4882a593Smuzhiyun if value == "unknown": 397*4882a593Smuzhiyun continue 398*4882a593Smuzhiyun pkgvar = pkgvar[:-8] 399*4882a593Smuzhiyun Package.all_licenses[pkgvar] = value 400*4882a593Smuzhiyun 401*4882a593Smuzhiyun elif pkgvar.endswith("_LICENSE_FILES"): 402*4882a593Smuzhiyun if pkgvar.endswith("_MANIFEST_LICENSE_FILES"): 403*4882a593Smuzhiyun continue 404*4882a593Smuzhiyun pkgvar = pkgvar[:-14] 405*4882a593Smuzhiyun Package.all_license_files.append(pkgvar) 406*4882a593Smuzhiyun 407*4882a593Smuzhiyun elif pkgvar.endswith("_VERSION"): 408*4882a593Smuzhiyun if pkgvar.endswith("_DL_VERSION"): 409*4882a593Smuzhiyun continue 410*4882a593Smuzhiyun pkgvar = pkgvar[:-8] 411*4882a593Smuzhiyun Package.all_versions[pkgvar] = value 412*4882a593Smuzhiyun 413*4882a593Smuzhiyun elif pkgvar.endswith("_IGNORE_CVES"): 414*4882a593Smuzhiyun pkgvar = pkgvar[:-12] 415*4882a593Smuzhiyun Package.all_ignored_cves[pkgvar] = value.split() 416*4882a593Smuzhiyun 417*4882a593Smuzhiyun elif pkgvar.endswith("_CPE_ID"): 418*4882a593Smuzhiyun pkgvar = pkgvar[:-7] 419*4882a593Smuzhiyun Package.all_cpeids[pkgvar] = value 420*4882a593Smuzhiyun 421*4882a593Smuzhiyun 422*4882a593Smuzhiyuncheck_url_count = 0 423*4882a593Smuzhiyun 424*4882a593Smuzhiyun 425*4882a593Smuzhiyunasync def check_url_status(session, pkg, npkgs, retry=True): 426*4882a593Smuzhiyun global check_url_count 427*4882a593Smuzhiyun 428*4882a593Smuzhiyun try: 429*4882a593Smuzhiyun async with session.get(pkg.url) as resp: 430*4882a593Smuzhiyun if resp.status >= 400: 431*4882a593Smuzhiyun pkg.status['url'] = ("error", "invalid {}".format(resp.status)) 432*4882a593Smuzhiyun check_url_count += 1 433*4882a593Smuzhiyun print("[%04d/%04d] %s" % (check_url_count, npkgs, pkg.name)) 434*4882a593Smuzhiyun return 435*4882a593Smuzhiyun except (aiohttp.ClientError, asyncio.TimeoutError): 436*4882a593Smuzhiyun if retry: 437*4882a593Smuzhiyun return await check_url_status(session, pkg, npkgs, retry=False) 438*4882a593Smuzhiyun else: 439*4882a593Smuzhiyun pkg.status['url'] = ("error", "invalid (err)") 440*4882a593Smuzhiyun check_url_count += 1 441*4882a593Smuzhiyun print("[%04d/%04d] %s" % (check_url_count, npkgs, pkg.name)) 442*4882a593Smuzhiyun return 443*4882a593Smuzhiyun 444*4882a593Smuzhiyun pkg.status['url'] = ("ok", "valid") 445*4882a593Smuzhiyun check_url_count += 1 446*4882a593Smuzhiyun print("[%04d/%04d] %s" % (check_url_count, npkgs, pkg.name)) 447*4882a593Smuzhiyun 448*4882a593Smuzhiyun 449*4882a593Smuzhiyunasync def check_package_urls(packages): 450*4882a593Smuzhiyun tasks = [] 451*4882a593Smuzhiyun connector = aiohttp.TCPConnector(limit_per_host=5) 452*4882a593Smuzhiyun async with aiohttp.ClientSession(connector=connector, trust_env=True) as sess: 453*4882a593Smuzhiyun packages = [p for p in packages if p.status['url'][0] == 'ok'] 454*4882a593Smuzhiyun for pkg in packages: 455*4882a593Smuzhiyun tasks.append(asyncio.ensure_future(check_url_status(sess, pkg, len(packages)))) 456*4882a593Smuzhiyun await asyncio.wait(tasks) 457*4882a593Smuzhiyun 458*4882a593Smuzhiyun 459*4882a593Smuzhiyundef check_package_latest_version_set_status(pkg, status, version, identifier): 460*4882a593Smuzhiyun pkg.latest_version = { 461*4882a593Smuzhiyun "status": status, 462*4882a593Smuzhiyun "version": version, 463*4882a593Smuzhiyun "id": identifier, 464*4882a593Smuzhiyun } 465*4882a593Smuzhiyun 466*4882a593Smuzhiyun if pkg.latest_version['status'] == RM_API_STATUS_ERROR: 467*4882a593Smuzhiyun pkg.status['version'] = ('warning', "Release Monitoring API error") 468*4882a593Smuzhiyun elif pkg.latest_version['status'] == RM_API_STATUS_NOT_FOUND: 469*4882a593Smuzhiyun pkg.status['version'] = ('warning', "Package not found on Release Monitoring") 470*4882a593Smuzhiyun 471*4882a593Smuzhiyun if pkg.latest_version['version'] is None: 472*4882a593Smuzhiyun pkg.status['version'] = ('warning', "No upstream version available on Release Monitoring") 473*4882a593Smuzhiyun elif pkg.latest_version['version'] != pkg.current_version: 474*4882a593Smuzhiyun pkg.status['version'] = ('error', "The newer version {} is available upstream".format(pkg.latest_version['version'])) 475*4882a593Smuzhiyun else: 476*4882a593Smuzhiyun pkg.status['version'] = ('ok', 'up-to-date') 477*4882a593Smuzhiyun 478*4882a593Smuzhiyun 479*4882a593Smuzhiyunasync def check_package_get_latest_version_by_distro(session, pkg, retry=True): 480*4882a593Smuzhiyun url = "https://release-monitoring.org//api/project/Buildroot/%s" % pkg.name 481*4882a593Smuzhiyun try: 482*4882a593Smuzhiyun async with session.get(url) as resp: 483*4882a593Smuzhiyun if resp.status != 200: 484*4882a593Smuzhiyun return False 485*4882a593Smuzhiyun 486*4882a593Smuzhiyun data = await resp.json() 487*4882a593Smuzhiyun version = data['stable_versions'][0] if 'stable_versions' in data else data['version'] if 'version' in data else None 488*4882a593Smuzhiyun check_package_latest_version_set_status(pkg, 489*4882a593Smuzhiyun RM_API_STATUS_FOUND_BY_DISTRO, 490*4882a593Smuzhiyun version, 491*4882a593Smuzhiyun data['id']) 492*4882a593Smuzhiyun return True 493*4882a593Smuzhiyun 494*4882a593Smuzhiyun except (aiohttp.ClientError, asyncio.TimeoutError): 495*4882a593Smuzhiyun if retry: 496*4882a593Smuzhiyun return await check_package_get_latest_version_by_distro(session, pkg, retry=False) 497*4882a593Smuzhiyun else: 498*4882a593Smuzhiyun return False 499*4882a593Smuzhiyun 500*4882a593Smuzhiyun 501*4882a593Smuzhiyunasync def check_package_get_latest_version_by_guess(session, pkg, retry=True): 502*4882a593Smuzhiyun url = "https://release-monitoring.org/api/projects/?pattern=%s" % pkg.name 503*4882a593Smuzhiyun try: 504*4882a593Smuzhiyun async with session.get(url) as resp: 505*4882a593Smuzhiyun if resp.status != 200: 506*4882a593Smuzhiyun return False 507*4882a593Smuzhiyun 508*4882a593Smuzhiyun data = await resp.json() 509*4882a593Smuzhiyun # filter projects that have the right name and a version defined 510*4882a593Smuzhiyun projects = [p for p in data['projects'] if p['name'] == pkg.name and 'stable_versions' in p] 511*4882a593Smuzhiyun projects.sort(key=lambda x: x['id']) 512*4882a593Smuzhiyun 513*4882a593Smuzhiyun if len(projects) > 0: 514*4882a593Smuzhiyun check_package_latest_version_set_status(pkg, 515*4882a593Smuzhiyun RM_API_STATUS_FOUND_BY_PATTERN, 516*4882a593Smuzhiyun projects[0]['stable_versions'][0], 517*4882a593Smuzhiyun projects[0]['id']) 518*4882a593Smuzhiyun return True 519*4882a593Smuzhiyun 520*4882a593Smuzhiyun except (aiohttp.ClientError, asyncio.TimeoutError): 521*4882a593Smuzhiyun if retry: 522*4882a593Smuzhiyun return await check_package_get_latest_version_by_guess(session, pkg, retry=False) 523*4882a593Smuzhiyun else: 524*4882a593Smuzhiyun return False 525*4882a593Smuzhiyun 526*4882a593Smuzhiyun 527*4882a593Smuzhiyuncheck_latest_count = 0 528*4882a593Smuzhiyun 529*4882a593Smuzhiyun 530*4882a593Smuzhiyunasync def check_package_latest_version_get(session, pkg, npkgs): 531*4882a593Smuzhiyun global check_latest_count 532*4882a593Smuzhiyun 533*4882a593Smuzhiyun if await check_package_get_latest_version_by_distro(session, pkg): 534*4882a593Smuzhiyun check_latest_count += 1 535*4882a593Smuzhiyun print("[%04d/%04d] %s" % (check_latest_count, npkgs, pkg.name)) 536*4882a593Smuzhiyun return 537*4882a593Smuzhiyun 538*4882a593Smuzhiyun if await check_package_get_latest_version_by_guess(session, pkg): 539*4882a593Smuzhiyun check_latest_count += 1 540*4882a593Smuzhiyun print("[%04d/%04d] %s" % (check_latest_count, npkgs, pkg.name)) 541*4882a593Smuzhiyun return 542*4882a593Smuzhiyun 543*4882a593Smuzhiyun check_package_latest_version_set_status(pkg, 544*4882a593Smuzhiyun RM_API_STATUS_NOT_FOUND, 545*4882a593Smuzhiyun None, None) 546*4882a593Smuzhiyun check_latest_count += 1 547*4882a593Smuzhiyun print("[%04d/%04d] %s" % (check_latest_count, npkgs, pkg.name)) 548*4882a593Smuzhiyun 549*4882a593Smuzhiyun 550*4882a593Smuzhiyunasync def check_package_latest_version(packages): 551*4882a593Smuzhiyun """ 552*4882a593Smuzhiyun Fills in the .latest_version field of all Package objects 553*4882a593Smuzhiyun 554*4882a593Smuzhiyun This field is a dict and has the following keys: 555*4882a593Smuzhiyun 556*4882a593Smuzhiyun - status: one of RM_API_STATUS_ERROR, 557*4882a593Smuzhiyun RM_API_STATUS_FOUND_BY_DISTRO, RM_API_STATUS_FOUND_BY_PATTERN, 558*4882a593Smuzhiyun RM_API_STATUS_NOT_FOUND 559*4882a593Smuzhiyun - version: string containing the latest version known by 560*4882a593Smuzhiyun release-monitoring.org for this package 561*4882a593Smuzhiyun - id: string containing the id of the project corresponding to this 562*4882a593Smuzhiyun package, as known by release-monitoring.org 563*4882a593Smuzhiyun """ 564*4882a593Smuzhiyun 565*4882a593Smuzhiyun for pkg in [p for p in packages if not p.is_actual_package]: 566*4882a593Smuzhiyun pkg.status['version'] = ("na", "no valid package infra") 567*4882a593Smuzhiyun 568*4882a593Smuzhiyun tasks = [] 569*4882a593Smuzhiyun connector = aiohttp.TCPConnector(limit_per_host=5) 570*4882a593Smuzhiyun async with aiohttp.ClientSession(connector=connector, trust_env=True) as sess: 571*4882a593Smuzhiyun packages = [p for p in packages if p.is_actual_package] 572*4882a593Smuzhiyun for pkg in packages: 573*4882a593Smuzhiyun tasks.append(asyncio.ensure_future(check_package_latest_version_get(sess, pkg, len(packages)))) 574*4882a593Smuzhiyun await asyncio.wait(tasks) 575*4882a593Smuzhiyun 576*4882a593Smuzhiyun 577*4882a593Smuzhiyundef check_package_cve_affects(cve, cpe_product_pkgs): 578*4882a593Smuzhiyun for product in cve.affected_products: 579*4882a593Smuzhiyun if product not in cpe_product_pkgs: 580*4882a593Smuzhiyun continue 581*4882a593Smuzhiyun for pkg in cpe_product_pkgs[product]: 582*4882a593Smuzhiyun if cve.affects(pkg.name, pkg.current_version, pkg.ignored_cves, pkg.cpeid) == cve.CVE_AFFECTS: 583*4882a593Smuzhiyun pkg.cves.append(cve.identifier) 584*4882a593Smuzhiyun 585*4882a593Smuzhiyun 586*4882a593Smuzhiyundef check_package_cves(nvd_path, packages): 587*4882a593Smuzhiyun if not os.path.isdir(nvd_path): 588*4882a593Smuzhiyun os.makedirs(nvd_path) 589*4882a593Smuzhiyun 590*4882a593Smuzhiyun cpe_product_pkgs = defaultdict(list) 591*4882a593Smuzhiyun for pkg in packages: 592*4882a593Smuzhiyun if not pkg.is_actual_package: 593*4882a593Smuzhiyun pkg.status['cve'] = ("na", "N/A") 594*4882a593Smuzhiyun continue 595*4882a593Smuzhiyun if not pkg.current_version: 596*4882a593Smuzhiyun pkg.status['cve'] = ("na", "no version information available") 597*4882a593Smuzhiyun continue 598*4882a593Smuzhiyun if pkg.cpeid: 599*4882a593Smuzhiyun cpe_product = cvecheck.cpe_product(pkg.cpeid) 600*4882a593Smuzhiyun cpe_product_pkgs[cpe_product].append(pkg) 601*4882a593Smuzhiyun else: 602*4882a593Smuzhiyun cpe_product_pkgs[pkg.name].append(pkg) 603*4882a593Smuzhiyun 604*4882a593Smuzhiyun for cve in cvecheck.CVE.read_nvd_dir(nvd_path): 605*4882a593Smuzhiyun check_package_cve_affects(cve, cpe_product_pkgs) 606*4882a593Smuzhiyun 607*4882a593Smuzhiyun for pkg in packages: 608*4882a593Smuzhiyun if 'cve' not in pkg.status: 609*4882a593Smuzhiyun if pkg.cves: 610*4882a593Smuzhiyun pkg.status['cve'] = ("error", "affected by CVEs") 611*4882a593Smuzhiyun else: 612*4882a593Smuzhiyun pkg.status['cve'] = ("ok", "not affected by CVEs") 613*4882a593Smuzhiyun 614*4882a593Smuzhiyun 615*4882a593Smuzhiyundef check_package_cpes(nvd_path, packages): 616*4882a593Smuzhiyun cpedb = CPEDB(nvd_path) 617*4882a593Smuzhiyun cpedb.get_xml_dict() 618*4882a593Smuzhiyun for p in packages: 619*4882a593Smuzhiyun if not p.cpeid: 620*4882a593Smuzhiyun continue 621*4882a593Smuzhiyun if cpedb.find(p.cpeid): 622*4882a593Smuzhiyun p.status['cpe'] = ("ok", "verified CPE identifier") 623*4882a593Smuzhiyun else: 624*4882a593Smuzhiyun p.status['cpe'] = ("error", "CPE version unknown in CPE database") 625*4882a593Smuzhiyun 626*4882a593Smuzhiyun 627*4882a593Smuzhiyundef calculate_stats(packages): 628*4882a593Smuzhiyun stats = defaultdict(int) 629*4882a593Smuzhiyun stats['packages'] = len(packages) 630*4882a593Smuzhiyun for pkg in packages: 631*4882a593Smuzhiyun # If packages have multiple infra, take the first one. For the 632*4882a593Smuzhiyun # vast majority of packages, the target and host infra are the 633*4882a593Smuzhiyun # same. There are very few packages that use a different infra 634*4882a593Smuzhiyun # for the host and target variants. 635*4882a593Smuzhiyun if len(pkg.infras) > 0: 636*4882a593Smuzhiyun infra = pkg.infras[0][1] 637*4882a593Smuzhiyun stats["infra-%s" % infra] += 1 638*4882a593Smuzhiyun else: 639*4882a593Smuzhiyun stats["infra-unknown"] += 1 640*4882a593Smuzhiyun if pkg.is_status_ok('license'): 641*4882a593Smuzhiyun stats["license"] += 1 642*4882a593Smuzhiyun else: 643*4882a593Smuzhiyun stats["no-license"] += 1 644*4882a593Smuzhiyun if pkg.is_status_ok('license-files'): 645*4882a593Smuzhiyun stats["license-files"] += 1 646*4882a593Smuzhiyun else: 647*4882a593Smuzhiyun stats["no-license-files"] += 1 648*4882a593Smuzhiyun if pkg.is_status_ok('hash'): 649*4882a593Smuzhiyun stats["hash"] += 1 650*4882a593Smuzhiyun else: 651*4882a593Smuzhiyun stats["no-hash"] += 1 652*4882a593Smuzhiyun if pkg.latest_version['status'] == RM_API_STATUS_FOUND_BY_DISTRO: 653*4882a593Smuzhiyun stats["rmo-mapping"] += 1 654*4882a593Smuzhiyun else: 655*4882a593Smuzhiyun stats["rmo-no-mapping"] += 1 656*4882a593Smuzhiyun if not pkg.latest_version['version']: 657*4882a593Smuzhiyun stats["version-unknown"] += 1 658*4882a593Smuzhiyun elif pkg.latest_version['version'] == pkg.current_version: 659*4882a593Smuzhiyun stats["version-uptodate"] += 1 660*4882a593Smuzhiyun else: 661*4882a593Smuzhiyun stats["version-not-uptodate"] += 1 662*4882a593Smuzhiyun stats["patches"] += pkg.patch_count 663*4882a593Smuzhiyun stats["total-cves"] += len(pkg.cves) 664*4882a593Smuzhiyun if len(pkg.cves) != 0: 665*4882a593Smuzhiyun stats["pkg-cves"] += 1 666*4882a593Smuzhiyun if pkg.cpeid: 667*4882a593Smuzhiyun stats["cpe-id"] += 1 668*4882a593Smuzhiyun else: 669*4882a593Smuzhiyun stats["no-cpe-id"] += 1 670*4882a593Smuzhiyun return stats 671*4882a593Smuzhiyun 672*4882a593Smuzhiyun 673*4882a593Smuzhiyunhtml_header = """ 674*4882a593Smuzhiyun<head> 675*4882a593Smuzhiyun<script src=\"https://www.kryogenix.org/code/browser/sorttable/sorttable.js\"></script> 676*4882a593Smuzhiyun<style type=\"text/css\"> 677*4882a593Smuzhiyuntable { 678*4882a593Smuzhiyun width: 100%; 679*4882a593Smuzhiyun} 680*4882a593Smuzhiyuntd { 681*4882a593Smuzhiyun border: 1px solid black; 682*4882a593Smuzhiyun} 683*4882a593Smuzhiyuntd.centered { 684*4882a593Smuzhiyun text-align: center; 685*4882a593Smuzhiyun} 686*4882a593Smuzhiyuntd.wrong { 687*4882a593Smuzhiyun background: #ff9a69; 688*4882a593Smuzhiyun} 689*4882a593Smuzhiyuntd.correct { 690*4882a593Smuzhiyun background: #d2ffc4; 691*4882a593Smuzhiyun} 692*4882a593Smuzhiyuntd.nopatches { 693*4882a593Smuzhiyun background: #d2ffc4; 694*4882a593Smuzhiyun} 695*4882a593Smuzhiyuntd.somepatches { 696*4882a593Smuzhiyun background: #ffd870; 697*4882a593Smuzhiyun} 698*4882a593Smuzhiyuntd.lotsofpatches { 699*4882a593Smuzhiyun background: #ff9a69; 700*4882a593Smuzhiyun} 701*4882a593Smuzhiyun 702*4882a593Smuzhiyuntd.good_url { 703*4882a593Smuzhiyun background: #d2ffc4; 704*4882a593Smuzhiyun} 705*4882a593Smuzhiyuntd.missing_url { 706*4882a593Smuzhiyun background: #ffd870; 707*4882a593Smuzhiyun} 708*4882a593Smuzhiyuntd.invalid_url { 709*4882a593Smuzhiyun background: #ff9a69; 710*4882a593Smuzhiyun} 711*4882a593Smuzhiyun 712*4882a593Smuzhiyuntd.version-good { 713*4882a593Smuzhiyun background: #d2ffc4; 714*4882a593Smuzhiyun} 715*4882a593Smuzhiyuntd.version-needs-update { 716*4882a593Smuzhiyun background: #ff9a69; 717*4882a593Smuzhiyun} 718*4882a593Smuzhiyuntd.version-unknown { 719*4882a593Smuzhiyun background: #ffd870; 720*4882a593Smuzhiyun} 721*4882a593Smuzhiyuntd.version-error { 722*4882a593Smuzhiyun background: #ccc; 723*4882a593Smuzhiyun} 724*4882a593Smuzhiyun 725*4882a593Smuzhiyuntd.cpe-ok { 726*4882a593Smuzhiyun background: #d2ffc4; 727*4882a593Smuzhiyun} 728*4882a593Smuzhiyun 729*4882a593Smuzhiyuntd.cpe-nok { 730*4882a593Smuzhiyun background: #ff9a69; 731*4882a593Smuzhiyun} 732*4882a593Smuzhiyun 733*4882a593Smuzhiyuntd.cpe-unknown { 734*4882a593Smuzhiyun background: #ffd870; 735*4882a593Smuzhiyun} 736*4882a593Smuzhiyun 737*4882a593Smuzhiyuntd.cve-ok { 738*4882a593Smuzhiyun background: #d2ffc4; 739*4882a593Smuzhiyun} 740*4882a593Smuzhiyun 741*4882a593Smuzhiyuntd.cve-nok { 742*4882a593Smuzhiyun background: #ff9a69; 743*4882a593Smuzhiyun} 744*4882a593Smuzhiyun 745*4882a593Smuzhiyuntd.cve-unknown { 746*4882a593Smuzhiyun background: #ffd870; 747*4882a593Smuzhiyun} 748*4882a593Smuzhiyun 749*4882a593Smuzhiyuntd.cve_ignored { 750*4882a593Smuzhiyun background: #ccc; 751*4882a593Smuzhiyun} 752*4882a593Smuzhiyun 753*4882a593Smuzhiyun</style> 754*4882a593Smuzhiyun<title>Statistics of Buildroot packages</title> 755*4882a593Smuzhiyun</head> 756*4882a593Smuzhiyun 757*4882a593Smuzhiyun<a href=\"#results\">Results</a><br/> 758*4882a593Smuzhiyun 759*4882a593Smuzhiyun<p id=\"sortable_hint\"></p> 760*4882a593Smuzhiyun""" 761*4882a593Smuzhiyun 762*4882a593Smuzhiyun 763*4882a593Smuzhiyunhtml_footer = """ 764*4882a593Smuzhiyun</body> 765*4882a593Smuzhiyun<script> 766*4882a593Smuzhiyunif (typeof sorttable === \"object\") { 767*4882a593Smuzhiyun document.getElementById(\"sortable_hint\").innerHTML = 768*4882a593Smuzhiyun \"hint: the table can be sorted by clicking the column headers\" 769*4882a593Smuzhiyun} 770*4882a593Smuzhiyun</script> 771*4882a593Smuzhiyun</html> 772*4882a593Smuzhiyun""" 773*4882a593Smuzhiyun 774*4882a593Smuzhiyun 775*4882a593Smuzhiyundef infra_str(infra_list): 776*4882a593Smuzhiyun if not infra_list: 777*4882a593Smuzhiyun return "Unknown" 778*4882a593Smuzhiyun elif len(infra_list) == 1: 779*4882a593Smuzhiyun return "<b>%s</b><br/>%s" % (infra_list[0][1], infra_list[0][0]) 780*4882a593Smuzhiyun elif infra_list[0][1] == infra_list[1][1]: 781*4882a593Smuzhiyun return "<b>%s</b><br/>%s + %s" % \ 782*4882a593Smuzhiyun (infra_list[0][1], infra_list[0][0], infra_list[1][0]) 783*4882a593Smuzhiyun else: 784*4882a593Smuzhiyun return "<b>%s</b> (%s)<br/><b>%s</b> (%s)" % \ 785*4882a593Smuzhiyun (infra_list[0][1], infra_list[0][0], 786*4882a593Smuzhiyun infra_list[1][1], infra_list[1][0]) 787*4882a593Smuzhiyun 788*4882a593Smuzhiyun 789*4882a593Smuzhiyundef boolean_str(b): 790*4882a593Smuzhiyun if b: 791*4882a593Smuzhiyun return "Yes" 792*4882a593Smuzhiyun else: 793*4882a593Smuzhiyun return "No" 794*4882a593Smuzhiyun 795*4882a593Smuzhiyun 796*4882a593Smuzhiyundef dump_html_pkg(f, pkg): 797*4882a593Smuzhiyun f.write(" <tr>\n") 798*4882a593Smuzhiyun f.write(" <td>%s</td>\n" % pkg.path) 799*4882a593Smuzhiyun 800*4882a593Smuzhiyun # Patch count 801*4882a593Smuzhiyun td_class = ["centered"] 802*4882a593Smuzhiyun if pkg.patch_count == 0: 803*4882a593Smuzhiyun td_class.append("nopatches") 804*4882a593Smuzhiyun elif pkg.patch_count < 5: 805*4882a593Smuzhiyun td_class.append("somepatches") 806*4882a593Smuzhiyun else: 807*4882a593Smuzhiyun td_class.append("lotsofpatches") 808*4882a593Smuzhiyun f.write(" <td class=\"%s\">%s</td>\n" % 809*4882a593Smuzhiyun (" ".join(td_class), str(pkg.patch_count))) 810*4882a593Smuzhiyun 811*4882a593Smuzhiyun # Infrastructure 812*4882a593Smuzhiyun infra = infra_str(pkg.infras) 813*4882a593Smuzhiyun td_class = ["centered"] 814*4882a593Smuzhiyun if infra == "Unknown": 815*4882a593Smuzhiyun td_class.append("wrong") 816*4882a593Smuzhiyun else: 817*4882a593Smuzhiyun td_class.append("correct") 818*4882a593Smuzhiyun f.write(" <td class=\"%s\">%s</td>\n" % 819*4882a593Smuzhiyun (" ".join(td_class), infra_str(pkg.infras))) 820*4882a593Smuzhiyun 821*4882a593Smuzhiyun # License 822*4882a593Smuzhiyun td_class = ["centered"] 823*4882a593Smuzhiyun if pkg.is_status_ok('license'): 824*4882a593Smuzhiyun td_class.append("correct") 825*4882a593Smuzhiyun else: 826*4882a593Smuzhiyun td_class.append("wrong") 827*4882a593Smuzhiyun f.write(" <td class=\"%s\">%s</td>\n" % 828*4882a593Smuzhiyun (" ".join(td_class), boolean_str(pkg.is_status_ok('license')))) 829*4882a593Smuzhiyun 830*4882a593Smuzhiyun # License files 831*4882a593Smuzhiyun td_class = ["centered"] 832*4882a593Smuzhiyun if pkg.is_status_ok('license-files'): 833*4882a593Smuzhiyun td_class.append("correct") 834*4882a593Smuzhiyun else: 835*4882a593Smuzhiyun td_class.append("wrong") 836*4882a593Smuzhiyun f.write(" <td class=\"%s\">%s</td>\n" % 837*4882a593Smuzhiyun (" ".join(td_class), boolean_str(pkg.is_status_ok('license-files')))) 838*4882a593Smuzhiyun 839*4882a593Smuzhiyun # Hash 840*4882a593Smuzhiyun td_class = ["centered"] 841*4882a593Smuzhiyun if pkg.is_status_ok('hash'): 842*4882a593Smuzhiyun td_class.append("correct") 843*4882a593Smuzhiyun else: 844*4882a593Smuzhiyun td_class.append("wrong") 845*4882a593Smuzhiyun f.write(" <td class=\"%s\">%s</td>\n" % 846*4882a593Smuzhiyun (" ".join(td_class), boolean_str(pkg.is_status_ok('hash')))) 847*4882a593Smuzhiyun 848*4882a593Smuzhiyun # Current version 849*4882a593Smuzhiyun if len(pkg.current_version) > 20: 850*4882a593Smuzhiyun current_version = pkg.current_version[:20] + "..." 851*4882a593Smuzhiyun else: 852*4882a593Smuzhiyun current_version = pkg.current_version 853*4882a593Smuzhiyun f.write(" <td class=\"centered\">%s</td>\n" % current_version) 854*4882a593Smuzhiyun 855*4882a593Smuzhiyun # Latest version 856*4882a593Smuzhiyun if pkg.latest_version['status'] == RM_API_STATUS_ERROR: 857*4882a593Smuzhiyun td_class.append("version-error") 858*4882a593Smuzhiyun if pkg.latest_version['version'] is None: 859*4882a593Smuzhiyun td_class.append("version-unknown") 860*4882a593Smuzhiyun elif pkg.latest_version['version'] != pkg.current_version: 861*4882a593Smuzhiyun td_class.append("version-needs-update") 862*4882a593Smuzhiyun else: 863*4882a593Smuzhiyun td_class.append("version-good") 864*4882a593Smuzhiyun 865*4882a593Smuzhiyun if pkg.latest_version['status'] == RM_API_STATUS_ERROR: 866*4882a593Smuzhiyun latest_version_text = "<b>Error</b>" 867*4882a593Smuzhiyun elif pkg.latest_version['status'] == RM_API_STATUS_NOT_FOUND: 868*4882a593Smuzhiyun latest_version_text = "<b>Not found</b>" 869*4882a593Smuzhiyun else: 870*4882a593Smuzhiyun if pkg.latest_version['version'] is None: 871*4882a593Smuzhiyun latest_version_text = "<b>Found, but no version</b>" 872*4882a593Smuzhiyun else: 873*4882a593Smuzhiyun latest_version_text = "<a href=\"https://release-monitoring.org/project/%s\"><b>%s</b></a>" % \ 874*4882a593Smuzhiyun (pkg.latest_version['id'], str(pkg.latest_version['version'])) 875*4882a593Smuzhiyun 876*4882a593Smuzhiyun latest_version_text += "<br/>" 877*4882a593Smuzhiyun 878*4882a593Smuzhiyun if pkg.latest_version['status'] == RM_API_STATUS_FOUND_BY_DISTRO: 879*4882a593Smuzhiyun latest_version_text += "found by <a href=\"https://release-monitoring.org/distro/Buildroot/\">distro</a>" 880*4882a593Smuzhiyun else: 881*4882a593Smuzhiyun latest_version_text += "found by guess" 882*4882a593Smuzhiyun 883*4882a593Smuzhiyun f.write(" <td class=\"%s\">%s</td>\n" % 884*4882a593Smuzhiyun (" ".join(td_class), latest_version_text)) 885*4882a593Smuzhiyun 886*4882a593Smuzhiyun # Warnings 887*4882a593Smuzhiyun td_class = ["centered"] 888*4882a593Smuzhiyun if pkg.warnings == 0: 889*4882a593Smuzhiyun td_class.append("correct") 890*4882a593Smuzhiyun else: 891*4882a593Smuzhiyun td_class.append("wrong") 892*4882a593Smuzhiyun f.write(" <td class=\"%s\">%d</td>\n" % 893*4882a593Smuzhiyun (" ".join(td_class), pkg.warnings)) 894*4882a593Smuzhiyun 895*4882a593Smuzhiyun # URL status 896*4882a593Smuzhiyun td_class = ["centered"] 897*4882a593Smuzhiyun url_str = pkg.status['url'][1] 898*4882a593Smuzhiyun if pkg.status['url'][0] in ("error", "warning"): 899*4882a593Smuzhiyun td_class.append("missing_url") 900*4882a593Smuzhiyun if pkg.status['url'][0] == "error": 901*4882a593Smuzhiyun td_class.append("invalid_url") 902*4882a593Smuzhiyun url_str = "<a href=%s>%s</a>" % (pkg.url, pkg.status['url'][1]) 903*4882a593Smuzhiyun else: 904*4882a593Smuzhiyun td_class.append("good_url") 905*4882a593Smuzhiyun url_str = "<a href=%s>Link</a>" % pkg.url 906*4882a593Smuzhiyun f.write(" <td class=\"%s\">%s</td>\n" % 907*4882a593Smuzhiyun (" ".join(td_class), url_str)) 908*4882a593Smuzhiyun 909*4882a593Smuzhiyun # CVEs 910*4882a593Smuzhiyun td_class = ["centered"] 911*4882a593Smuzhiyun if pkg.is_status_ok("cve"): 912*4882a593Smuzhiyun td_class.append("cve-ok") 913*4882a593Smuzhiyun elif pkg.is_status_error("cve"): 914*4882a593Smuzhiyun td_class.append("cve-nok") 915*4882a593Smuzhiyun elif pkg.is_status_na("cve") and not pkg.is_actual_package: 916*4882a593Smuzhiyun td_class.append("cve-ok") 917*4882a593Smuzhiyun else: 918*4882a593Smuzhiyun td_class.append("cve-unknown") 919*4882a593Smuzhiyun f.write(" <td class=\"%s\">\n" % " ".join(td_class)) 920*4882a593Smuzhiyun if pkg.is_status_error("cve"): 921*4882a593Smuzhiyun for cve in pkg.cves: 922*4882a593Smuzhiyun f.write(" <a href=\"https://security-tracker.debian.org/tracker/%s\">%s<br/>\n" % (cve, cve)) 923*4882a593Smuzhiyun elif pkg.is_status_na("cve"): 924*4882a593Smuzhiyun f.write(" %s" % pkg.status['cve'][1]) 925*4882a593Smuzhiyun else: 926*4882a593Smuzhiyun f.write(" N/A\n") 927*4882a593Smuzhiyun f.write(" </td>\n") 928*4882a593Smuzhiyun 929*4882a593Smuzhiyun # CVEs Ignored 930*4882a593Smuzhiyun td_class = ["centered"] 931*4882a593Smuzhiyun if pkg.ignored_cves: 932*4882a593Smuzhiyun td_class.append("cve_ignored") 933*4882a593Smuzhiyun f.write(" <td class=\"%s\">\n" % " ".join(td_class)) 934*4882a593Smuzhiyun for ignored_cve in pkg.ignored_cves: 935*4882a593Smuzhiyun f.write(" <a href=\"https://security-tracker.debian.org/tracker/%s\">%s<br/>\n" % (ignored_cve, ignored_cve)) 936*4882a593Smuzhiyun f.write(" </td>\n") 937*4882a593Smuzhiyun 938*4882a593Smuzhiyun # CPE ID 939*4882a593Smuzhiyun td_class = ["left"] 940*4882a593Smuzhiyun if pkg.is_status_ok("cpe"): 941*4882a593Smuzhiyun td_class.append("cpe-ok") 942*4882a593Smuzhiyun elif pkg.is_status_error("cpe"): 943*4882a593Smuzhiyun td_class.append("cpe-nok") 944*4882a593Smuzhiyun elif pkg.is_status_na("cpe") and not pkg.is_actual_package: 945*4882a593Smuzhiyun td_class.append("cpe-ok") 946*4882a593Smuzhiyun else: 947*4882a593Smuzhiyun td_class.append("cpe-unknown") 948*4882a593Smuzhiyun f.write(" <td class=\"%s\">\n" % " ".join(td_class)) 949*4882a593Smuzhiyun if pkg.cpeid: 950*4882a593Smuzhiyun f.write(" <code>%s</code>\n" % pkg.cpeid) 951*4882a593Smuzhiyun if not pkg.is_status_ok("cpe"): 952*4882a593Smuzhiyun if pkg.is_actual_package and pkg.current_version: 953*4882a593Smuzhiyun if pkg.cpeid: 954*4882a593Smuzhiyun f.write(" <br/>%s <a href=\"https://nvd.nist.gov/products/cpe/search/results?namingFormat=2.3&keyword=%s\">(Search)</a>\n" % # noqa: E501 955*4882a593Smuzhiyun (pkg.status['cpe'][1], ":".join(pkg.cpeid.split(":")[0:5]))) 956*4882a593Smuzhiyun else: 957*4882a593Smuzhiyun f.write(" %s <a href=\"https://nvd.nist.gov/products/cpe/search/results?namingFormat=2.3&keyword=%s\">(Search)</a>\n" % # noqa: E501 958*4882a593Smuzhiyun (pkg.status['cpe'][1], pkg.name)) 959*4882a593Smuzhiyun else: 960*4882a593Smuzhiyun f.write(" %s\n" % pkg.status['cpe'][1]) 961*4882a593Smuzhiyun 962*4882a593Smuzhiyun f.write(" </td>\n") 963*4882a593Smuzhiyun 964*4882a593Smuzhiyun f.write(" </tr>\n") 965*4882a593Smuzhiyun 966*4882a593Smuzhiyun 967*4882a593Smuzhiyundef dump_html_all_pkgs(f, packages): 968*4882a593Smuzhiyun f.write(""" 969*4882a593Smuzhiyun<table class=\"sortable\"> 970*4882a593Smuzhiyun<tr> 971*4882a593Smuzhiyun<td>Package</td> 972*4882a593Smuzhiyun<td class=\"centered\">Patch count</td> 973*4882a593Smuzhiyun<td class=\"centered\">Infrastructure</td> 974*4882a593Smuzhiyun<td class=\"centered\">License</td> 975*4882a593Smuzhiyun<td class=\"centered\">License files</td> 976*4882a593Smuzhiyun<td class=\"centered\">Hash file</td> 977*4882a593Smuzhiyun<td class=\"centered\">Current version</td> 978*4882a593Smuzhiyun<td class=\"centered\">Latest version</td> 979*4882a593Smuzhiyun<td class=\"centered\">Warnings</td> 980*4882a593Smuzhiyun<td class=\"centered\">Upstream URL</td> 981*4882a593Smuzhiyun<td class=\"centered\">CVEs</td> 982*4882a593Smuzhiyun<td class=\"centered\">CVEs Ignored</td> 983*4882a593Smuzhiyun<td class=\"centered\">CPE ID</td> 984*4882a593Smuzhiyun</tr> 985*4882a593Smuzhiyun""") 986*4882a593Smuzhiyun for pkg in sorted(packages): 987*4882a593Smuzhiyun dump_html_pkg(f, pkg) 988*4882a593Smuzhiyun f.write("</table>") 989*4882a593Smuzhiyun 990*4882a593Smuzhiyun 991*4882a593Smuzhiyundef dump_html_stats(f, stats): 992*4882a593Smuzhiyun f.write("<a id=\"results\"></a>\n") 993*4882a593Smuzhiyun f.write("<table>\n") 994*4882a593Smuzhiyun infras = [infra[6:] for infra in stats.keys() if infra.startswith("infra-")] 995*4882a593Smuzhiyun for infra in infras: 996*4882a593Smuzhiyun f.write(" <tr><td>Packages using the <i>%s</i> infrastructure</td><td>%s</td></tr>\n" % 997*4882a593Smuzhiyun (infra, stats["infra-%s" % infra])) 998*4882a593Smuzhiyun f.write(" <tr><td>Packages having license information</td><td>%s</td></tr>\n" % 999*4882a593Smuzhiyun stats["license"]) 1000*4882a593Smuzhiyun f.write(" <tr><td>Packages not having license information</td><td>%s</td></tr>\n" % 1001*4882a593Smuzhiyun stats["no-license"]) 1002*4882a593Smuzhiyun f.write(" <tr><td>Packages having license files information</td><td>%s</td></tr>\n" % 1003*4882a593Smuzhiyun stats["license-files"]) 1004*4882a593Smuzhiyun f.write(" <tr><td>Packages not having license files information</td><td>%s</td></tr>\n" % 1005*4882a593Smuzhiyun stats["no-license-files"]) 1006*4882a593Smuzhiyun f.write(" <tr><td>Packages having a hash file</td><td>%s</td></tr>\n" % 1007*4882a593Smuzhiyun stats["hash"]) 1008*4882a593Smuzhiyun f.write(" <tr><td>Packages not having a hash file</td><td>%s</td></tr>\n" % 1009*4882a593Smuzhiyun stats["no-hash"]) 1010*4882a593Smuzhiyun f.write(" <tr><td>Total number of patches</td><td>%s</td></tr>\n" % 1011*4882a593Smuzhiyun stats["patches"]) 1012*4882a593Smuzhiyun f.write("<tr><td>Packages having a mapping on <i>release-monitoring.org</i></td><td>%s</td></tr>\n" % 1013*4882a593Smuzhiyun stats["rmo-mapping"]) 1014*4882a593Smuzhiyun f.write("<tr><td>Packages lacking a mapping on <i>release-monitoring.org</i></td><td>%s</td></tr>\n" % 1015*4882a593Smuzhiyun stats["rmo-no-mapping"]) 1016*4882a593Smuzhiyun f.write("<tr><td>Packages that are up-to-date</td><td>%s</td></tr>\n" % 1017*4882a593Smuzhiyun stats["version-uptodate"]) 1018*4882a593Smuzhiyun f.write("<tr><td>Packages that are not up-to-date</td><td>%s</td></tr>\n" % 1019*4882a593Smuzhiyun stats["version-not-uptodate"]) 1020*4882a593Smuzhiyun f.write("<tr><td>Packages with no known upstream version</td><td>%s</td></tr>\n" % 1021*4882a593Smuzhiyun stats["version-unknown"]) 1022*4882a593Smuzhiyun f.write("<tr><td>Packages affected by CVEs</td><td>%s</td></tr>\n" % 1023*4882a593Smuzhiyun stats["pkg-cves"]) 1024*4882a593Smuzhiyun f.write("<tr><td>Total number of CVEs affecting all packages</td><td>%s</td></tr>\n" % 1025*4882a593Smuzhiyun stats["total-cves"]) 1026*4882a593Smuzhiyun f.write("<tr><td>Packages with CPE ID</td><td>%s</td></tr>\n" % 1027*4882a593Smuzhiyun stats["cpe-id"]) 1028*4882a593Smuzhiyun f.write("<tr><td>Packages without CPE ID</td><td>%s</td></tr>\n" % 1029*4882a593Smuzhiyun stats["no-cpe-id"]) 1030*4882a593Smuzhiyun f.write("</table>\n") 1031*4882a593Smuzhiyun 1032*4882a593Smuzhiyun 1033*4882a593Smuzhiyundef dump_html_gen_info(f, date, commit): 1034*4882a593Smuzhiyun # Updated on Mon Feb 19 08:12:08 CET 2018, Git commit aa77030b8f5e41f1c53eb1c1ad664b8c814ba032 1035*4882a593Smuzhiyun f.write("<p><i>Updated on %s, git commit %s</i></p>\n" % (str(date), commit)) 1036*4882a593Smuzhiyun 1037*4882a593Smuzhiyun 1038*4882a593Smuzhiyundef dump_html(packages, stats, date, commit, output): 1039*4882a593Smuzhiyun with open(output, 'w') as f: 1040*4882a593Smuzhiyun f.write(html_header) 1041*4882a593Smuzhiyun dump_html_all_pkgs(f, packages) 1042*4882a593Smuzhiyun dump_html_stats(f, stats) 1043*4882a593Smuzhiyun dump_html_gen_info(f, date, commit) 1044*4882a593Smuzhiyun f.write(html_footer) 1045*4882a593Smuzhiyun 1046*4882a593Smuzhiyun 1047*4882a593Smuzhiyundef dump_json(packages, defconfigs, stats, date, commit, output): 1048*4882a593Smuzhiyun # Format packages as a dictionnary instead of a list 1049*4882a593Smuzhiyun # Exclude local field that does not contains real date 1050*4882a593Smuzhiyun excluded_fields = ['url_worker', 'name'] 1051*4882a593Smuzhiyun pkgs = { 1052*4882a593Smuzhiyun pkg.name: { 1053*4882a593Smuzhiyun k: v 1054*4882a593Smuzhiyun for k, v in pkg.__dict__.items() 1055*4882a593Smuzhiyun if k not in excluded_fields 1056*4882a593Smuzhiyun } for pkg in packages 1057*4882a593Smuzhiyun } 1058*4882a593Smuzhiyun defconfigs = { 1059*4882a593Smuzhiyun d.name: { 1060*4882a593Smuzhiyun k: v 1061*4882a593Smuzhiyun for k, v in d.__dict__.items() 1062*4882a593Smuzhiyun } for d in defconfigs 1063*4882a593Smuzhiyun } 1064*4882a593Smuzhiyun # Aggregate infrastructures into a single dict entry 1065*4882a593Smuzhiyun statistics = { 1066*4882a593Smuzhiyun k: v 1067*4882a593Smuzhiyun for k, v in stats.items() 1068*4882a593Smuzhiyun if not k.startswith('infra-') 1069*4882a593Smuzhiyun } 1070*4882a593Smuzhiyun statistics['infra'] = {k[6:]: v for k, v in stats.items() if k.startswith('infra-')} 1071*4882a593Smuzhiyun # The actual structure to dump, add commit and date to it 1072*4882a593Smuzhiyun final = {'packages': pkgs, 1073*4882a593Smuzhiyun 'stats': statistics, 1074*4882a593Smuzhiyun 'defconfigs': defconfigs, 1075*4882a593Smuzhiyun 'package_status_checks': Package.status_checks, 1076*4882a593Smuzhiyun 'commit': commit, 1077*4882a593Smuzhiyun 'date': str(date)} 1078*4882a593Smuzhiyun 1079*4882a593Smuzhiyun with open(output, 'w') as f: 1080*4882a593Smuzhiyun json.dump(final, f, indent=2, separators=(',', ': ')) 1081*4882a593Smuzhiyun f.write('\n') 1082*4882a593Smuzhiyun 1083*4882a593Smuzhiyun 1084*4882a593Smuzhiyundef resolvepath(path): 1085*4882a593Smuzhiyun return os.path.abspath(os.path.expanduser(path)) 1086*4882a593Smuzhiyun 1087*4882a593Smuzhiyun 1088*4882a593Smuzhiyundef parse_args(): 1089*4882a593Smuzhiyun parser = argparse.ArgumentParser() 1090*4882a593Smuzhiyun output = parser.add_argument_group('output', 'Output file(s)') 1091*4882a593Smuzhiyun output.add_argument('--html', dest='html', type=resolvepath, 1092*4882a593Smuzhiyun help='HTML output file') 1093*4882a593Smuzhiyun output.add_argument('--json', dest='json', type=resolvepath, 1094*4882a593Smuzhiyun help='JSON output file') 1095*4882a593Smuzhiyun packages = parser.add_mutually_exclusive_group() 1096*4882a593Smuzhiyun packages.add_argument('-c', dest='configpackages', action='store_true', 1097*4882a593Smuzhiyun help='Apply to packages enabled in current configuration') 1098*4882a593Smuzhiyun packages.add_argument('-n', dest='npackages', type=int, action='store', 1099*4882a593Smuzhiyun help='Number of packages') 1100*4882a593Smuzhiyun packages.add_argument('-p', dest='packages', action='store', 1101*4882a593Smuzhiyun help='List of packages (comma separated)') 1102*4882a593Smuzhiyun parser.add_argument('--nvd-path', dest='nvd_path', 1103*4882a593Smuzhiyun help='Path to the local NVD database', type=resolvepath) 1104*4882a593Smuzhiyun args = parser.parse_args() 1105*4882a593Smuzhiyun if not args.html and not args.json: 1106*4882a593Smuzhiyun parser.error('at least one of --html or --json (or both) is required') 1107*4882a593Smuzhiyun return args 1108*4882a593Smuzhiyun 1109*4882a593Smuzhiyun 1110*4882a593Smuzhiyundef __main__(): 1111*4882a593Smuzhiyun global cvecheck 1112*4882a593Smuzhiyun 1113*4882a593Smuzhiyun args = parse_args() 1114*4882a593Smuzhiyun 1115*4882a593Smuzhiyun if args.nvd_path: 1116*4882a593Smuzhiyun import cve as cvecheck 1117*4882a593Smuzhiyun 1118*4882a593Smuzhiyun if args.packages: 1119*4882a593Smuzhiyun package_list = args.packages.split(",") 1120*4882a593Smuzhiyun elif args.configpackages: 1121*4882a593Smuzhiyun package_list = get_config_packages() 1122*4882a593Smuzhiyun else: 1123*4882a593Smuzhiyun package_list = None 1124*4882a593Smuzhiyun date = datetime.datetime.utcnow() 1125*4882a593Smuzhiyun commit = subprocess.check_output(['git', '-C', brpath, 1126*4882a593Smuzhiyun 'rev-parse', 1127*4882a593Smuzhiyun 'HEAD']).splitlines()[0].decode() 1128*4882a593Smuzhiyun print("Build package list ...") 1129*4882a593Smuzhiyun packages = get_pkglist(args.npackages, package_list) 1130*4882a593Smuzhiyun print("Getting developers ...") 1131*4882a593Smuzhiyun developers = parse_developers() 1132*4882a593Smuzhiyun print("Build defconfig list ...") 1133*4882a593Smuzhiyun defconfigs = get_defconfig_list() 1134*4882a593Smuzhiyun for d in defconfigs: 1135*4882a593Smuzhiyun d.set_developers(developers) 1136*4882a593Smuzhiyun print("Getting package make info ...") 1137*4882a593Smuzhiyun package_init_make_info() 1138*4882a593Smuzhiyun print("Getting package details ...") 1139*4882a593Smuzhiyun for pkg in packages: 1140*4882a593Smuzhiyun pkg.set_infra() 1141*4882a593Smuzhiyun pkg.set_license() 1142*4882a593Smuzhiyun pkg.set_hash_info() 1143*4882a593Smuzhiyun pkg.set_patch_count() 1144*4882a593Smuzhiyun pkg.set_check_package_warnings() 1145*4882a593Smuzhiyun pkg.set_current_version() 1146*4882a593Smuzhiyun pkg.set_cpeid() 1147*4882a593Smuzhiyun pkg.set_url() 1148*4882a593Smuzhiyun pkg.set_ignored_cves() 1149*4882a593Smuzhiyun pkg.set_developers(developers) 1150*4882a593Smuzhiyun print("Checking URL status") 1151*4882a593Smuzhiyun loop = asyncio.get_event_loop() 1152*4882a593Smuzhiyun loop.run_until_complete(check_package_urls(packages)) 1153*4882a593Smuzhiyun print("Getting latest versions ...") 1154*4882a593Smuzhiyun loop = asyncio.get_event_loop() 1155*4882a593Smuzhiyun loop.run_until_complete(check_package_latest_version(packages)) 1156*4882a593Smuzhiyun if args.nvd_path: 1157*4882a593Smuzhiyun print("Checking packages CVEs") 1158*4882a593Smuzhiyun check_package_cves(args.nvd_path, packages) 1159*4882a593Smuzhiyun check_package_cpes(args.nvd_path, packages) 1160*4882a593Smuzhiyun print("Calculate stats") 1161*4882a593Smuzhiyun stats = calculate_stats(packages) 1162*4882a593Smuzhiyun if args.html: 1163*4882a593Smuzhiyun print("Write HTML") 1164*4882a593Smuzhiyun dump_html(packages, stats, date, commit, args.html) 1165*4882a593Smuzhiyun if args.json: 1166*4882a593Smuzhiyun print("Write JSON") 1167*4882a593Smuzhiyun dump_json(packages, defconfigs, stats, date, commit, args.json) 1168*4882a593Smuzhiyun 1169*4882a593Smuzhiyun 1170*4882a593Smuzhiyun__main__() 1171