diff options
author | Michael Drake <michael.drake@codethink.co.uk> | 2015-04-14 11:10:33 +0000 |
---|---|---|
committer | Michael Drake <michael.drake@codethink.co.uk> | 2015-04-15 14:35:26 +0000 |
commit | c244702280771e025bf0e5ed0a83d2ff6921e3ec (patch) | |
tree | 5c7199976797c5ea731366fb9cd9a19d512edc82 | |
parent | f2384463316e7bd53dc8bb4f52febbf8b4973c30 (diff) | |
download | morph-c244702280771e025bf0e5ed0a83d2ff6921e3ec.tar.gz |
Add WIP CVE checking plugin
Change-Id: Iffa52255975eb99f7ca6cf52215cd1f490f5a866
-rw-r--r-- | morphlib/plugins/cve_check_plugin.py | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/morphlib/plugins/cve_check_plugin.py b/morphlib/plugins/cve_check_plugin.py new file mode 100644 index 00000000..8a3694e0 --- /dev/null +++ b/morphlib/plugins/cve_check_plugin.py @@ -0,0 +1,165 @@ +# 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: <http://wiki.baserock.org/guides/release-process> 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 = [] + + # Add LibPNG + libpng = CVESoftware('libpng') + libpng.add_cve('CVE-2014-9495', + [['0', '1.5.20'], + ['1.6.9', '1.6.15']]) + libpng.add_cve('CVE-2014-0333', + [['1.6.0', '1.6.9']]) + + self.db.append(libpng) + + def check_vulnerability(self, name, version): + for s in self.db: + if s.name != name: + continue + + s.check_vulnerability(version) + break |