X-Git-Url: https://git.decadent.org.uk/gitweb/?a=blobdiff_plain;f=daklib%2Fchecks.py;h=cae801e489f341c370d22ad982313ff0553fe4b6;hb=73e23995d806657f985ab44272905512a2368b55;hp=81bd629e481e171991d1ddf0da8d3b4dce282c12;hpb=707a89a3b86961755a99cb9e1a0a5f23690f9529;p=dak.git diff --git a/daklib/checks.py b/daklib/checks.py index 81bd629e..cae801e4 100644 --- a/daklib/checks.py +++ b/daklib/checks.py @@ -30,10 +30,12 @@ from daklib.regexes import * from daklib.textutils import fix_maintainer, ParseMaintError import daklib.lintian as lintian import daklib.utils as utils +from daklib.upload import InvalidHashException import apt_inst import apt_pkg from apt_pkg import version_compare +import errno import os import time import yaml @@ -45,6 +47,12 @@ class Reject(Exception): """exception raised by failing checks""" pass +class RejectStupidMaintainerException(Exception): + """exception raised by failing the external hashes check""" + + def __str__(self): + return "'%s' has mismatching %s from the external files db ('%s' [current] vs '%s' [external])" % self.args[:4] + class Check(object): """base class for checks @@ -159,13 +167,62 @@ class ChangesCheck(Check): class HashesCheck(Check): """Check hashes in .changes and .dsc are valid.""" def check(self, upload): + what = None + try: + changes = upload.changes + what = changes.filename + for f in changes.files.itervalues(): + f.check(upload.directory) + source = changes.source + if source is not None: + what = source.filename + for f in source.files.itervalues(): + f.check(upload.directory) + except IOError as e: + if e.errno == errno.ENOENT: + raise Reject('{0} refers to non-existing file: {1}\n' + 'Perhaps you need to include it in your upload?' + .format(what, os.path.basename(e.filename))) + raise + except InvalidHashException as e: + raise Reject('{0}: {1}'.format(what, unicode(e))) + +class ExternalHashesCheck(Check): + """Checks hashes in .changes and .dsc against an external database.""" + def check_single(self, session, f): + q = session.execute("SELECT size, md5sum, sha1sum, sha256sum FROM external_files WHERE filename LIKE '%%/%s'" % f.filename) + (ext_size, ext_md5sum, ext_sha1sum, ext_sha256sum) = q.fetchone() or (None, None, None, None) + + if not ext_size: + return + + if ext_size != f.size: + raise RejectStupidMaintainerException(f.filename, 'size', f.size, ext_size) + + if ext_md5sum != f.md5sum: + raise RejectStupidMaintainerException(f.filename, 'md5sum', f.md5sum, ext_md5sum) + + if ext_sha1sum != f.sha1sum: + raise RejectStupidMaintainerException(f.filename, 'sha1sum', f.sha1sum, ext_sha1sum) + + if ext_sha256sum != f.sha256sum: + raise RejectStupidMaintainerException(f.filename, 'sha256sum', f.sha256sum, ext_sha256sum) + + def check(self, upload): + cnf = Config() + + if not cnf.use_extfiles: + return + + session = upload.session changes = upload.changes + for f in changes.files.itervalues(): - f.check(upload.directory) - source = changes.source + self.check_single(session, f) + source = changes.source if source is not None: for f in source.files.itervalues(): - f.check(upload.directory) + self.check_single(session, f) class BinaryCheck(Check): """Check binary packages for syntax errors.""" @@ -354,6 +411,10 @@ class ACLCheck(Check): """Check the uploader is allowed to upload the packages in .changes""" def _does_hijack(self, session, upload, suite): + # Try to catch hijacks. + # This doesn't work correctly. Uploads to experimental can still + # "hijack" binaries from unstable. Also one can hijack packages + # via buildds (but people who try this should not be DMs). for binary_name in upload.changes.binary_names: binaries = session.query(DBBinary).join(DBBinary.source) \ .filter(DBBinary.suites.contains(suite)) \ @@ -389,9 +450,9 @@ class ACLCheck(Check): uploaded_arches = set(upload.changes.architectures) uploaded_arches.discard('source') allowed_arches = set(a.arch_string for a in acl.architectures) - for a in uploaded_arches: - if a not in allowed_arches: - return False, "uploads for architecture {0} are not allowed".format(a) + forbidden_arches = uploaded_arches - allowed_arches + if len(forbidden_arches) != 0: + return False, "uploads for architecture(s) {0} are not allowed".format(", ".join(forbidden_arches)) if not acl.allow_hijack: for suite in upload.final_suites: does_hijack, hijacked_binary, hijacked_from = self._does_hijack(session, upload, suite) @@ -401,9 +462,13 @@ class ACLCheck(Check): acl_per_source = session.query(ACLPerSource).filter_by(acl=acl, fingerprint=upload.fingerprint, source=source_name).first() if acl.allow_per_source: # XXX: Drop DMUA part here and switch to new implementation. + # XXX: Send warning mail once users can set the new DMUA flag dmua_status, dmua_reason = self._check_dmua(upload) - if not dmua_status: - return False, dmua_reason + if acl_per_source is None: + if not dmua_status: + return False, dmua_reason + else: + upload.warn('DM flag not set, but accepted as DMUA was set.') #if acl_per_source is None: # return False, "not allowed to upload source package '{0}'".format(source_name) if acl.deny_per_source and acl_per_source is not None: @@ -590,7 +655,7 @@ class LintianCheck(Check): except yaml.YAMLError as msg: raise Exception('Could not read lintian tags file {0}, YAML error: {1}'.format(tagfile, msg)) - fd, temp_filename = utils.temp_filename() + fd, temp_filename = utils.temp_filename(mode=0o644) temptagfile = os.fdopen(fd, 'w') for tags in lintiantags.itervalues(): for tag in tags: @@ -599,8 +664,10 @@ class LintianCheck(Check): changespath = os.path.join(upload.directory, changes.filename) try: - # FIXME: no shell - cmd = "lintian --show-overrides --tags-from-file {0} {1}".format(temp_filename, changespath) + if cnf.unprivgroup: + cmd = "sudo -H -u {0} -- /usr/bin/lintian --show-overrides --tags-from-file {1} {2}".format(cnf.unprivgroup, temp_filename, changespath) + else: + cmd = "/usr/bin/lintian --show-overrides --tags-from-file {0} {1}".format(temp_filename, changespath) result, output = commands.getstatusoutput(cmd) finally: os.unlink(temp_filename)