# Copyright (C) 2014-2015 Codethink Limited # # 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; version 2 of the License. # # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # This plugin is used as part of the Baserock automated release process. # # See: for more information. import warnings import cliapp import morphlib class CVECheckPlugin(cliapp.Plugin): def enable(self): self.app.add_subcommand( 'cve-check', self.cve_check, arg_synopsis='REPO REF MORPH [MORPH]...') def disable(self): pass def cve_check(self, args): '''Certify that any given system definition is reproducable. Command line arguments: * `REPO` is a git repository URL. * `REF` is a branch or other commit reference in that repository. * `MORPH` is a system morphology name at that ref. ''' if len(args) < 3: raise cliapp.AppException( 'Wrong number of arguments to certify command ' '(see help)') repo, ref = args[0], args[1] system_filenames = map(morphlib.util.sanitise_morphology_path, args[2:]) self.lrc, self.rrc = morphlib.util.new_repo_caches(self.app) self.resolver = morphlib.artifactresolver.ArtifactResolver() self.cve_db = CVEDataBase() for system_filename in system_filenames: self.certify_system(repo, ref, system_filename) def certify_system(self, repo, ref, system_filename): '''Certify reproducibility of system.''' self.app.status( msg='Creating source pool for %s' % system_filename, chatty=True) source_pool = morphlib.sourceresolver.create_source_pool( self.lrc, self.rrc, repo, ref, system_filename, cachedir=self.app.settings['cachedir'], update_repos = not self.app.settings['no-git-update'], status_cb=self.app.status) self.app.status( msg='Resolving artifacts for %s' % system_filename, chatty=True) root_artifacts = self.resolver.resolve_root_artifacts(source_pool) def find_artifact_by_name(artifacts_list, filename): for a in artifacts_list: if a.source.filename == filename: return a raise ValueError system_artifact = find_artifact_by_name(root_artifacts, system_filename) aliases = self.app.settings['repo-alias'] resolver = morphlib.repoaliasresolver.RepoAliasResolver(aliases) for source in set(a.source for a in system_artifact.walk()): if source.morphology['kind'] != 'chunk': continue name = source.morphology['name'] ref = source.original_ref print(' Checking chunk: {}'.format(name)) # Ensure we have a cache of the repo if not self.lrc.has_repo(source.repo_name): self.lrc.cache_repo(source.repo_name) cached = self.lrc.get_repo(source.repo_name) version = cached.version_guess(ref) self.cve_db.check_vulnerability(name, version) class CVEDetail: """ A single CVE """ def __init__(self, id, ranges): self.id = id self.ranges = ranges def check_vulnerability(self, version): for r in self.ranges: print(' {}: version is {}; vulnerable range is: {} to {}'. format(self.id, version, r[0], r[1])) class CVESoftware: """ A piece of software we track CVEs for """ def __init__(self, name): self.name = name self.cves = [] def add_cve(self, id, ranges): cve = CVEDetail(id, ranges) self.cves.append(cve) def check_vulnerability(self, version): for v in self.cves: v.check_vulnerability(version) class CVEDataBase: """ Provides CVE checking functionality """ def __init__(self): # TODO: In the future this could connect to a DB or load YAML data # For now it just creates a hardcoded DB self.db = [] self._add_software('libpng', [['CVE-2014-9495', [['0', '1.5.20'], ['1.6.9', '1.6.15']]], ['CVE-2014-0333', [['1.6.0', '1.6.9' ]]] ]) self._add_software('openssl-new', [['CVE-2014-3567', [['1.0.1', '1.0.1i' ], ['1.0.0', '1.0.0n' ], ['0.9.8', '0.9.8zc']]], ['CVE-2014-3568', [['1.0.1', '1.0.1i' ], ['1.0.0', '1.0.0n' ], ['0.9.8', '0.9.8zc']]], ['CVE-2014-3513', [['1.0.1', '1.0.1i' ]]], ['CVE-2015-0289', [['1.0.2', '1.0.2' ], ['1.0.1', '1.0.1l' ], ['1.0.0', '1.0.0q' ], ['0.9.8', '0.9.8ze']]] ]) def _add_software(self, name, cves): sw = CVESoftware(name) for v in cves: sw.add_cve(v[0], v[1]) self.db.append(sw) def check_vulnerability(self, name, version): for s in self.db: if s.name != name: continue s.check_vulnerability(version) break