Example #1
0
    def check(self, pkg):
        if pkg.isSource():
            return

        for req in pkg.requires() + pkg.prereq():
            if req[0] == 'xinetd':
                printError(pkg, xinetd_tag)
Example #2
0
    def check(self, pkg):
        if pkg.isSource():
            return

        for req in pkg.requires() + pkg.prereq():
            if req[0] == 'xinetd':
                printError(pkg, xinetd_tag)
Example #3
0
    def check(self, pkg):
        if pkg.isSource():
            return

        files = pkg.files()
        dirs = {}

        for f, pkgfile in files.items():
            if f in pkg.ghostFiles():
                continue

            if f.startswith("/etc/logrotate.d/"):
                try:
                    for n, o in self.parselogrotateconf(pkg.dirName(), f).items():
                        if n in dirs and dirs[n] != o:
                            printError(pkg, "logrotate-duplicate", n)
                        else:
                            dirs[n] = o
                except Exception as x:
                    printError(pkg, 'rpmlint-exception', "%(file)s raised an exception: %(x)s" % {'file': f, 'x': x})

        for d in sorted(dirs.keys()):
            if d not in files:
                if d != '/var/log':
                    printError(pkg, 'suse-logrotate-log-dir-not-packaged', d)
                continue
            mode = files[d].mode & 0o777
            if files[d].user != 'root' and (dirs[d] is None or dirs[d][0] != files[d].user):
                printError(
                    pkg, 'suse-logrotate-user-writable-log-dir',
                    "%s %s:%s %04o" % (d, files[d].user, files[d].group, mode))
            elif files[d].group != 'root' and mode & 0o20 and (dirs[d] is None or dirs[d][1] != files[d].group):
                    printError(
                        pkg, 'suse-logrotate-user-writable-log-dir',
                        "%s %s:%s %04o" % (d, files[d].user, files[d].group, mode))
Example #4
0
 def check_spec(self, pkg, spec_file, spec_lines=None):
     '''SCL spec file checks'''
     spec = '\n'.join(Pkg.readlines(spec_file))
     if global_scl_definition.search(spec):
         self.check_metapackage(pkg, spec)
     elif scl_package_definition.search(spec):
         self.check_scl_spec(pkg, spec)
     elif scl_use.search(spec):
         printError(pkg, 'undeclared-scl')
Example #5
0
 def check_spec(self, pkg, spec_file):
     """SCL spec file checks"""
     spec = "\n".join(Pkg.readlines(spec_file))
     if global_scl_definition.search(spec):
         self.check_metapackage(pkg, spec)
     elif scl_package_definition.search(spec):
         self.check_scl_spec(pkg, spec)
     elif scl_use.search(spec):
         printError(pkg, "undeclared-scl")
 def check_spec(self, pkg, spec_file, spec_lines=[]):
     '''SCL spec file checks'''
     spec = '\n'.join(Pkg.readlines(spec_file))
     if global_scl_definition.search(spec):
         self.check_metapackage(pkg, spec)
     elif scl_package_definition.search(spec):
         self.check_scl_spec(pkg, spec)
     elif scl_use.search(spec):
         printError(pkg, 'undeclared-scl')
Example #7
0
 def check_file(self, pkg, filename):
     root = pkg.dirName()
     f = root + filename
     try:
         st = getstatusoutput(appdata_checker + (f, ))
     except OSError:
         # ignore if the checker is not installed
         return
     if st[0]:
         printError(pkg, 'invalid-appdata-file', filename)
Example #8
0
 def check_file(self, pkg, filename):
     root = pkg.dirName()
     f = root + filename
     try:
         st = getstatusoutput(appdata_checker + (f,))
     except OSError:
         # ignore if the checker is not installed
         return
     if st[0]:
         printError(pkg, "invalid-appdata-file", filename)
Example #9
0
    def check_binary(self, pkg):
        config_files = pkg.configFiles()
        noreplace_files = pkg.noreplaceFiles()

        for c in config_files:
            if c.startswith("/var/lib/games/"):
                printError(pkg, "score-file-must-not-be-conffile", c)
            elif not c.startswith("/etc/") and not c.startswith("/var/"):
                printWarning(pkg, "non-etc-or-var-file-marked-as-conffile", c)

            if c not in noreplace_files:
                printWarning(pkg, "conffile-without-noreplace-flag", c)
Example #10
0
 def check_summary(self, pkg, lang, ignored_words):
     summary = pkg.langtag(rpm.RPMTAG_SUMMARY, lang)
     if use_utf8:
         if not Pkg.is_utf8_bytestr(summary):
             printError(pkg, 'tag-not-utf8', 'Summary', lang)
         summary = Pkg.to_unicode(summary)
     else:
         summary = Pkg.b2s(summary)
     self._unexpanded_macros(pkg, 'Summary(%s)' % lang, summary)
     spell_check(pkg, summary, 'Summary(%s)', lang, ignored_words)
     if '\n' in summary:
         printError(pkg, 'summary-on-multiple-lines', lang)
     if summary[0] != summary[0].upper():
         printWarning(pkg, 'summary-not-capitalized', lang, summary)
     if summary[-1] == '.':
         printWarning(pkg, 'summary-ended-with-dot', lang, summary)
     if len(summary) > max_line_len:
         printError(pkg, 'summary-too-long', lang, summary)
     if leading_space_regex.search(summary):
         printError(pkg, 'summary-has-leading-spaces', lang, summary)
     res = forbidden_words_regex.search(summary)
     if res and Config.getOption('ForbiddenWords'):
         printWarning(pkg, 'summary-use-invalid-word', lang, res.group(1))
     if pkg.name:
         sepchars = r'[\s%s]' % punct
         res = re.search(r'(?:^|\s)(%s)(?:%s|$)' %
                         (re.escape(pkg.name), sepchars),
                         summary, re.IGNORECASE | re.UNICODE)
         if res:
             printWarning(pkg, 'name-repeated-in-summary', lang,
                          res.group(1))
Example #11
0
 def check_summary(self, pkg, lang, ignored_words):
     summary = pkg.langtag(rpm.RPMTAG_SUMMARY, lang)
     if use_utf8:
         if not Pkg.is_utf8_bytestr(summary):
             printError(pkg, 'tag-not-utf8', 'Summary', lang)
         summary = Pkg.to_unicode(summary)
     else:
         summary = Pkg.b2s(summary)
     self._unexpanded_macros(pkg, 'Summary(%s)' % lang, summary)
     spell_check(pkg, summary, 'Summary(%s)', lang, ignored_words)
     if '\n' in summary:
         printError(pkg, 'summary-on-multiple-lines', lang)
     if summary[0] != summary[0].upper():
         printWarning(pkg, 'summary-not-capitalized', lang, summary)
     if summary[-1] == '.':
         printWarning(pkg, 'summary-ended-with-dot', lang, summary)
     if len(summary) > max_line_len:
         printError(pkg, 'summary-too-long', lang, summary)
     if leading_space_regex.search(summary):
         printError(pkg, 'summary-has-leading-spaces', lang, summary)
     res = forbidden_words_regex.search(summary)
     if res and Config.getOption('ForbiddenWords'):
         printWarning(pkg, 'summary-use-invalid-word', lang, res.group(1))
     if pkg.name:
         sepchars = '[\s' + punct + ']'
         res = re.search('(?:^|\s)(%s)(?:%s|$)' %
                         (re.escape(pkg.name), sepchars),
                         summary, re.IGNORECASE | re.UNICODE)
         if res:
             printWarning(pkg, 'name-repeated-in-summary', lang,
                          res.group(1))
Example #12
0
 def check(self, pkg):
     res = pkg.checkSignature()
     if not res or res[0] != 0:
         if res and res[1]:
             kres = SignatureCheck.unknown_key_regex.search(res[1])
         else:
             kres = None
         if kres:
             printError(pkg, "unknown-key", kres.group(1))
         else:
             Pkg.warn("Error checking signature of %s: %s" %
                      (pkg.filename, res[1]))
     else:
         if not SignatureCheck.pgp_regex.search(res[1]):
             printError(pkg, "no-signature")
Example #13
0
 def check(self, pkg):
     res = pkg.checkSignature()
     if not res or res[0] != 0:
         if res and res[1]:
             kres = SignatureCheck.unknown_key_regex.search(res[1])
         else:
             kres = None
         if kres:
             printError(pkg, "unknown-key", kres.group(1))
         else:
             Pkg.warn("Error checking signature of %s: %s" %
                      (pkg.filename, res[1]))
     else:
         if not SignatureCheck.pgp_regex.search(res[1]):
             printError(pkg, "no-signature")
Example #14
0
 def check_source(self, pkg):
     # process file list
     spec_file = None
     for fname, pkgfile in pkg.files().items():
         if fname.endswith('.spec'):
             if spec_file:
                 printError(pkg, 'multiple-specfiles', spec_file, fname)
             else:
                 spec_file = fname
         elif source_regex.search(fname) and compress_ext and \
                 not fname.endswith(compress_ext):
             printWarning(pkg, 'source-or-patch-not-compressed',
                          compress_ext, fname)
         perm = pkgfile.mode & 0o7777
         if perm not in valid_src_perms:
             printWarning(pkg, 'strange-permission', fname, "%o" % perm)
Example #15
0
 def check_source(self, pkg):
     # process file list
     spec_file = None
     for fname, pkgfile in pkg.files().items():
         if fname.endswith('.spec'):
             if spec_file:
                 printError(pkg, 'multiple-specfiles', spec_file, fname)
             else:
                 spec_file = fname
         elif source_regex.search(fname) and compress_ext and \
                 not fname.endswith(compress_ext):
             printWarning(pkg, 'source-or-patch-not-compressed',
                          compress_ext, fname)
         perm = pkgfile.mode & 0o7777
         if perm not in valid_src_perms:
             printWarning(pkg, 'strange-permission', fname, "%o" % perm)
Example #16
0
    def check(self, pkg):
        for fname, pkgfile in pkg.files().items():
            path = pkgfile.path
            if zip_regex.search(fname) and os.path.exists(path) and \
               stat.S_ISREG(os.lstat(path)[stat.ST_MODE]) and \
               zipfile.is_zipfile(path):
                z = None  # TODO ZipFile is context manager in 2.7+
                try:
                    z = zipfile.ZipFile(path, 'r')
                    badcrc = z.testzip()
                    if badcrc:
                        printError(pkg, 'bad-crc-in-zip', badcrc, fname)
                except zipfile.error:
                    printWarning(pkg, 'unable-to-read-zip',
                                 '%s: %s' % (fname, sys.exc_info()[1]))
                else:
                    compressed = False
                    for zinfo in z.infolist():
                        if zinfo.compress_type != zipfile.ZIP_STORED:
                            compressed = True
                            break
                    if not compressed:
                        printWarning(pkg, 'uncompressed-zip', fname)

                    # additional jar checks
                    if jar_regex.search(fname):
                        try:
                            mf = Pkg.b2s(z.read('META-INF/MANIFEST.MF'))
                            if classpath_regex.search(mf):
                                printWarning(pkg, 'class-path-in-manifest',
                                             fname)
                        except KeyError:
                            # META-INF/* are optional:
                            # http://java.sun.com/j2se/1.4/docs/guide/jar/jar.html
                            pass
                        try:
                            zinfo = z.getinfo('META-INF/INDEX.LIST')
                            if not want_indexed_jars:
                                printWarning(pkg, 'jar-indexed', fname)
                        except KeyError:
                            if want_indexed_jars:
                                printWarning(pkg, 'jar-not-indexed', fname)
                            pass

                z and z.close()
Example #17
0
    def check(self, pkg):
        for fname, pkgfile in pkg.files().items():
            path = pkgfile.path
            if zip_regex.search(fname) and os.path.exists(path) and \
               stat.S_ISREG(os.lstat(path)[stat.ST_MODE]) and \
               zipfile.is_zipfile(path):
                z = None  # TODO ZipFile is context manager in 2.7+
                try:
                    z = zipfile.ZipFile(path, 'r')
                    badcrc = z.testzip()
                    if badcrc:
                        printError(pkg, 'bad-crc-in-zip', badcrc, fname)
                except zipfile.error:
                    printWarning(pkg, 'unable-to-read-zip', '%s: %s' %
                                 (fname, sys.exc_info()[1]))
                else:
                    compressed = False
                    for zinfo in z.infolist():
                        if zinfo.compress_type != zipfile.ZIP_STORED:
                            compressed = True
                            break
                    if not compressed:
                        printWarning(pkg, 'uncompressed-zip', fname)

                    # additional jar checks
                    if jar_regex.search(fname):
                        try:
                            mf = Pkg.b2s(z.read('META-INF/MANIFEST.MF'))
                            if classpath_regex.search(mf):
                                printWarning(pkg,
                                             'class-path-in-manifest', fname)
                        except KeyError:
                            # META-INF/* are optional:
                            # http://java.sun.com/j2se/1.4/docs/guide/jar/jar.html
                            pass
                        try:
                            zinfo = z.getinfo('META-INF/INDEX.LIST')
                            if not want_indexed_jars:
                                printWarning(pkg, 'jar-indexed', fname)
                        except KeyError:
                            if want_indexed_jars:
                                printWarning(pkg, 'jar-not-indexed', fname)
                            pass

                z and z.close()
Example #18
0
    def check_binary(self, pkg):
        '''SCL binary package checks'''
        # Assume that no dash in package name means no SCL
        splits = pkg.name.split('-')
        if len(splits) < 2:
            return
        scl_name = splits[0]
        # While we are here, check if it's a runtime/build package
        is_runtime = splits[-1] == 'runtime'
        is_build = splits[-1] == 'build'
        del splits

        # Now test if there is /opt/foo/ dir
        good = False
        for fname in pkg.files().keys():
            if startdir.search(fname):
                good = True
                break
        if not good:
            return

        # Test if our dir is named the same way as scl
        good = True
        for fname in pkg.files().keys():
            if not startdir.search(fname):
                if allowed_etc.search(fname) or allowed_var.search(fname) or \
                   fname.startswith('/usr/bin/'):
                    continue
                if fname.startswith('/etc/rpm/'):
                    if not is_build:
                        printWarning(pkg, 'scl-rpm-macros-outside-of-build',
                                     fname)
                    continue
                if is_runtime and \
                   fname == os.path.join('/etc/scl/prefixes', scl_name):
                    continue
                printError(pkg, 'file-outside-of-scl-tree', fname)
            else:
                if fname.split('/')[3] != scl_name:
                    good = False

        if not good:
            printError(pkg, 'scl-name-screwed-up')
Example #19
0
    def check(self, pkg):
        if pkg.isSource():
            return

        files = pkg.files()
        for path, info in files.items():
            parent = path.rpartition("/")[0]
            if parent not in files:
                # can't figure out who owns the parent directory if it's part of another RPM :(
                continue

            parent_owner = files[parent].user

            # root user is trusted
            if info.user != parent_owner and parent_owner not in ('root', '0'):
                printError(pkg, 'file-parent-ownership-mismatch', path,
                           "owned by", info.user,
                           "is stored in directory owned by different user",
                           parent_owner)
Example #20
0
    def check(self, pkg):
        if pkg.isSource():
            return

        files = pkg.files()
        dirs = {}

        for f in files:
            if f in pkg.ghostFiles():
                continue

            if f.startswith("/etc/logrotate.d/"):
                try:
                    for n, o in self.parselogrotateconf(pkg.dirName(),
                                                        f).items():
                        if n in dirs and dirs[n] != o:
                            printError(pkg, "logrotate-duplicate", n)
                        else:
                            dirs[n] = o
                except Exception as x:
                    printError(
                        pkg, 'rpmlint-exception',
                        "%(file)s raised an exception: %(x)s" % {
                            'file': f,
                            'x': x
                        })

        for d in sorted(dirs.keys()):
            if d not in files:
                if d != '/var/log':
                    printError(pkg, 'suse-logrotate-log-dir-not-packaged', d)
                continue
            mode = files[d].mode & 0o777
            if files[d].user != 'root' and (dirs[d] is None
                                            or dirs[d][0] != files[d].user):
                printError(
                    pkg, 'suse-logrotate-user-writable-log-dir',
                    "%s %s:%s %04o" % (d, files[d].user, files[d].group, mode))
            elif files[d].group != 'root' and mode & 0o20 and (
                    dirs[d] is None or dirs[d][1] != files[d].group):
                printError(
                    pkg, 'suse-logrotate-user-writable-log-dir',
                    "%s %s:%s %04o" % (d, files[d].user, files[d].group, mode))
 def check_description(self, pkg, lang, ignored_words):
     description = pkg.langtag(rpm.RPMTAG_DESCRIPTION, lang)
     self._unexpanded_macros(pkg, '%%description -l %s' % lang, description)
     utf8desc = description
     if use_utf8:
         utf8desc = Pkg.to_utf8(description).decode('utf-8')
     spell_check(pkg, utf8desc, '%%description -l %s', lang, ignored_words)
     for l in utf8desc.splitlines():
         if len(l) > max_line_len:
             printError(pkg, 'description-line-too-long', lang, l)
         res = forbidden_words_regex.search(l)
         if res and Config.getOption('ForbiddenWords'):
             printWarning(pkg, 'description-use-invalid-word', lang,
                          res.group(1))
         res = tag_regex.search(l)
         if res:
             printWarning(pkg, 'tag-in-description', lang, res.group(1))
     if use_utf8 and not Pkg.is_utf8_str(description):
         printError(pkg, 'tag-not-utf8', '%description', lang)
    def check_binary(self, pkg):
        '''SCL binary package checks'''
        # Assume that no dash in package name means no SCL
        splits = pkg.name.split('-')
        if len(splits) < 2:
            return
        scl_name = splits[0]
        # While we are here, check if it's a runtime/build package
        is_runtime = splits[-1] == 'runtime'
        is_build = splits[-1] == 'build'
        del splits

        # Now test if there is /opt/foo/ dir
        good = False
        for fname in pkg.files().keys():
            if startdir.search(fname):
                good = True
                break
        if not good:
            return

        # Test if our dir is named the same way as scl
        good = True
        for fname in pkg.files().keys():
            if not startdir.search(fname):
                if allowed_etc.search(fname) or allowed_var.search(fname) or \
                   fname.startswith('/usr/bin/'):
                    continue
                if fname.startswith('/etc/rpm/'):
                    if not is_build:
                        printWarning(pkg, 'scl-rpm-macros-outside-of-build',
                                     fname)
                    continue
                if is_runtime and \
                   fname == os.path.join('/etc/scl/prefixes', scl_name):
                    continue
                printError(pkg, 'file-outside-of-scl-tree', fname)
            else:
                if fname.split('/')[3] != scl_name:
                    good = False

        if not good:
            printError(pkg, 'scl-name-screwed-up')
 def check_description(self, pkg, lang, ignored_words):
     description = pkg.langtag(rpm.RPMTAG_DESCRIPTION, lang)
     self._unexpanded_macros(pkg, '%%description -l %s' % lang, description)
     utf8desc = description
     if use_utf8:
         utf8desc = Pkg.to_utf8(description).decode('utf-8')
     spell_check(pkg, utf8desc, '%%description -l %s', lang, ignored_words)
     for l in utf8desc.splitlines():
         if len(l) > max_line_len:
             printError(pkg, 'description-line-too-long', lang, l)
         res = forbidden_words_regex.search(l)
         if res and Config.getOption('ForbiddenWords'):
             printWarning(pkg, 'description-use-invalid-word', lang,
                          res.group(1))
         res = tag_regex.search(l)
         if res:
             printWarning(pkg, 'tag-in-description', lang, res.group(1))
     if use_utf8 and not Pkg.is_utf8_str(description):
         printError(pkg, 'tag-not-utf8', '%description', lang)
Example #24
0
    def check_file(self, pkg, filename):
        root = pkg.dirName()
        f = root + filename

        checker = appdata_checker
        if checker[0] == "appstream-util" and not self.network_enabled:
            checker += ("--nonet", )
        validation_failed = False
        try:
            st = getstatusoutput(checker + (f, ))
            # Return code nonzero?
            validation_failed = (st[0] != 0)
        except OSError:
            # checker is not installed, do a validation manually
            try:
                ET.parse(pkg.dirName() + filename)
            except ET.ParseError:
                validation_failed = True
        if validation_failed:
            printError(pkg, 'invalid-appdata-file', filename)
Example #25
0
    def check_source(self, pkg):
        wrong_spec = False

        # lookup spec file
        for fname, pkgfile in pkg.files().items():
            if fname.endswith('.spec'):
                self._spec_file = pkgfile.path
                if fname == pkg.name + ".spec":
                    wrong_spec = False
                    break
                else:
                    wrong_spec = True
        if not self._spec_file:
            printError(pkg, "no-spec-file")
        else:
            if wrong_spec:
                printError(pkg, "invalid-spec-name")

            # check content of spec file
            self.check_spec(pkg, self._spec_file)
Example #26
0
    def check_source(self, pkg):
        wrong_spec = False

        # lookup spec file
        for fname, pkgfile in pkg.files().items():
            if fname.endswith('.spec'):
                self._spec_file = pkgfile.path
                if fname == pkg.name + ".spec":
                    wrong_spec = False
                    break
                else:
                    wrong_spec = True
        if not self._spec_file:
            printError(pkg, "no-spec-file")
        else:
            if wrong_spec:
                printError(pkg, "invalid-spec-name")

            # check content of spec file
            self.check_spec(pkg, self._spec_file)
Example #27
0
    def check_file(self, pkg, filename):
        root = pkg.dirName()
        f = root + filename

        checker = appdata_checker
        if checker[0] == "appstream-util" and not self.network_enabled:
            checker += ("--nonet",)
        validation_failed = False
        try:
            st = getstatusoutput(checker + (f,))
            # Return code nonzero?
            validation_failed = (st[0] != 0)
        except OSError:
            # checker is not installed, do a validation manually
            try:
                ET.parse(pkg.dirName() + filename)
            except ET.ParseError:
                validation_failed = True
        if validation_failed:
            printError(pkg, 'invalid-appdata-file', filename)
Example #28
0
    def check_file(self, pkg, filename):
        root = pkg.dirName()
        f = root + filename
        st = getstatusoutput(('desktop-file-validate', f), True)
        if st[0]:
            error_printed = False
            for line in st[1].splitlines():
                if 'error: ' in line:
                    printError(pkg, 'invalid-desktopfile', filename,
                               line.split('error: ')[1])
                    error_printed = True
            if not error_printed:
                printError(pkg, 'invalid-desktopfile', filename)
        if not is_utf8(f):
            printError(pkg, 'non-utf8-desktopfile', filename)

        cfp = RawConfigParser()
        cfp.read(f)
        binary = None
        if cfp.has_option('Desktop Entry', 'Exec'):
            binary = cfp.get('Desktop Entry', 'Exec').split(' ', 1)[0]
        if binary:
            found = False
            if binary.startswith('/'):
                found = os.path.exists(root + binary)
            else:
                for i in STANDARD_BIN_DIRS:
                    if os.path.exists(root + i + binary):
                        # no need to check if the binary is +x, rpmlint does it
                        # in another place
                        found = True
                        break
            if not found:
                printWarning(pkg, 'desktopfile-without-binary', filename,
                             binary)
Example #29
0
    def check_file(self, pkg, filename):
        root = pkg.dirName()
        f = root + filename
        st = getstatusoutput(('desktop-file-validate', f), True)
        if st[0]:
            error_printed = False
            for line in st[1].splitlines():
                if 'error: ' in line:
                    printError(pkg, 'invalid-desktopfile', filename,
                               line.split('error: ')[1])
                    error_printed = True
            if not error_printed:
                printError(pkg, 'invalid-desktopfile', filename)
        if not is_utf8(f):
            printError(pkg, 'non-utf8-desktopfile', filename)

        cfp = RawConfigParser()
        cfp.read(f)
        binary = None
        if cfp.has_option('Desktop Entry', 'Exec'):
            binary = cfp.get('Desktop Entry', 'Exec').split(' ', 1)[0]
        if binary:
            found = False
            if binary.startswith('/'):
                found = os.path.exists(root + binary)
            else:
                for i in STANDARD_BIN_DIRS:
                    if os.path.exists(root + i + binary):
                        # no need to check if the binary is +x, rpmlint does it
                        # in another place
                        found = True
                        break
            if not found:
                printWarning(pkg, 'desktopfile-without-binary', filename,
                             binary)
    def check(self, pkg):

        if pkg.isSource():
            return

        for fname, pkgfile in pkg.files().items():

            if '/animations/' in fname:
                continue

            res = self.file_size_regex.search(fname)
            if res:
                sizes = (res.group(1), res.group(2))
                res = self.info_size_regex.search(pkgfile.magic)
                if res:
                    actualsizes = (res.group(1), res.group(2))

                    if abs(int(sizes[0])-int(actualsizes[0])) > 2 or \
                            abs(int(sizes[1])-int(actualsizes[1])) > 2:
                        printError(pkg, "wrong-icon-size", fname, "expected:",
                                   "x".join(sizes),
                                   "actual:", "x".join(actualsizes))
Example #31
0
    def check(self, pkg):

        if pkg.isSource():
            return

        for fname, pkgfile in pkg.files().items():

            if '/animations/' in fname:
                continue

            res = self.file_size_regex.search(fname)
            if res:
                sizes = (res.group(1), res.group(2))
                res = self.info_size_regex.search(pkgfile.magic)
                if res:
                    actualsizes = (res.group(1), res.group(2))

                    if abs(int(sizes[0]) - int(actualsizes[0])) > 2 or \
                            abs(int(sizes[1]) - int(actualsizes[1])) > 2:
                        printError(pkg, "wrong-icon-size", fname, "expected:",
                                   "x".join(sizes), "actual:",
                                   "x".join(actualsizes))
Example #32
0
    def check(self, pkg):
        """Checks the given RPM pkg instance against the configured whitelist
        restriction.

        Each whitelist violation will be printed with the according error tag.
        Nothing is returned from this function.
        """

        from Filter import printError

        if pkg.isSource():
            return

        files = pkg.files()

        for f, meta in files.items():
            if not self._hasRestrictedMeta(meta):
                continue

            wl_match = self._getWhitelist(pkg.name, f)

            if not wl_match:
                # no whitelist entry exists for this file
                printError(pkg, self.m_error_map['unauthorized'], f)
                continue

            for audit in wl_match.audits():
                res, msg = audit.compareMeta(pkg, f, meta)

                if res:
                    if msg:
                        # a warning only message
                        print("{}: {}".format(f, msg), file=sys.stderr)
                    break

                print("{}: {}".format(f, msg), file=sys.stderr)
                printError(pkg, self.m_error_map['mismatch'], f)
Example #33
0
    def check(self, pkg):
        """Checks the given RPM pkg instance against the configured whitelist
        restriction.

        Each whitelist violation will be printed with the according error tag.
        Nothing is returned from this function.
        """

        if pkg.isSource():
            return

        files = pkg.files()

        for f in files:
            for restricted in self.m_restricted_paths:
                if f.startswith(restricted):
                    break
            else:
                # no match
                continue

            if f in pkg.ghostFiles():
                printError(pkg, self.m_error_map['ghost'], f)
                continue

            entries = self.m_whitelist_entries.get(f, [])
            wl_match = None
            for entry in entries:
                if entry.package() == pkg.name:
                    wl_match = entry
                    break
            else:
                # no whitelist entry exists for this file
                printError(pkg, self.m_error_map['unauthorized'], f)
                continue

            # for the case that there's no match of digests, remember the most
            # recent digest verification result for diagnosis output towards
            # the user
            diag_results = None

            # check the newest (bottom) entry first it is more likely to match
            # what we have
            for audit in reversed(wl_match.audits()):
                digest_matches, results = audit.compareDigests(pkg)

                if digest_matches:
                    break

                if not diag_results:
                    diag_results = results
            else:
                # none of the digest entries matched
                self._printVerificationResults(diag_results)
                printError(pkg, self.m_error_map['changed'], f)
                continue
Example #34
0
    def check(self, pkg):

        name = pkg.name
        if name and not name_regex.search(name):
            printError(pkg, 'non-lsb-compliant-package-name', name)

        version = pkg[rpm.RPMTAG_VERSION]
        if version and not version_regex.search(version):
            printError(pkg, 'non-lsb-compliant-version', version)

        release = pkg[rpm.RPMTAG_RELEASE]
        if release and not version_regex.search(release):
            printError(pkg, 'non-lsb-compliant-release', release)
Example #35
0
    def check(self, pkg):

        name = pkg.name
        if name and not name_regex.search(name):
            printError(pkg, 'non-lsb-compliant-package-name', name)

        version = pkg[rpm.RPMTAG_VERSION]
        if version and not version_regex.search(version):
            printError(pkg, 'non-lsb-compliant-version', version)

        release = pkg[rpm.RPMTAG_RELEASE]
        if release and not version_regex.search(release):
            printError(pkg, 'non-lsb-compliant-release', release)
Example #36
0
    def check(self, pkg):
        if pkg.isSource():
            return

        for req in pkg.requires() + pkg.prereq():
            if req[0] == 'insserv':
                printError(pkg, insserv_tag)

        for fn, pkgfile in pkg.files().items():
            if not fn.startswith('/etc/init.d'):
                continue
            if os.path.basename(fn).startswith('boot.'):
                printError(pkg, bootscr_tag, fn)
            else:
                printError(pkg, etcinit_tag, fn)
Example #37
0
    def check_file(self, pkg, filename):
        root = pkg.dirName()
        f = root + filename
        st = getstatusoutput(('desktop-file-validate', f), True)
        if st[0]:
            error_printed = False
            for line in st[1].splitlines():
                if 'error: ' in line:
                    printError(pkg, 'invalid-desktopfile', filename,
                               line.split('error: ')[1])
                    error_printed = True
            if not error_printed:
                printError(pkg, 'invalid-desktopfile', filename)
        if not is_utf8(f):
            printError(pkg, 'non-utf8-desktopfile', filename)

        self.parse_desktop_file(pkg, root, f, filename)
Example #38
0
    def check_file(self, pkg, filename):
        root = pkg.dirName()
        f = root + filename
        st = getstatusoutput(('desktop-file-validate', f), True)
        if st[0]:
            error_printed = False
            for line in st[1].splitlines():
                if 'error: ' in line:
                    printError(pkg, 'invalid-desktopfile', filename,
                               line.split('error: ')[1])
                    error_printed = True
            if not error_printed:
                printError(pkg, 'invalid-desktopfile', filename)
        if not is_utf8(f):
            printError(pkg, 'non-utf8-desktopfile', filename)

        self.parse_desktop_file(pkg, root, f, filename)
Example #39
0
 def parse_desktop_file(self, pkg, root, f, filename):
     cfp = cfgparser.RawConfigParser()
     try:
         with codecs.open(f, encoding='utf-8') as inputf:
             cfp.readfp(inputf, filename)
     except cfgparser.DuplicateSectionError as e:
         printError(
             pkg, 'desktopfile-duplicate-section', filename,
             '[%s]' % e.section)
     except cfgparser.MissingSectionHeaderError:
         printError(
             pkg, 'desktopfile-missing-header', filename)
     except cfgparser.Error as e:
         # Only in Python >= 3.2
         if (hasattr(cfgparser, 'DuplicateOptionError') and
                 isinstance(e, cfgparser.DuplicateOptionError)):
             printError(
                 pkg, 'desktopfile-duplicate-option', filename,
                 '[%s]/%s' % (e.section, e.option))
         else:
             printWarning(
                 pkg, 'invalid-desktopfile', filename,
                 e.message.partition(':')[0])
     except UnicodeDecodeError as e:
         printWarning(
             pkg, 'invalid-desktopfile', filename, 'No valid Unicode')
     else:
         binary = None
         if cfp.has_option('Desktop Entry', 'Exec'):
             binary = cfp.get('Desktop Entry', 'Exec').partition(' ')[0]
         if binary:
             found = False
             if binary.startswith('/'):
                 found = os.path.exists(root + binary)
             else:
                 for i in STANDARD_BIN_DIRS:
                     if os.path.exists(root + i + '/' + binary):
                         # no need to check if the binary is +x, rpmlint does it
                         # in another place
                         found = True
                         break
             if not found:
                 printWarning(
                     pkg, 'desktopfile-without-binary', filename, binary)
Example #40
0
 def parse_desktop_file(self, pkg, root, f, filename):
     cfp = cfgparser.RawConfigParser()
     try:
         with codecs.open(f, encoding='utf-8') as inputf:
             cfp.readfp(inputf, filename)
     except cfgparser.DuplicateSectionError as e:
         printError(pkg, 'desktopfile-duplicate-section', filename,
                    '[%s]' % e.section)
     except cfgparser.MissingSectionHeaderError:
         printError(pkg, 'desktopfile-missing-header', filename)
     except cfgparser.Error as e:
         # Only in Python >= 3.2
         if (hasattr(cfgparser, 'DuplicateOptionError')
                 and isinstance(e, cfgparser.DuplicateOptionError)):
             printError(pkg, 'desktopfile-duplicate-option', filename,
                        '[%s]/%s' % (e.section, e.option))
         else:
             printWarning(pkg, 'invalid-desktopfile', filename,
                          e.message.partition(':')[0])
     except UnicodeDecodeError as e:
         printWarning(pkg, 'invalid-desktopfile', filename,
                      'No valid Unicode')
     else:
         binary = None
         if cfp.has_option('Desktop Entry', 'Exec'):
             binary = cfp.get('Desktop Entry', 'Exec').partition(' ')[0]
         if binary:
             found = False
             if binary.startswith('/'):
                 found = os.path.exists(root + binary)
             else:
                 for i in STANDARD_BIN_DIRS:
                     if os.path.exists(root + i + '/' + binary):
                         # no need to check if the binary is +x, rpmlint does it
                         # in another place
                         found = True
                         break
             if not found:
                 printWarning(pkg, 'desktopfile-without-binary', filename,
                              binary)
Example #41
0
    def check_aux(self, pkg, files, prog, script, tag, prereq):
        if script:
            if prog:
                if prog not in valid_shells:
                    printError(pkg, "invalid-shell-in-" + tag, prog)
                if prog in empty_shells:
                    printError(pkg, "non-empty-" + tag, prog)
            if prog in syntaxcheck_shells or prog == "/usr/bin/perl":
                if percent_regex.search(script):
                    printWarning(pkg, "percent-in-" + tag)
                if bracket_regex.search(script):
                    printWarning(pkg, "spurious-bracket-in-" + tag)
                res = dangerous_command_regex.search(script)
                if res:
                    printWarning(pkg, "dangerous-command-in-" + tag, res.group(2))
                res = selinux_regex.search(script)
                if res:
                    printError(pkg, "forbidden-selinux-command-in-" + tag, res.group(2))

                if "update-menus" in script:
                    menu_error = True
                    for f in files:
                        if menu_regex.search(f):
                            menu_error = False
                            break
                    if menu_error:
                        printError(pkg, "update-menus-without-menu-file-in-" + tag)
                if tmp_regex.search(script):
                    printError(pkg, "use-tmp-in-" + tag)
                for c in prereq_assoc:
                    if c[0].search(script):
                        found = False
                        for p in c[1]:
                            if p in prereq or p in files:
                                found = True
                                break
                        if not found:
                            printError(pkg, "no-prereq-on", c[1][0])

            if prog in syntaxcheck_shells:
                if incorrect_shell_script(prog, script):
                    printError(pkg, "shell-syntax-error-in-" + tag)
                if home_regex.search(script):
                    printError(pkg, "use-of-home-in-" + tag)
                res = bogus_var_regex.search(script)
                if res:
                    printWarning(pkg, "bogus-variable-use-in-" + tag, res.group(1))

            if prog == "/usr/bin/perl":
                if incorrect_perl_script(prog, script):
                    printError(pkg, "perl-syntax-error-in-" + tag)
            elif prog.endswith("sh"):
                res = single_command_regex.search(script)
                if res:
                    printWarning(pkg, "one-line-command-in-" + tag, res.group(1))

        elif prog not in empty_shells and prog in valid_shells:
            printWarning(pkg, "empty-" + tag)
    def check(self, pkg):
        global _permissions_d_whitelist

        if pkg.isSource():
            return

        files = pkg.files()

        permfiles = {}
        # first pass, find and parse permissions.d files
        for f in files:
            if f in pkg.ghostFiles():
                continue

            if f.startswith("/etc/permissions.d/"):

                bn = f[19:]
                if bn not in _permissions_d_whitelist:
                    printError(pkg, "permissions-unauthorized-file", f)

                bn = bn.split('.')[0]
                if bn not in permfiles:
                    permfiles[bn] = 1

        for f in permfiles:
            f = pkg.dirName() + "/etc/permissions.d/" + f
            if os.path.exists(f + ".secure"):
                self._parsefile(f + ".secure")
            else:
                self._parsefile(f)

        need_set_permissions = False
        found_suseconfig = False
        # second pass, find permissions violations
        for f, pkgfile in files.items():

            if pkgfile.filecaps:
                printError(pkg, 'permissions-fscaps',
                                '%(fname)s has fscaps "%(caps)s"' %
                                {'fname': f, 'caps': pkgfile.filecaps})

            mode = pkgfile.mode
            owner = pkgfile.user + ':' + pkgfile.group

#           S_IFSOCK   014   socket
#           S_IFLNK    012   symbolic link
#           S_IFREG    010   regular file
#           S_IFBLK    006   block device
#           S_IFDIR    004   directory
#           S_IFCHR    002   character device
#           S_IFIFO    001   FIFO
            type = (mode >> 12) & 0o17
            mode &= 0o7777
            need_verifyscript = False
            if f in self.perms or (type == 4 and f + "/" in self.perms):
                if type == 0o12:
                    printWarning(pkg, "permissions-symlink", f)
                    continue

                need_verifyscript = True

                m = 0
                o = "invalid"
                if type == 4:
                    if f in self.perms:
                        printWarning(pkg, 'permissions-dir-without-slash', f)
                    else:
                        f += '/'

                if type == 0o10 and mode & 0o111:
                    # pie binaries have 'shared object' here
                    if (pkgfile.magic.startswith('ELF ') and
                            ('shared object' not in pkgfile.magic) and
                            ('pie executable' not in pkgfile.magic)):
                        printError(pkg, 'non-position-independent-executable',
                                   f)

                m = self.perms[f]['mode']
                o = self.perms[f]['owner']

                if mode != m:
                    printError(
                        pkg, 'permissions-incorrect',
                        '%(file)s has mode 0%(mode)o but should be 0%(m)o' %
                        {'file': f, 'mode': mode, 'm': m})

                if owner != o:
                    printError(
                        pkg, 'permissions-incorrect-owner',
                        '%(file)s belongs to %(owner)s but should be %(o)s' %
                        {'file': f, 'owner': owner, 'o': o})

            elif type != 0o12:

                if f + '/' in self.perms:
                    printWarning(
                        pkg, 'permissions-file-as-dir',
                        f + ' is a file but listed as directory')

                if mode & 0o6000:
                    need_verifyscript = True
                    msg = '%(file)s is packaged with ' \
                          'setuid/setgid bits (0%(mode)o)' % \
                          {'file': f, 'mode': mode}
                    if type != 0o4:
                        printError(pkg, 'permissions-file-setuid-bit', msg)
                    else:
                        printWarning(pkg, 'permissions-directory-setuid-bit', msg)

                    if type == 0o10:
                        if ('shared object' not in pkgfile.magic and
                                'pie executable' not in pkgfile.magic):
                            printError(pkg, 'non-position-independent-executable', f)

                if mode & 0o2:
                    need_verifyscript = True
                    printError(pkg, 'permissions-world-writable',
                               '%(file)s is packaged with world writable permissions (0%(mode)o)' %
                               {'file': f, 'mode': mode})

            script = pkg[rpm.RPMTAG_POSTIN] or pkg.scriptprog(rpm.RPMTAG_POSTINPROG)
            found = False
            if script:
                for line in script.split("\n"):
                    if "chkstat -n" in line and f in line:
                        found = True
                        break

                    if "SuSEconfig --module permissions" in line \
                            or "run_permissions is obsolete" in line:
                        found = True
                        found_suseconfig = True
                        break

            if need_verifyscript and \
                    (f not in self.perms or 'static' not in self.perms[f]):

                if not script or not found:
                    printError(pkg, 'permissions-missing-postin',
                               "missing %%set_permissions %s in %%post" % f)

                need_set_permissions = True
                script = pkg[rpm.RPMTAG_VERIFYSCRIPT] or pkg[rpm.RPMTAG_VERIFYSCRIPTPROG]

                found = False
                if script:
                    for line in script.split("\n"):
                        if "/chkstat" in line and f in line:
                            found = True
                            break

                if not script or not found:
                    printWarning(pkg, 'permissions-missing-verifyscript',
                                 "missing %%verify_permissions -e %s" % f)

        if need_set_permissions:
            if 'permissions' not in map(lambda x: x[0], pkg.prereq()):
                printError(pkg, 'permissions-missing-requires',
                           "missing 'permissions' in PreReq")

        if found_suseconfig:
            printInfo(pkg, 'permissions-suseconfig-obsolete',
                      "%run_permissions is obsolete")
    def check_scl_spec(self, pkg, spec):
        '''SCL ready spec checks'''

        # For the entire spec
        if not pkg_name.search(spec):
            printWarning(pkg, 'missing-pkg_name-definition')
        if scl_prefix_noncond.search(self.remove_scl_conds(spec)):
            printWarning(pkg, 'scl-prefix-without-condition')
        if not scl_prefix.search(self.get_name(spec)):
            printError(pkg, 'name-without-scl-prefix')
        for item in self.get_obsoletes_and_conflicts(spec):
            if not scl_prefix.search(item):
                printError(pkg, 'obsoletes-or-conflicts-without-scl-prefix')
                break
        for item in self.get_provides(spec):
            if not scl_prefix.search(item):
                printError(pkg, 'provides-without-scl-prefix')
                break
        setup_opts = setup.search(spec)
        if setup_opts:
            if '-n' not in setup_opts.groups()[0]:
                printError(pkg, 'scl-setup-without-n')

        # Examine main package and subpackages one by one
        borders = []
        borders.append(0)  # main package starts at the beginning
        while True:
            more = subpackage_any.search(spec[borders[-1]:])
            if not more:
                break
            splits = more.groups()[1].split()
            if len(splits) > 1 and splits[0] == '-n':
                if not scl_prefix_start.search(splits[-1]):
                    printError(pkg, 'subpackage-with-n-without-scl-prefix')
            # current end is counted only from last one
            borders.append(borders[-1]+more.end())
        subpackages = [(borders[i], borders[i+1])
                       for i in range(len(borders)-1)]
        for subpackage in subpackages:
            ok = False
            for require in self.get_requires(spec[subpackage[0]:subpackage[1]]):
                # Remove flase entries
                if not require or require == ':':
                    continue
                # If it starts with %{name}, it,s fine
                # If it starts with SCL prefix, it's fine
                # If it is scl-runtime, it's the best
                if name_small.search(require) or \
                   scl_prefix_start.search(require) or \
                   scl_runtime.match(require):
                    ok = True
                    break
            if not ok:
                printError(pkg,
                           'doesnt-require-scl-runtime-or-other-scl-package')
                break
    def check_metapackage(self, pkg, spec):
        '''SCL metapackage spec checks'''

        # Examine subpackages
        runtime = subpackage_runtime.search(spec)
        if not runtime:
            printError(pkg, 'no-runtime-in-scl-metapackage')

        build = subpackage_build.search(spec)
        if not build:
            printError(pkg, 'no-build-in-scl-metapackage')
        else:
            # Get (B)Rs section for build subpackage
            end = index_or_sub(spec[build.end():], '%package', -1)
            if 'scl-utils-build' not in \
               ' '.join(self.get_requires(spec[build.end():end])):
                printWarning(pkg,
                             'scl-build-without-requiring-scl-utils-build')

        alien = subpackage_alien.search(spec)
        if alien:
            printError(pkg, 'weird-subpackage-in-scl-metapackage',
                       alien.group()[9:])

        # Get (B)Rs section for main package
        end = index_or_sub(spec, '%package', -1)
        if 'scl-utils-build' not in \
           ' '.join(self.get_build_requires(spec[:end])):
            printError(pkg, 'scl-metapackage-without-scl-utils-build-br')

        # Enter %install section
        install_start = index_or_sub(spec, '%install')
        install_end = index_or_sub(spec, '%check')
        if not install_end:
            install_end = index_or_sub(spec, '%clean')
        if not install_end:
            install_end = index_or_sub(spec, '%files')
        if not install_end:
            install_end = index_or_sub(spec, '%changelog', -1)
        # Search %scl_install
        if not scl_install.search(spec[install_start:install_end]):
            printError(pkg, 'scl-metapackage-without-%scl_install')
        if noarch.search(spec[:install_start]) and \
           libdir.search(spec[install_start:install_end]):
            printError(pkg, 'noarch-scl-metapackage-with-libdir')

        # Analyze %files
        files = self.get_files(spec)
        if files:
            printWarning(pkg, 'scl-main-metapackage-contains-files',
                         ', '.join(files))
        if runtime:
            if not scl_files.search(
                    '\n'.join(self.get_files(spec, 'runtime'))):
                printError(pkg, 'scl-runtime-package-without-%scl_files')
        if build:
            if not scl_macros.search(
                    '\n'.join(self.get_files(spec, 'build'))):
                printError(pkg, 'scl-build-package-without-rpm-macros')
Example #45
0
    def check_aux(self, pkg, files, prog, script, tag, prereq):
        if script:
            if prog:
                if prog not in valid_shells:
                    printError(pkg, 'invalid-shell-in-' + tag, prog)
                if prog in empty_shells:
                    printError(pkg, 'non-empty-' + tag, prog)
            if prog in syntaxcheck_shells or prog == '/usr/bin/perl':
                if percent_regex.search(script):
                    printWarning(pkg, 'percent-in-' + tag)
                if bracket_regex.search(script):
                    printWarning(pkg, 'spurious-bracket-in-' + tag)
                res = dangerous_command_regex.search(script)
                if res:
                    printWarning(pkg, 'dangerous-command-in-' + tag,
                                 res.group(2))
                res = selinux_regex.search(script)
                if res:
                    printError(pkg, 'forbidden-selinux-command-in-' + tag,
                               res.group(2))

                if 'update-menus' in script:
                    menu_error = True
                    for f in files:
                        if menu_regex.search(f):
                            menu_error = False
                            break
                    if menu_error:
                        printError(pkg,
                                   'update-menus-without-menu-file-in-' + tag)
                if tmp_regex.search(script):
                    printError(pkg, 'use-tmp-in-' + tag)
                for c in prereq_assoc:
                    if c[0].search(script):
                        found = False
                        for p in c[1]:
                            if p in prereq or p in files:
                                found = True
                                break
                        if not found:
                            printError(pkg, 'no-prereq-on', c[1][0])

            if prog in syntaxcheck_shells:
                if incorrect_shell_script(prog, script):
                    printError(pkg, 'shell-syntax-error-in-' + tag)
                if home_regex.search(script):
                    printError(pkg, 'use-of-home-in-' + tag)
                res = bogus_var_regex.search(script)
                if res:
                    printWarning(pkg, 'bogus-variable-use-in-' + tag,
                                 res.group(1))

            if prog == '/usr/bin/perl':
                if incorrect_perl_script(prog, script):
                    printError(pkg, 'perl-syntax-error-in-' + tag)
            elif prog.endswith('sh'):
                res = single_command_regex.search(script)
                if res:
                    printWarning(pkg, 'one-line-command-in-' + tag,
                                 res.group(1))

        elif prog not in empty_shells and prog in valid_shells:
            printWarning(pkg, 'empty-' + tag)
Example #46
0
    def check_binary(self, pkg):
        files = pkg.files()
        menus = []

        for fname, pkgfile in files.items():
            # Check menu files
            res = menu_file_regex.search(fname)
            mode = pkgfile.mode
            if res:
                basename = res.group(1)
                if not stat.S_ISREG(mode):
                    printError(pkg, 'non-file-in-menu-dir', fname)
                else:
                    if basename != pkg.name:
                        printWarning(pkg, 'non-coherent-menu-filename', fname)
                    if mode & 0o444 != 0o444:
                        printError(pkg, 'non-readable-menu-file', fname)
                    if mode & 0o111:
                        printError(pkg, 'executable-menu-file', fname)
                    menus.append(fname)
            else:
                # Check old menus from KDE and GNOME
                res = old_menu_file_regex.search(fname)
                if res:
                    if stat.S_ISREG(mode):
                        printError(pkg, 'old-menu-entry', fname)
                else:
                    # Check non transparent xpm files
                    res = xpm_ext_regex.search(fname)
                    if res:
                        if stat.S_ISREG(mode) and not pkg.grep(
                                'None",', fname):
                            printWarning(pkg, 'non-transparent-xpm', fname)
                if fname.startswith('/usr/lib64/menu'):
                    printError(pkg, 'menu-in-wrong-dir', fname)

        if menus:
            postin = pkg[rpm.RPMTAG_POSTIN] or \
                pkg.scriptprog(rpm.RPMTAG_POSTINPROG)
            if not postin:
                printError(pkg, 'menu-without-postin')
            elif not update_menus_regex.search(postin):
                printError(pkg, 'postin-without-update-menus')

            postun = pkg[rpm.RPMTAG_POSTUN] or \
                pkg.scriptprog(rpm.RPMTAG_POSTUNPROG)
            if not postun:
                printError(pkg, 'menu-without-postun')
            elif not update_menus_regex.search(postun):
                printError(pkg, 'postun-without-update-menus')

            directory = pkg.dirName()
            for f in menus:
                # remove comments and handle cpp continuation lines
                cmd = Pkg.getstatusoutput(('/lib/cpp', directory + f), True)[1]

                for line in cmd.splitlines():
                    if not line.startswith('?'):
                        continue
                    res = package_regex.search(line)
                    if res:
                        package = res.group(1)
                        if package != pkg.name:
                            printWarning(pkg,
                                         'incoherent-package-value-in-menu',
                                         package, f)
                    else:
                        printInfo(pkg, 'unable-to-parse-menu-entry', line)

                    command = True
                    res = command_regex.search(line)
                    if res:
                        command_line = (res.group(1) or res.group(2)).split()
                        command = command_line[0]
                        for launcher in launchers:
                            if not launcher[0].search(command):
                                continue
                            found = False
                            if launcher[1]:
                                found = '/bin/' + command_line[0] in files or \
                                    '/usr/bin/' + command_line[0] in files or \
                                    '/usr/X11R6/bin/' + command_line[0] \
                                    in files
                                if not found:
                                    for l in launcher[1]:
                                        if l in pkg.req_names():
                                            found = True
                                            break
                                if not found:
                                    printError(
                                        pkg,
                                        'use-of-launcher-in-menu-but-no-requires-on',
                                        launcher[1][0])
                            command = command_line[1]
                            break

                        if command[0] == '/':
                            if command not in files:
                                printWarning(pkg,
                                             'menu-command-not-in-package',
                                             command)
                        elif not ('/bin/' + command in files
                                  or '/usr/bin/' + command in files
                                  or '/usr/X11R6/bin/' + command in files):
                            printWarning(pkg, 'menu-command-not-in-package',
                                         command)
                    else:
                        printWarning(pkg, 'missing-menu-command')
                        command = False

                    res = longtitle_regex.search(line)
                    if res:
                        grp = res.groups()
                        title = grp[1] or grp[2]
                        if title[0] != title[0].upper():
                            printWarning(pkg, 'menu-longtitle-not-capitalized',
                                         title)
                        res = version_regex.search(title)
                        if res:
                            printWarning(pkg, 'version-in-menu-longtitle',
                                         title)
                    else:
                        printError(pkg, 'no-longtitle-in-menu', f)
                        title = None

                    res = title_regex.search(line)
                    if res:
                        grp = res.groups()
                        title = grp[1] or grp[2]
                        if title[0] != title[0].upper():
                            printWarning(pkg, 'menu-title-not-capitalized',
                                         title)
                        res = version_regex.search(title)
                        if res:
                            printWarning(pkg, 'version-in-menu-title', title)
                        if '/' in title:
                            printError(pkg, 'invalid-title', title)
                    else:
                        printError(pkg, 'no-title-in-menu', f)
                        title = None

                    res = needs_regex.search(line)
                    if res:
                        grp = res.groups()
                        needs = (grp[1] or grp[2]).lower()
                        if needs in ('x11', 'text', 'wm'):
                            res = section_regex.search(line)
                            if res:
                                grp = res.groups()
                                section = grp[1] or grp[2]
                                # don't warn entries for sections
                                if command and section not in valid_sections:
                                    printError(pkg, 'invalid-menu-section',
                                               section, f)
                            else:
                                printInfo(pkg, 'unable-to-parse-menu-section',
                                          line)
                        elif needs not in standard_needs:
                            printInfo(pkg, 'strange-needs', needs, f)
                    else:
                        printInfo(pkg, 'unable-to-parse-menu-needs', line)

                    res = icon_regex.search(line)
                    if res:
                        icon = res.group(1)
                        if not icon_ext_regex.search(icon):
                            printWarning(pkg, 'invalid-menu-icon-type', icon)
                        if icon[0] == '/' and needs == 'x11':
                            printWarning(pkg, 'hardcoded-path-in-menu-icon',
                                         icon)
                        else:
                            for path in icon_paths:
                                if (path[0] + icon) not in files:
                                    printError(
                                        pkg, path[1] + '-icon-not-in-package',
                                        icon, f)
                    else:
                        printWarning(pkg, 'no-icon-in-menu', title)

                    res = xdg_migrated_regex.search(line)
                    if res:
                        if not res.group(1).lower() == "true":
                            printError(pkg, 'non-xdg-migrated-menu')
                    else:
                        printError(pkg, 'non-xdg-migrated-menu')
Example #47
0
    def check(self, pkg):

        packager = pkg[rpm.RPMTAG_PACKAGER]
        if packager:
            self._unexpanded_macros(pkg, 'Packager', packager)
            if Config.getOption('Packager') and \
               not packager_regex.search(packager):
                printWarning(pkg, 'invalid-packager', packager)
        else:
            printError(pkg, 'no-packager-tag')

        version = pkg[rpm.RPMTAG_VERSION]
        if version:
            self._unexpanded_macros(pkg, 'Version', version)
            res = invalid_version_regex.search(version)
            if res:
                printError(pkg, 'invalid-version', version)
        else:
            printError(pkg, 'no-version-tag')

        release = pkg[rpm.RPMTAG_RELEASE]
        if release:
            self._unexpanded_macros(pkg, 'Release', release)
            if release_ext and not extension_regex.search(release):
                printWarning(pkg, 'not-standard-release-extension', release)
        else:
            printError(pkg, 'no-release-tag')

        epoch = pkg[rpm.RPMTAG_EPOCH]
        if epoch is None:
            if use_epoch:
                printError(pkg, 'no-epoch-tag')
        else:
            if epoch > 99:
                printWarning(pkg, 'unreasonable-epoch', epoch)
            epoch = str(epoch)

        if use_epoch:
            for tag in ("obsoletes", "conflicts", "provides", "recommends",
                        "suggests", "enhances", "supplements"):
                for x in (x for x in getattr(pkg, tag)()
                          if x[1] and x[2][0] is None):
                    printWarning(pkg, 'no-epoch-in-%s' % tag,
                                 Pkg.formatRequire(*x))

        name = pkg.name
        deps = pkg.requires() + pkg.prereq()
        devel_depend = False
        is_devel = FilesCheck.devel_regex.search(name)
        is_source = pkg.isSource()
        for d in deps:
            value = Pkg.formatRequire(*d)
            if use_epoch and d[1] and d[2][0] is None and \
                    not d[0].startswith('rpmlib('):
                printWarning(pkg, 'no-epoch-in-dependency', value)
            for r in INVALID_REQUIRES:
                if r.search(d[0]):
                    printError(pkg, 'invalid-dependency', d[0])

            if d[0].startswith('/usr/local/'):
                printError(pkg, 'invalid-dependency', d[0])

            if is_source:
                if lib_devel_number_regex.search(d[0]):
                    printError(pkg, 'invalid-build-requires', d[0])
            elif not is_devel:
                if not devel_depend and FilesCheck.devel_regex.search(d[0]):
                    printError(pkg, 'devel-dependency', d[0])
                    devel_depend = True
                if not d[1]:
                    res = lib_package_regex.search(d[0])
                    if res and not res.group(1):
                        printError(pkg, 'explicit-lib-dependency', d[0])

            if d[1] == rpm.RPMSENSE_EQUAL and d[2][2] is not None:
                printWarning(pkg, 'requires-on-release', value)
            self._unexpanded_macros(pkg, 'dependency %s' % (value,), value)

        self._unexpanded_macros(pkg, 'Name', name)
        if not name:
            printError(pkg, 'no-name-tag')
        else:
            if is_devel and not is_source:
                base = is_devel.group(1)
                dep = None
                has_so = False
                for fname in pkg.files():
                    if fname.endswith('.so'):
                        has_so = True
                        break
                if has_so:
                    base_or_libs = base + '/' + base + '-libs/lib' + base
                    # try to match *%_isa as well (e.g. "(x86-64)", "(x86-32)")
                    base_or_libs_re = re.compile(
                        r'^(lib)?%s(-libs)?(\(\w+-\d+\))?$' % re.escape(base))
                    for d in deps:
                        if base_or_libs_re.match(d[0]):
                            dep = d
                            break
                    if not dep:
                        printWarning(pkg, 'no-dependency-on', base_or_libs)
                    elif version:
                        exp = (epoch, version, None)
                        sexp = Pkg.versionToString(exp)
                        if not dep[1]:
                            printWarning(pkg, 'no-version-dependency-on',
                                         base_or_libs, sexp)
                        elif dep[2][:2] != exp[:2]:
                            printWarning(pkg,
                                         'incoherent-version-dependency-on',
                                         base_or_libs,
                                         Pkg.versionToString((dep[2][0],
                                                              dep[2][1], None)),
                                         sexp)
                    res = devel_number_regex.search(name)
                    if not res:
                        printWarning(pkg, 'no-major-in-name', name)
                    else:
                        if res.group(3):
                            prov = res.group(1) + res.group(2) + '-devel'
                        else:
                            prov = res.group(1) + '-devel'

                        if prov not in (x[0] for x in pkg.provides()):
                            printWarning(pkg, 'no-provides', prov)

        # List of words to ignore in spell check
        ignored_words = set()
        for pf in pkg.files():
            ignored_words.update(pf.split('/'))
        ignored_words.update((x[0] for x in pkg.provides()))
        ignored_words.update((x[0] for x in pkg.requires()))
        ignored_words.update((x[0] for x in pkg.conflicts()))
        ignored_words.update((x[0] for x in pkg.obsoletes()))

        langs = pkg[rpm.RPMTAG_HEADERI18NTABLE]

        summary = pkg[rpm.RPMTAG_SUMMARY]
        if summary:
            if not langs:
                self._unexpanded_macros(pkg, 'Summary', Pkg.b2s(summary))
            else:
                for lang in langs:
                    self.check_summary(pkg, lang, ignored_words)
        else:
            printError(pkg, 'no-summary-tag')

        description = pkg[rpm.RPMTAG_DESCRIPTION]
        if description:
            if not langs:
                self._unexpanded_macros(pkg, '%description',
                                        Pkg.b2s(description))
            else:
                for lang in langs:
                    self.check_description(pkg, lang, ignored_words)
        else:
            printError(pkg, 'no-description-tag')

        group = pkg[rpm.RPMTAG_GROUP]
        self._unexpanded_macros(pkg, 'Group', group)
        if not group:
            printError(pkg, 'no-group-tag')
        elif VALID_GROUPS and group not in VALID_GROUPS:
            printWarning(pkg, 'non-standard-group', group)

        buildhost = pkg[rpm.RPMTAG_BUILDHOST]
        self._unexpanded_macros(pkg, 'BuildHost', buildhost)
        if not buildhost:
            printError(pkg, 'no-buildhost-tag')
        elif Config.getOption('ValidBuildHost') and \
                not valid_buildhost_regex.search(buildhost):
            printWarning(pkg, 'invalid-buildhost', buildhost)

        changelog = pkg[rpm.RPMTAG_CHANGELOGNAME]
        if not changelog:
            printError(pkg, 'no-changelogname-tag')
        else:
            clt = pkg[rpm.RPMTAG_CHANGELOGTEXT]
            if use_version_in_changelog:
                ret = changelog_version_regex.search(Pkg.b2s(changelog[0]))
                if not ret and clt:
                    # we also allow the version specified as the first
                    # thing on the first line of the text
                    ret = changelog_text_version_regex.search(Pkg.b2s(clt[0]))
                if not ret:
                    printWarning(pkg, 'no-version-in-last-changelog')
                elif version and release:
                    srpm = pkg[rpm.RPMTAG_SOURCERPM] or ''
                    # only check when source name correspond to name
                    if srpm[0:-8] == '%s-%s-%s' % (name, version, release):
                        expected = [version + '-' + release]
                        if epoch is not None:  # regardless of use_epoch
                            expected[0] = str(epoch) + ':' + expected[0]
                        # Allow EVR in changelog without release extension,
                        # the extension is often a macro or otherwise dynamic.
                        if release_ext:
                            expected.append(
                                extension_regex.sub('', expected[0]))
                        if ret.group(1) not in expected:
                            if len(expected) == 1:
                                expected = expected[0]
                            printWarning(pkg, 'incoherent-version-in-changelog',
                                         ret.group(1), expected)

            if use_utf8:
                if clt:
                    changelog = changelog + clt
                for s in changelog:
                    if not Pkg.is_utf8_bytestr(s):
                        printError(pkg, 'tag-not-utf8', '%changelog')
                        break

            clt = pkg[rpm.RPMTAG_CHANGELOGTIME][0]
            if clt:
                clt -= clt % (24 * 3600)  # roll back to 00:00:00, see #246
                if clt < oldest_changelog_timestamp:
                    printWarning(pkg, 'changelog-time-overflow',
                                 time.strftime("%Y-%m-%d", time.gmtime(clt)))
                elif clt > time.time():
                    printError(pkg, 'changelog-time-in-future',
                               time.strftime("%Y-%m-%d", time.gmtime(clt)))

#         for provide_name in (x[0] for x in pkg.provides()):
#             if name == provide_name:
#                 printWarning(pkg, 'package-provides-itself')
#                 break

        def split_license(license):
            return (x.strip() for x in
                    (l for l in license_regex.split(license) if l))

        rpm_license = pkg[rpm.RPMTAG_LICENSE]
        if not rpm_license:
            printError(pkg, 'no-license')
        else:
            valid_license = True
            if rpm_license not in VALID_LICENSES:
                for l1 in split_license(rpm_license):
                    if l1 in VALID_LICENSES:
                        continue
                    for l2 in split_license(l1):
                        if l2 not in VALID_LICENSES:
                            printWarning(pkg, 'invalid-license', l2)
                            valid_license = False
            if not valid_license:
                self._unexpanded_macros(pkg, 'License', rpm_license)

        for tag in ('URL', 'DistURL', 'BugURL'):
            if hasattr(rpm, 'RPMTAG_%s' % tag.upper()):
                url = Pkg.b2s(pkg[getattr(rpm, 'RPMTAG_%s' % tag.upper())])
                self._unexpanded_macros(pkg, tag, url, is_url=True)
                if url:
                    (scheme, netloc) = urlparse(url)[0:2]
                    if not scheme or not netloc or "." not in netloc or \
                            scheme not in ('http', 'https', 'ftp') or \
                            (Config.getOption('InvalidURL') and
                             invalid_url_regex.search(url)):
                        printWarning(pkg, 'invalid-url', tag, url)
                    else:
                        self.check_url(pkg, tag, url)
                elif tag == 'URL':
                    printWarning(pkg, 'no-url-tag')

        obs_names = [x[0] for x in pkg.obsoletes()]
        prov_names = [x[0] for x in pkg.provides()]

        for o in (x for x in obs_names if x not in prov_names):
            printWarning(pkg, 'obsolete-not-provided', o)
        for o in pkg.obsoletes():
            value = Pkg.formatRequire(*o)
            self._unexpanded_macros(pkg, 'Obsoletes %s' % (value,), value)

        # TODO: should take versions, <, <=, =, >=, > into account here
        #       https://bugzilla.redhat.com/460872
        useless_provides = []
        for p in prov_names:
            if prov_names.count(p) != 1 and p not in useless_provides:
                useless_provides.append(p)
        for p in useless_provides:
            printError(pkg, 'useless-provides', p)

        for p in pkg.provides():
            value = Pkg.formatRequire(*p)
            self._unexpanded_macros(pkg, 'Provides %s' % (value,), value)

        for c in pkg.conflicts():
            value = Pkg.formatRequire(*c)
            self._unexpanded_macros(pkg, 'Conflicts %s' % (value,), value)

        obss = pkg.obsoletes()
        if obss:
            provs = pkg.provides()
            for prov in provs:
                for obs in obss:
                    if Pkg.rangeCompare(obs, prov):
                        printWarning(pkg, 'self-obsoletion',
                                     '%s obsoletes %s' %
                                     (Pkg.formatRequire(*obs),
                                      Pkg.formatRequire(*prov)))

        expfmt = rpm.expandMacro("%{_build_name_fmt}")
        if pkg.isSource():
            # _build_name_fmt often (always?) ends up not outputting src/nosrc
            # as arch for source packages, do it ourselves
            expfmt = re.sub(r'(?i)%\{?ARCH\b\}?', pkg.arch, expfmt)
        expected = pkg.header.sprintf(expfmt).split("/")[-1]
        basename = os.path.basename(pkg.filename)
        if basename != expected:
            printWarning(pkg, 'non-coherent-filename', basename, expected)

        for tag in ('Distribution', 'DistTag', 'ExcludeArch', 'ExcludeOS',
                    'Vendor'):
            if hasattr(rpm, 'RPMTAG_%s' % tag.upper()):
                res = Pkg.b2s(pkg[getattr(rpm, 'RPMTAG_%s' % tag.upper())])
                self._unexpanded_macros(pkg, tag, res)

        for path in private_so_paths:
            for fname, pkgfile in pkg.files().items():
                if fname.startswith(path):
                    for prov in pkgfile.provides:
                        if so_dep_regex.search(prov[0]):
                            printWarning(pkg, "private-shared-object-provides",
                                         fname, Pkg.formatRequire(*prov))
Example #48
0
    def check_scl_spec(self, pkg, spec):
        '''SCL ready spec checks'''

        # For the entire spec
        if not pkg_name.search(spec):
            printWarning(pkg, 'missing-pkg_name-definition')
        if scl_prefix_noncond.search(self.remove_scl_conds(spec)):
            printWarning(pkg, 'scl-prefix-without-condition')
        if not scl_prefix.search(self.get_name(spec)):
            printError(pkg, 'name-without-scl-prefix')
        for item in self.get_obsoletes_and_conflicts(spec):
            if not scl_prefix.search(item):
                printError(pkg, 'obsoletes-or-conflicts-without-scl-prefix')
                break
        for item in self.get_provides(spec):
            if not scl_prefix.search(item):
                printError(pkg, 'provides-without-scl-prefix')
                break
        setup_opts = setup.search(spec)
        if setup_opts:
            if '-n' not in setup_opts.groups()[0]:
                printError(pkg, 'scl-setup-without-n')

        # Examine main package and subpackages one by one
        borders = []
        borders.append(0)  # main package starts at the beginning
        while True:
            more = subpackage_any.search(spec[borders[-1]:])
            if not more:
                break
            splits = more.groups()[1].split()
            if len(splits) > 1 and splits[0] == '-n':
                if not scl_prefix_start.search(splits[-1]):
                    printError(pkg, 'subpackage-with-n-without-scl-prefix')
            # current end is counted only from last one
            borders.append(borders[-1] + more.end())
        subpackages = [(borders[i], borders[i + 1])
                       for i in range(len(borders) - 1)]
        for subpackage in subpackages:
            ok = False
            for require in self.get_requires(
                    spec[subpackage[0]:subpackage[1]]):
                # Remove flase entries
                if not require or require == ':':
                    continue
                # If it starts with %{name}, it,s fine
                # If it starts with SCL prefix, it's fine
                # If it is scl-runtime, it's the best
                if name_small.search(require) or \
                   scl_prefix_start.search(require) or \
                   scl_runtime.match(require):
                    ok = True
                    break
            if not ok:
                printError(pkg,
                           'doesnt-require-scl-runtime-or-other-scl-package')
                break
Example #49
0
    def check_binary(self, pkg):
        files = pkg.files()
        exec_files = []
        has_lib = False
        version = None
        binary = False
        binary_in_usr_lib = False
        has_usr_lib_file = False

        multi_pkg = False
        srpm = pkg[rpm.RPMTAG_SOURCERPM]
        if srpm:
            res = srcname_regex.search(srpm)
            if res:
                multi_pkg = (pkg.name != res.group(1))

        for fname, pkgfile in files.items():

            if not stat.S_ISDIR(pkgfile.mode) and usr_lib_regex.search(fname):
                has_usr_lib_file = True
                if not binary_in_usr_lib and \
                        usr_lib_exception_regex.search(fname):
                    # Fake that we have binaries there to avoid
                    # only-non-binary-in-usr-lib false positives
                    binary_in_usr_lib = True

            is_elf = pkgfile.magic.startswith('ELF ')
            is_ar = 'current ar archive' in pkgfile.magic
            is_ocaml_native = 'Objective caml native' in pkgfile.magic
            is_lua_bytecode = 'Lua bytecode' in pkgfile.magic
            is_binary = is_elf or is_ar or is_ocaml_native or is_lua_bytecode

            if not is_binary:
                if reference_regex.search(fname):
                    lines = pkg.grep(invalid_dir_ref_regex, fname)
                    if lines:
                        printError(pkg, 'invalid-directory-reference', fname,
                                   '(line %s)' % ", ".join(lines))
                continue

            # binary files only from here on

            binary = True

            if has_usr_lib_file and not binary_in_usr_lib and \
                    usr_lib_regex.search(fname):
                binary_in_usr_lib = True

            if pkg.arch == 'noarch':
                printError(
                    pkg,
                    'arch-independent-package-contains-binary-or-object',
                    fname)
                continue

            # arch dependent packages only from here on

            # in /usr/share ?
            if fname.startswith('/usr/share/'):
                printError(pkg, 'arch-dependent-file-in-usr-share', fname)

            # in /etc ?
            if fname.startswith('/etc/'):
                printError(pkg, 'binary-in-etc', fname)

            if pkg.arch == 'sparc' and sparc_regex.search(pkgfile.magic):
                printError(pkg, 'non-sparc32-binary', fname)

            if is_ocaml_native or is_lua_bytecode or fname.endswith('.o') or \
                    fname.endswith('.static'):
                continue

            # stripped ?
            if 'not stripped' in pkgfile.magic:
                printWarning(pkg, 'unstripped-binary-or-object', fname)

            # inspect binary file
            is_shlib = so_regex.search(fname)
            bin_info = BinaryInfo(pkg, pkgfile.path, fname, is_ar, is_shlib)

            if is_shlib:
                has_lib = True

            # shared libs
            if is_shlib and not bin_info.readelf_error:

                # so name in library
                if not bin_info.soname:
                    printWarning(pkg, 'no-soname', fname)
                else:
                    if not validso_regex.search(bin_info.soname):
                        printError(pkg, 'invalid-soname', fname,
                                   bin_info.soname)
                    else:
                        (directory, base) = dir_base(fname)
                        try:
                            symlink = directory + bin_info.soname
                            link = files[symlink].linkto
                            if link not in (fname, base, ''):
                                printError(pkg, 'invalid-ldconfig-symlink',
                                           fname, link)
                        except KeyError:
                            if base.startswith("lib") or \
                               base.startswith("ld-"):
                                printError(pkg, 'no-ldconfig-symlink', fname)

                    res = soversion_regex.search(bin_info.soname)
                    if res:
                        soversion = res.group(1) or res.group(2)
                        if version is None:
                            version = soversion
                        elif version != soversion:
                            version = -1

                if bin_info.non_pic:
                    printError(pkg, 'shlib-with-non-pic-code', fname)

                # It could be useful to check these for others than shared
                # libs only, but that has potential to generate lots of
                # false positives and noise.
                for s in bin_info.undef:
                    printWarning(pkg, 'undefined-non-weak-symbol', fname, s)
                for s in bin_info.unused:
                    printWarning(pkg, 'unused-direct-shlib-dependency',
                                 fname, s)

                # calls exit() or _exit()?
                for ec in bin_info.exit_calls:
                    printWarning(pkg, 'shared-lib-calls-exit', fname, ec)

            for ec in bin_info.forbidden_calls:
                printWarning(pkg, ec, fname,
                             BinaryInfo.forbidden_functions[ec]['f_name'])

            # rpath ?
            if bin_info.rpath:
                for p in bin_info.rpath:
                    if p in system_lib_paths or not usr_lib_regex.search(p):
                        printError(pkg, 'binary-or-shlib-defines-rpath',
                                   fname, bin_info.rpath)
                        break

            is_exec = 'executable' in pkgfile.magic
            is_shobj = 'shared object' in pkgfile.magic

            if not is_exec and not is_shobj:
                continue

            if is_shobj and not is_exec and '.so' not in fname and \
                    bin_regex.search(fname):
                # pkgfile.magic does not contain "executable" for PIEs
                is_exec = True

            if is_exec:

                if bin_regex.search(fname):
                    exec_files.append(fname)

                if ocaml_mixed_regex.search(bin_info.tail):
                    printWarning(pkg, 'ocaml-mixed-executable', fname)

                if not is_shobj and pie_exec_re and pie_exec_re.search(fname):
                    printError(pkg, 'non-position-independent-executable',
                               fname)

            if bin_info.readelf_error:
                continue

            if not bin_info.needed and not (
                    bin_info.soname and
                    ldso_soname_regex.search(bin_info.soname)):
                if is_shobj:
                    printError(pkg,
                               'shared-lib-without-dependency-information',
                               fname)
                else:
                    printError(pkg, 'statically-linked-binary', fname)

            else:
                # linked against libc ?
                if "libc." not in fname and \
                   (not bin_info.soname or
                    ("libc." not in bin_info.soname and
                     not ldso_soname_regex.search(bin_info.soname))):

                    found_libc = False
                    for lib in bin_info.needed:
                        if "libc." in lib:
                            found_libc = True
                            break

                    if not found_libc:
                        if is_shobj:
                            printError(pkg, 'library-not-linked-against-libc',
                                       fname)
                        else:
                            printError(pkg, 'program-not-linked-against-libc',
                                       fname)

            if bin_info.stack:
                if bin_info.exec_stack:
                    printWarning(pkg, 'executable-stack', fname)
            elif not bin_info.readelf_error and (
                    pkg.arch.endswith("86") or
                    pkg.arch.startswith("pentium") or
                    pkg.arch in ("athlon", "x86_64")):
                printError(pkg, 'missing-PT_GNU_STACK-section', fname)

            if bin_info.setgid and bin_info.setuid and not bin_info.setgroups:
                printError(pkg, 'missing-call-to-setgroups-before-setuid',
                           fname)

            if bin_info.chroot and not bin_info.chroot_near_chdir:
                printError(pkg, 'missing-call-to-chdir-with-chroot', fname)

            if bin_info.mktemp:
                printError(pkg, 'call-to-mktemp', fname)

        if has_lib:
            for f in exec_files:
                printError(pkg, 'executable-in-library-package', f)
            for f in files:
                res = numeric_dir_regex.search(f)
                fn = res and res.group(1) or f
                if f not in exec_files and not so_regex.search(f) and \
                        not versioned_dir_regex.search(fn):
                    printError(pkg, 'non-versioned-file-in-library-package', f)
            if version and version != -1 and version not in pkg.name:
                printError(pkg, 'incoherent-version-in-name', version)

        if not binary and not multi_pkg and pkg.arch != 'noarch':
            printError(pkg, 'no-binary')

        if has_usr_lib_file and not binary_in_usr_lib:
            printWarning(pkg, 'only-non-binary-in-usr-lib')
Example #50
0
    def check(self, pkg):
        """Checks the given RPM pkg instance against the configured whitelist
        restriction.

        Each whitelist violation will be printed with the according error tag.
        Nothing is returned from this function.
        """

        from Filter import printError

        if pkg.isSource():
            return

        files = pkg.files()
        already_tested = set()

        for f in files:
            if not self._isRestrictedPath(f):
                continue

            if f in pkg.ghostFiles():
                printError(pkg, self.m_error_map['ghost'], f)
                continue

            wl_match = self._getWhitelist(pkg.name, f)

            if not wl_match:
                # no whitelist entry exists for this file
                printError(pkg, self.m_error_map['unauthorized'], f)
                continue

            # avoid testing the same paths multiple times thereby avoiding
            # duplicate error messages or unnecessary re-checks of the same
            # files.
            # this is necessary since whitelisting entries can consist of
            # groups of files that are all checked in one go below.
            if f in already_tested:
                continue

            # for the case that there's no match of digests, remember the most
            # recent digest verification result for diagnosis output towards
            # the user
            diag_results = None

            # check the newest (bottom) entry first it is more likely to match
            # what we have
            for audit in reversed(wl_match.audits()):
                digest_matches, results = audit.compareDigests(pkg)

                if digest_matches:
                    for r in results:
                        already_tested.add(r.path())
                    break

                if not diag_results:
                    diag_results = results
            else:
                for r in diag_results:
                    already_tested.add(r.path())
                # none of the digest entries matched
                self._printVerificationResults(diag_results)
                printError(pkg, self.m_error_map['changed'], f)
                continue
Example #51
0
    def check_spec(self, pkg, spec_file):
        self._spec_file = spec_file
        spec_only = isinstance(pkg, Pkg.FakePkg)
        patches = {}
        applied_patches = []
        applied_patches_ifarch = []
        patches_auto_applied = False
        source_dir = False
        buildroot = False
        configure_linenum = None
        configure_cmdline = ""
        mklibname = False
        is_lib_pkg = False
        if_depth = 0
        ifarch_depth = -1
        current_section = 'package'
        buildroot_clean = {'clean': False, 'install': False}
        depscript_override = False
        depgen_disabled = False
        patch_fuzz_override = False
        indent_spaces = 0
        indent_tabs = 0
        section = {}
        # None == main package
        current_package = None
        package_noarch = {}

        is_utf8 = False
        if self._spec_file and use_utf8:
            if Pkg.is_utf8(self._spec_file):
                is_utf8 = True
            else:
                printError(pkg, "non-utf8-spec-file",
                           self._spec_name or self._spec_file)

        # gather info from spec lines

        pkg.current_linenum = 0

        nbsp = UNICODE_NBSP if is_utf8 else chr(0xA0)
        do_unicode = is_utf8 and sys.version_info[0] <= 2

        for line in Pkg.readlines(spec_file):

            pkg.current_linenum += 1

            if do_unicode:
                line = unicode(line, "utf-8", "replace")  # noqa false positive

            char = line.find(nbsp)
            if char != -1:
                printWarning(pkg, "non-break-space", "line %s, char %d" %
                             (pkg.current_linenum, char))

            section_marker = False
            for sec, regex in section_regexs.items():
                res = regex.search(line)
                if res:
                    current_section = sec
                    section_marker = True
                    section[sec] = section.get(sec, 0) + 1
                    if sec in ('package', 'files'):
                        rest = filelist_regex.sub('', line[res.end() - 1:])
                        res = pkgname_regex.search(rest)
                        if res:
                            current_package = res.group(1)
                        else:
                            current_package = None
                    break

            if section_marker:

                if not is_lib_pkg and lib_package_regex.search(line):
                    is_lib_pkg = True

                continue

            if current_section in ('prep', 'build') and \
                    contains_buildroot(line):
                printWarning(pkg, 'rpm-buildroot-usage', '%' + current_section,
                             line[:-1].strip())

            if make_check_regex.search(line) and current_section not in \
                    ('check', 'changelog', 'package', 'description'):
                printWarning(pkg, 'make-check-outside-check-section',
                             line[:-1])

            if current_section in buildroot_clean and \
                    not buildroot_clean[current_section] and \
                    contains_buildroot(line) and rm_regex.search(line):
                buildroot_clean[current_section] = True

            if ifarch_regex.search(line):
                if_depth = if_depth + 1
                ifarch_depth = if_depth

            if if_regex.search(line):
                if_depth = if_depth + 1

            if setup_regex.match(line):
                if not setup_q_regex.search(line):
                    # Don't warn if there's a -T without -a or -b
                    if setup_t_regex.search(line):
                        if setup_ab_regex.search(line):
                            printWarning(pkg, 'setup-not-quiet')
                    else:
                        printWarning(pkg, 'setup-not-quiet')
                if current_section != 'prep':
                    printWarning(pkg, 'setup-not-in-prep')
            elif autopatch_regex.search(line):
                patches_auto_applied = True
                if current_section != 'prep':
                    printWarning(pkg, '%autopatch-not-in-prep')
            else:
                res = autosetup_regex.search(line)
                if res:
                    if not autosetup_n_regex.search(res.group(1)):
                        patches_auto_applied = True
                    if current_section != 'prep':
                        printWarning(pkg, '%autosetup-not-in-prep')

            if endif_regex.search(line):
                if ifarch_depth == if_depth:
                    ifarch_depth = -1
                if_depth = if_depth - 1

            res = applied_patch_regex.search(line)
            if res:
                pnum = res.group(1) or 0
                for tmp in applied_patch_p_regex.findall(line) or [pnum]:
                    pnum = int(tmp)
                    applied_patches.append(pnum)
                    if ifarch_depth > 0:
                        applied_patches_ifarch.append(pnum)
            else:
                res = applied_patch_pipe_regex.search(line)
                if res:
                    pnum = int(res.group(1))
                    applied_patches.append(pnum)
                    if ifarch_depth > 0:
                        applied_patches_ifarch.append(pnum)
            if not res and not source_dir:
                res = source_dir_regex.search(line)
                if res:
                    source_dir = True
                    printError(pkg, "use-of-RPM_SOURCE_DIR")

            if configure_linenum:
                if configure_cmdline[-1] == "\\":
                    configure_cmdline = configure_cmdline[:-1] + line.strip()
                else:
                    res = configure_libdir_spec_regex.search(configure_cmdline)
                    if not res:
                        # Hack to get the correct (start of ./configure) line
                        # number displayed:
                        real_linenum = pkg.current_linenum
                        pkg.current_linenum = configure_linenum
                        printWarning(pkg, "configure-without-libdir-spec")
                        pkg.current_linenum = real_linenum
                    elif res.group(1):
                        res = re.match(hardcoded_library_paths, res.group(1))
                        if res:
                            printError(pkg, "hardcoded-library-path",
                                       res.group(1), "in configure options")
                    configure_linenum = None

            hashPos = line.find("#")

            if current_section != 'changelog':
                cfgPos = line.find('./configure')
                if cfgPos != -1 and (hashPos == -1 or hashPos > cfgPos):
                    # store line where it started
                    configure_linenum = pkg.current_linenum
                    configure_cmdline = line.strip()

            res = hardcoded_library_path_regex.search(line)
            if current_section != 'changelog' and res and not \
                    (biarch_package_regex.match(pkg.name) or
                     hardcoded_lib_path_exceptions_regex.search(
                         res.group(1).lstrip())):
                printError(pkg, "hardcoded-library-path", "in",
                           res.group(1).lstrip())

            if '%mklibname' in line:
                mklibname = True

            if current_section == 'package':

                # Would be cleaner to get sources and patches from the
                # specfile parsed in Python (see below), but we want to
                # catch %ifarch'd etc ones as well, and also catch these when
                # the specfile is not parseable.

                res = patch_regex.search(line)
                if res:
                    pnum = int(res.group(1) or 0)
                    patches[pnum] = res.group(2)

                res = obsolete_tags_regex.search(line)
                if res:
                    printWarning(pkg, "obsolete-tag", res.group(1))

                res = buildroot_regex.search(line)
                if res:
                    buildroot = True
                    if res.group(1).startswith('/'):
                        printWarning(pkg, 'hardcoded-path-in-buildroot-tag',
                                     res.group(1))

                res = buildarch_regex.search(line)
                if res:
                    if res.group(1) != "noarch":
                        printError(pkg,
                                   'buildarch-instead-of-exclusivearch-tag',
                                   res.group(1))
                    else:
                        package_noarch[current_package] = True

                res = packager_regex.search(line)
                if res:
                    printWarning(pkg, 'hardcoded-packager-tag', res.group(1))

                res = prefix_regex.search(line)
                if res:
                    if not res.group(1).startswith('%'):
                        printWarning(pkg, 'hardcoded-prefix-tag', res.group(1))

                res = prereq_regex.search(line)
                if res:
                    printError(pkg, 'prereq-use', res.group(2))

                res = buildprereq_regex.search(line)
                if res:
                    printError(pkg, 'buildprereq-use', res.group(1))

                if scriptlet_requires_regex.search(line):
                    printError(pkg, 'broken-syntax-in-scriptlet-requires',
                               line.strip())

                res = requires_regex.search(line)
                if res:
                    reqs = Pkg.parse_deps(res.group(1))
                    for req in unversioned(reqs):
                        if compop_regex.search(req):
                            printWarning(pkg,
                                         'comparison-operator-in-deptoken',
                                         req)

                res = provides_regex.search(line)
                if res:
                    provs = Pkg.parse_deps(res.group(1))
                    for prov in unversioned(provs):
                        if not prov.startswith('/'):
                            printWarning(pkg, 'unversioned-explicit-provides',
                                         prov)
                        if compop_regex.search(prov):
                            printWarning(pkg,
                                         'comparison-operator-in-deptoken',
                                         prov)

                res = obsoletes_regex.search(line)
                if res:
                    obses = Pkg.parse_deps(res.group(1))
                    for obs in unversioned(obses):
                        if not obs.startswith('/'):
                            printWarning(pkg, 'unversioned-explicit-obsoletes',
                                         obs)
                        if compop_regex.search(obs):
                            printWarning(pkg,
                                         'comparison-operator-in-deptoken',
                                         obs)

                res = conflicts_regex.search(line)
                if res:
                    confs = Pkg.parse_deps(res.group(1))
                    for conf in unversioned(confs):
                        if compop_regex.search(conf):
                            printWarning(pkg,
                                         'comparison-operator-in-deptoken',
                                         conf)

            if current_section == 'changelog':
                for match in AbstractCheck.macro_regex.findall(line):
                    res = re.match('%+', match)
                    if len(res.group(0)) % 2:
                        printWarning(pkg, 'macro-in-%changelog', match)
            else:
                if not depscript_override:
                    depscript_override = \
                        depscript_override_regex.search(line) is not None
                if not depgen_disabled:
                    depgen_disabled = \
                        depgen_disable_regex.search(line) is not None
                if not patch_fuzz_override:
                    patch_fuzz_override = \
                        patch_fuzz_override_regex.search(line) is not None

            if current_section == 'files':
                # TODO: check scriptlets for these too?
                if package_noarch.get(current_package) or \
                        (current_package not in package_noarch and
                         package_noarch.get(None)):
                    res = libdir_regex.search(line)
                    if res:
                        pkgname = current_package
                        if pkgname is None:
                            pkgname = '(main package)'
                        printWarning(pkg, 'libdir-macro-in-noarch-package',
                                     pkgname, line.rstrip())

            if not indent_tabs and '\t' in line:
                indent_tabs = pkg.current_linenum
            if not indent_spaces and indent_spaces_regex.search(line):
                indent_spaces = pkg.current_linenum

            # Check if egrep or fgrep is used
            if current_section not in \
                    ('package', 'changelog', 'description', 'files'):
                greps = deprecated_grep_regex.findall(line)
                if greps:
                    printWarning(pkg, "deprecated-grep", greps)

            # If not checking spec file only, we're checking one inside a
            # SRPM -> skip this check to avoid duplicate warnings (#167)
            if spec_only and VALID_GROUPS and \
               line.lower().startswith("group:"):
                group = line[6:].strip()
                if group not in VALID_GROUPS:
                    printWarning(pkg, 'non-standard-group', group)

            # Test if there are macros in comments
            if hashPos != -1 and \
                    (hashPos == 0 or line[hashPos - 1] in (" ", "\t")):
                for match in AbstractCheck.macro_regex.findall(
                        line[hashPos + 1:]):
                    res = re.match('%+', match)
                    if len(res.group(0)) % 2:
                        printWarning(pkg, 'macro-in-comment', match)

        # Last line read is not useful after this point
        pkg.current_linenum = None

        for sect in (x for x in buildroot_clean if not buildroot_clean[x]):
            printWarning(pkg, 'no-cleaning-of-buildroot', '%' + sect)

        if not buildroot:
            printWarning(pkg, 'no-buildroot-tag')

        for sec in ('prep', 'build', 'install', 'clean'):
            if not section.get(sec):
                printWarning(pkg, 'no-%%%s-section' % sec)
        for sec in ('changelog',):
            # prep, build, install, clean, check prevented by rpmbuild 4.4
            if section.get(sec, 0) > 1:
                printWarning(pkg, 'more-than-one-%%%s-section' % sec)

        if is_lib_pkg and not mklibname:
            printError(pkg, 'lib-package-without-%mklibname')

        if depscript_override and not depgen_disabled:
            printWarning(pkg, 'depscript-without-disabling-depgen')

        if patch_fuzz_override:
            printWarning(pkg, 'patch-fuzz-is-changed')

        if indent_spaces and indent_tabs:
            pkg.current_linenum = max(indent_spaces, indent_tabs)
            printWarning(pkg, 'mixed-use-of-spaces-and-tabs',
                         '(spaces: line %d, tab: line %d)' %
                         (indent_spaces, indent_tabs))
            pkg.current_linenum = None

        # process gathered info
        if not patches_auto_applied:
            for pnum, pfile in patches.items():
                if pnum in applied_patches_ifarch:
                    printWarning(pkg, "%ifarch-applied-patch",
                                 "Patch%d:" % pnum, pfile)
                if pnum not in applied_patches:
                    printWarning(pkg, "patch-not-applied",
                                 "Patch%d:" % pnum, pfile)

        # Rest of the checks require a real spec file
        if not self._spec_file:
            return

        # We'd like to parse the specfile only once using python bindings,
        # but it seems errors from rpmlib get logged to stderr and we can't
        # capture and print them nicely, so we do it once each way :P

        out = Pkg.getstatusoutput(('env', 'LC_ALL=C', 'rpm', '-q',
                                   '--qf=', '--specfile', self._spec_file))
        parse_error = False
        for line in out[1].splitlines():
            # No such file or dir hack: https://bugzilla.redhat.com/487855
            if 'No such file or directory' not in line:
                parse_error = True
                printError(pkg, 'specfile-error', line)

        if not parse_error:
            # grab sources and patches from parsed spec object to get
            # them with macros expanded for URL checking

            spec_obj = None
            try:
                ts = rpm.TransactionSet()
                spec_obj = ts.parseSpec(self._spec_file)
            except:
                # errors logged above already
                pass
            if spec_obj:
                try:
                    # rpm < 4.8.0
                    sources = spec_obj.sources()
                except TypeError:
                    # rpm >= 4.8.0
                    sources = spec_obj.sources
                for src in sources:
                    (url, num, flags) = src
                    (scheme, netloc) = urlparse(url)[0:2]
                    if flags & 1:  # rpmspec.h, rpm.org ticket #123
                        srctype = "Source"
                    else:
                        srctype = "Patch"
                    tag = '%s%s' % (srctype, num)
                    if scheme and netloc:
                        info = self.check_url(pkg, tag, url)
                        if not info or not hasattr(pkg, 'files'):
                            continue
                        clen = info.get("Content-Length")
                        if clen is not None:
                            clen = int(clen)
                        cmd5 = info.get("Content-MD5")
                        if cmd5 is not None:
                            cmd5 = cmd5.lower()
                        if clen is not None or cmd5 is not None:
                            # Not using path from urlparse results to match how
                            # rpm itself parses the basename.
                            pkgfile = pkg.files().get(url.split("/")[-1])
                            if pkgfile:
                                if clen is not None and pkgfile.size != clen:
                                    printWarning(pkg, 'file-size-mismatch',
                                                 '%s = %s, %s = %s' %
                                                 (pkgfile.name, pkgfile.size,
                                                  url, clen))
                                # pkgfile.md5 could be some other digest than
                                # MD5, treat as MD5 only if it's 32 chars long
                                if cmd5 and len(pkgfile.md5) == 32 \
                                        and pkgfile.md5 != cmd5:
                                    printWarning(pkg, 'file-md5-mismatch',
                                                 '%s = %s, %s = %s' %
                                                 (pkgfile.name, pkgfile.md5,
                                                  url, cmd5))
                    elif srctype == "Source" and tarball_regex.search(url):
                        printWarning(pkg, 'invalid-url', '%s:' % tag, url)
    def check(self, pkg):
        global _policy_legacy_exceptions

        if pkg.isSource():
            return

        # Only check unsuffixed lib* packages
        if pkg.name.endswith("-devel") or pkg.name.endswith("-doc"):
            return

        files = pkg.files()

        # Search for shared libraries in this package
        libs = set()
        libs_needed = set()
        libs_to_dir = dict()
        dirs = set()
        reqlibs = set()
        pkg_requires = set(map(lambda x: string.split(x[0], "(")[0], pkg.requires()))

        for f, pkgfile in files.items():
            if f.find(".so.") != -1 or f.endswith(".so"):
                filename = pkg.dirName() + "/" + f
                try:
                    if stat.S_ISREG(files[f].mode) and "ELF" in pkgfile.magic:
                        bi = BinaryInfo(pkg, filename, f, False, True)
                        libs_needed = libs_needed.union(bi.needed)
                        if bi.soname != 0:
                            lib_dir = string.join(f.split("/")[:-1], "/")
                            libs.add(bi.soname)
                            libs_to_dir[bi.soname] = lib_dir
                            dirs.add(lib_dir)
                        if bi.soname in pkg_requires:
                            # But not if the library is used by the pkg itself
                            # This avoids program packages with their own
                            # private lib
                            # FIXME: we'd need to check if somebody else links
                            # to this lib
                            reqlibs.add(bi.soname)
                except Exception:
                    pass
            pass

        std_dirs = dirs.intersection(("/lib", "/lib64", "/usr/lib", "/usr/lib64", "/opt/kde3/lib", "/opt/kde3/lib64"))

        # If this is a program package (all libs it provides are
        # required by itself), bail out
        if not pkg.name.startswith("lib") and len(libs.difference(reqlibs)) == 0:
            return

        std_lib_package = False
        if pkg.name.startswith("lib") and pkg.name[-1].isdigit():
            std_lib_package = True

        # ignore libs in a versioned non_std_dir
        if std_lib_package:
            for lib in libs.copy():
                lib_dir = libs_to_dir[lib]
                if lib_dir.startswith("/opt/kde3"):
                    continue
                for lib_part in lib_dir.split("/"):
                    if len(lib_part) == 0:
                        continue
                    if lib_part[-1].isdigit() and not lib_part.endswith("lib64"):
                        libs.remove(lib)
                        break

        # Check for non-versioned libs in a std lib package
        if std_lib_package:
            for lib in libs.copy():
                if not lib[-1].isdigit():
                    printWarning(pkg, "shlib-unversioned-lib", lib)
                    libs.remove(lib)

        # If this package should be or should be splitted into shlib
        # package(s)
        if len(libs) > 0 and len(std_dirs) > 0:
            # If the package contains a single shlib, name after soname
            if len(libs) == 1:
                soname = libs.copy().pop()
                libname = libname_from_soname(soname)
                if libname.startswith("lib") and pkg.name != libname and pkg.name != libname + "-mini":
                    if libname in _policy_legacy_exceptions:
                        printWarning(pkg, "shlib-legacy-policy-name-error", libname)
                    else:
                        printError(pkg, "shlib-policy-name-error", libname)

            elif not pkg.name[-1:].isdigit():
                printError(pkg, "shlib-policy-missing-suffix")

        if (not pkg.name.startswith("lib")) or pkg.name.endswith("-lang"):
            return

        if not libs:
            if pkg.name in _policy_legacy_exceptions:
                printWarning(pkg, "shlib-legacy-policy-missing-lib", pkg.name)
            else:
                printError(pkg, "shlib-policy-missing-lib")

        # Verify no non-lib stuff is in the package
        dirs = set()
        for f in files:
            if os.path.isdir(pkg.dirName() + f):
                dirs.add(f)

        # Verify shared lib policy package doesn't have hard dependency on non-lib packages
        if std_lib_package:
            for dep in pkg.requires():
                if dep[0].startswith("rpmlib(") or dep[0].startswith("config("):
                    continue
                if (dep[1] & (rpm.RPMSENSE_GREATER | rpm.RPMSENSE_EQUAL)) == rpm.RPMSENSE_EQUAL:
                    printWarning(pkg, "shlib-fixed-dependency", Pkg.formatRequire(dep[0], dep[1], dep[2]))

        # Verify non-lib stuff does not add dependencies
        if libs:
            for dep in pkg_requires.difference(_essential_dependencies):
                if dep.find(".so.") != -1 and not dep in libs and not dep in libs_needed:
                    printError(pkg, "shlib-policy-excessive-dependency", dep)

        # Check for non-versioned directories beyond sysdirs in package
        sysdirs = ["/lib", "/lib64", "/usr/lib", "/usr/lib64", "/usr/share/doc/packages", "/usr/share"]
        cdirs = set()
        for sysdir in sysdirs:
            done = set()
            for dir in dirs:
                if dir.startswith(sysdir + "/"):
                    ssdir = string.split(dir[len(sysdir) + 1 :], "/")[0]
                    if not ssdir[-1].isdigit():
                        cdirs.add(sysdir + "/" + ssdir)
                    done.add(dir)
            dirs = dirs.difference(done)
        map(lambda dir: printError(pkg, "shlib-policy-nonversioned-dir", dir), cdirs)
Example #53
0
    def check_spec(self, pkg, spec_file):
        self._spec_file = spec_file
        spec_only = isinstance(pkg, Pkg.FakePkg)
        patches = {}
        applied_patches = []
        applied_patches_ifarch = []
        patches_auto_applied = False
        source_dir = False
        buildroot = False
        configure_linenum = None
        configure_cmdline = ""
        mklibname = False
        is_lib_pkg = False
        if_depth = 0
        ifarch_depth = -1
        current_section = 'package'
        buildroot_clean = {'clean': False, 'install': False}
        depscript_override = False
        depgen_disabled = False
        patch_fuzz_override = False
        indent_spaces = 0
        indent_tabs = 0
        files_has_defattr = False
        section = {}
        # None == main package
        current_package = None
        package_noarch = {}

        is_utf8 = False
        if self._spec_file and use_utf8:
            if Pkg.is_utf8(self._spec_file):
                is_utf8 = True
            else:
                printError(pkg, "non-utf8-spec-file", self._spec_file)

        # gather info from spec lines

        pkg.current_linenum = 0

        nbsp = chr(0xA0)
        if is_utf8:
            nbsp = UNICODE_NBSP
        do_unicode = is_utf8 and sys.version_info[0] <= 2

        for line in Pkg.readlines(spec_file):

            pkg.current_linenum += 1

            if do_unicode:
                line = unicode(line, "utf-8", "replace")  # noqa false positive

            char = line.find(nbsp)
            if char != -1:
                printWarning(pkg, "non-break-space", "line %s, char %d" %
                             (pkg.current_linenum, char))

            section_marker = False
            for sec, regex in section_regexs.items():
                res = regex.search(line)
                if res:
                    current_section = sec
                    section_marker = True
                    section[sec] = section.get(sec, 0) + 1
                    if sec in ('package', 'files'):
                        rest = filelist_regex.sub('', line[res.end() - 1:])
                        res = pkgname_regex.search(rest)
                        if res:
                            current_package = res.group(1)
                        else:
                            current_package = None
                    break

            if section_marker:

                if current_section == 'files':
                    files_has_defattr = False

                if not is_lib_pkg and lib_package_regex.search(line):
                    is_lib_pkg = True

                continue

            if current_section in ('prep', 'build') and \
                    contains_buildroot(line):
                printWarning(pkg, 'rpm-buildroot-usage', '%' + current_section,
                             line[:-1].strip())

            if make_check_regex.search(line) and current_section not in \
                    ('check', 'changelog', 'package', 'description'):
                printWarning(pkg, 'make-check-outside-check-section',
                             line[:-1])

            if current_section in buildroot_clean and \
                    not buildroot_clean[current_section] and \
                    contains_buildroot(line) and rm_regex.search(line):
                buildroot_clean[current_section] = True

            if ifarch_regex.search(line):
                if_depth = if_depth + 1
                ifarch_depth = if_depth

            if if_regex.search(line):
                if_depth = if_depth + 1

            if setup_regex.match(line):
                if not setup_q_regex.search(line):
                    # Don't warn if there's a -T without -a or -b
                    if setup_t_regex.search(line):
                        if setup_ab_regex.search(line):
                            printWarning(pkg, 'setup-not-quiet')
                    else:
                        printWarning(pkg, 'setup-not-quiet')
                if current_section != 'prep':
                    printWarning(pkg, 'setup-not-in-prep')
            elif autopatch_regex.search(line):
                patches_auto_applied = True
                if current_section != 'prep':
                    printWarning(pkg, '%autopatch-not-in-prep')
            else:
                res = autosetup_regex.search(line)
                if res:
                    if not autosetup_n_regex.search(res.group(1)):
                        patches_auto_applied = True
                    if current_section != 'prep':
                        printWarning(pkg, '%autosetup-not-in-prep')

            if endif_regex.search(line):
                if ifarch_depth == if_depth:
                    ifarch_depth = -1
                if_depth = if_depth - 1

            res = applied_patch_regex.search(line)
            if res:
                pnum = res.group(1) or 0
                for tmp in applied_patch_p_regex.findall(line) or [pnum]:
                    pnum = int(tmp)
                    applied_patches.append(pnum)
                    if ifarch_depth > 0:
                        applied_patches_ifarch.append(pnum)
            else:
                res = applied_patch_pipe_regex.search(line)
                if res:
                    pnum = int(res.group(1))
                    applied_patches.append(pnum)
                    if ifarch_depth > 0:
                        applied_patches_ifarch.append(pnum)
            if not res and not source_dir:
                res = source_dir_regex.search(line)
                if res:
                    source_dir = True
                    printError(pkg, "use-of-RPM_SOURCE_DIR")

            if configure_linenum:
                if configure_cmdline[-1] == "\\":
                    configure_cmdline = configure_cmdline[:-1] + line.strip()
                else:
                    res = configure_libdir_spec_regex.search(configure_cmdline)
                    if not res:
                        # Hack to get the correct (start of ./configure) line
                        # number displayed:
                        real_linenum = pkg.current_linenum
                        pkg.current_linenum = configure_linenum
                        printWarning(pkg, "configure-without-libdir-spec")
                        pkg.current_linenum = real_linenum
                    elif res.group(1):
                        res = re.match(hardcoded_library_paths, res.group(1))
                        if res:
                            printError(pkg, "hardcoded-library-path",
                                       res.group(1), "in configure options")
                    configure_linenum = None

            hashPos = line.find("#")

            if current_section != 'changelog':
                cfgPos = line.find('./configure')
                if cfgPos != -1 and (hashPos == -1 or hashPos > cfgPos):
                    # store line where it started
                    configure_linenum = pkg.current_linenum
                    configure_cmdline = line.strip()

            res = hardcoded_library_path_regex.search(line)
            if current_section != 'changelog' and res and not \
                    (biarch_package_regex.match(pkg.name) or
                     hardcoded_lib_path_exceptions_regex.search(
                         res.group(1).lstrip())):
                printError(pkg, "hardcoded-library-path", "in",
                           res.group(1).lstrip())

            if '%mklibname' in line:
                mklibname = True

            if current_section == 'package':

                # Would be cleaner to get sources and patches from the
                # specfile parsed in Python (see below), but we want to
                # catch %ifarch'd etc ones as well, and also catch these when
                # the specfile is not parseable.

                res = patch_regex.search(line)
                if res:
                    pnum = int(res.group(1) or 0)
                    patches[pnum] = res.group(2)

                res = obsolete_tags_regex.search(line)
                if res:
                    printWarning(pkg, "obsolete-tag", res.group(1))

                res = buildroot_regex.search(line)
                if res:
                    buildroot = True
                    if res.group(1).startswith('/'):
                        printWarning(pkg, 'hardcoded-path-in-buildroot-tag',
                                     res.group(1))

                res = buildarch_regex.search(line)
                if res:
                    if res.group(1) != "noarch":
                        printError(pkg,
                                   'buildarch-instead-of-exclusivearch-tag',
                                   res.group(1))
                    else:
                        package_noarch[current_package] = True

                res = packager_regex.search(line)
                if res:
                    printWarning(pkg, 'hardcoded-packager-tag', res.group(1))

                res = prefix_regex.search(line)
                if res:
                    if not res.group(1).startswith('%'):
                        printWarning(pkg, 'hardcoded-prefix-tag', res.group(1))

                res = prereq_regex.search(line)
                if res:
                    printError(pkg, 'prereq-use', res.group(2))

                res = buildprereq_regex.search(line)
                if res:
                    printError(pkg, 'buildprereq-use', res.group(1))

                if scriptlet_requires_regex.search(line):
                    printError(pkg, 'broken-syntax-in-scriptlet-requires',
                               line.strip())

                res = requires_regex.search(line)
                if res:
                    reqs = Pkg.parse_deps(res.group(1))
                    for req in unversioned(reqs):
                        if compop_regex.search(req):
                            printWarning(pkg,
                                         'comparison-operator-in-deptoken',
                                         req)

                res = provides_regex.search(line)
                if res:
                    provs = Pkg.parse_deps(res.group(1))
                    for prov in unversioned(provs):
                        printWarning(pkg, 'unversioned-explicit-provides',
                                     prov)
                        if compop_regex.search(prov):
                            printWarning(pkg,
                                         'comparison-operator-in-deptoken',
                                         prov)

                res = obsoletes_regex.search(line)
                if res:
                    obses = Pkg.parse_deps(res.group(1))
                    for obs in unversioned(obses):
                        printWarning(pkg, 'unversioned-explicit-obsoletes',
                                     obs)
                        if compop_regex.search(obs):
                            printWarning(pkg,
                                         'comparison-operator-in-deptoken',
                                         obs)

                res = conflicts_regex.search(line)
                if res:
                    confs = Pkg.parse_deps(res.group(1))
                    for conf in unversioned(confs):
                        if compop_regex.search(conf):
                            printWarning(pkg,
                                         'comparison-operator-in-deptoken',
                                         conf)

            if current_section == 'changelog':
                for match in AbstractCheck.macro_regex.findall(line):
                    res = re.match('%+', match)
                    if len(res.group(0)) % 2:
                        printWarning(pkg, 'macro-in-%changelog', match)
            else:
                if not depscript_override:
                    depscript_override = \
                        depscript_override_regex.search(line) is not None
                if not depgen_disabled:
                    depgen_disabled = \
                        depgen_disable_regex.search(line) is not None
                if not patch_fuzz_override:
                    patch_fuzz_override = \
                        patch_fuzz_override_regex.search(line) is not None

            if current_section == 'files':

                if not (comment_or_empty_regex.search(line) or
                        ifarch_regex.search(line) or if_regex.search(line) or
                        endif_regex.search(line)):
                    if defattr_regex.search(line):
                        files_has_defattr = True
                    elif not (files_has_defattr or attr_regex.search(line)):
                        printWarning(pkg, 'files-attr-not-set')

                # TODO: check scriptlets for these too?
                if package_noarch.get(current_package) or \
                        (current_package not in package_noarch and
                         package_noarch.get(None)):
                    res = libdir_regex.search(line)
                    if res:
                        pkgname = current_package
                        if pkgname is None:
                            pkgname = '(main package)'
                        printWarning(pkg, 'libdir-macro-in-noarch-package',
                                     pkgname, line.rstrip())

            if not indent_tabs and '\t' in line:
                indent_tabs = pkg.current_linenum
            if not indent_spaces and indent_spaces_regex.search(line):
                indent_spaces = pkg.current_linenum

            # Check if egrep or fgrep is used
            if current_section not in \
                    ('package', 'changelog', 'description', 'files'):
                greps = deprecated_grep_regex.findall(line)
                if greps:
                    printWarning(pkg, "deprecated-grep", greps)

            # If not checking spec file only, we're checking one inside a
            # SRPM -> skip this check to avoid duplicate warnings (#167)
            if spec_only and VALID_GROUPS and \
               line.lower().startswith("group:"):
                group = line[6:].strip()
                if group not in VALID_GROUPS:
                    printWarning(pkg, 'non-standard-group', group)

            # Test if there are macros in comments
            if hashPos != -1 and \
                    (hashPos == 0 or line[hashPos - 1] in (" ", "\t")):
                for match in AbstractCheck.macro_regex.findall(
                        line[hashPos + 1:]):
                    res = re.match('%+', match)
                    if len(res.group(0)) % 2:
                        printWarning(pkg, 'macro-in-comment', match)

        # Last line read is not useful after this point
        pkg.current_linenum = None

        for sect in (x for x in buildroot_clean if not buildroot_clean[x]):
            printWarning(pkg, 'no-cleaning-of-buildroot', '%' + sect)

        if not buildroot:
            printWarning(pkg, 'no-buildroot-tag')

        for sec in ('prep', 'build', 'install', 'clean'):
            if not section.get(sec):
                printWarning(pkg, 'no-%%%s-section' % sec)
        for sec in ('changelog',):
            # prep, build, install, clean, check prevented by rpmbuild 4.4
            if section.get(sec, 0) > 1:
                printWarning(pkg, 'more-than-one-%%%s-section' % sec)

        if is_lib_pkg and not mklibname:
            printError(pkg, 'lib-package-without-%mklibname')

        if depscript_override and not depgen_disabled:
            printWarning(pkg, 'depscript-without-disabling-depgen')

        if patch_fuzz_override:
            printWarning(pkg, 'patch-fuzz-is-changed')

        if indent_spaces and indent_tabs:
            pkg.current_linenum = max(indent_spaces, indent_tabs)
            printWarning(pkg, 'mixed-use-of-spaces-and-tabs',
                         '(spaces: line %d, tab: line %d)' %
                         (indent_spaces, indent_tabs))
            pkg.current_linenum = None

        # process gathered info
        if not patches_auto_applied:
            for pnum, pfile in patches.items():
                if pnum in applied_patches_ifarch:
                    printWarning(pkg, "%ifarch-applied-patch",
                                 "Patch%d:" % pnum, pfile)
                if pnum not in applied_patches:
                    printWarning(pkg, "patch-not-applied",
                                 "Patch%d:" % pnum, pfile)

        # Rest of the checks require a real spec file
        if not self._spec_file:
            return

        # We'd like to parse the specfile only once using python bindings,
        # but it seems errors from rpmlib get logged to stderr and we can't
        # capture and print them nicely, so we do it once each way :P

        out = Pkg.getstatusoutput(('env', 'LC_ALL=C', 'rpm', '-q',
                                   '--qf=', '--specfile', self._spec_file))
        parse_error = False
        for line in out[1].splitlines():
            # No such file or dir hack: https://bugzilla.redhat.com/487855
            if 'No such file or directory' not in line:
                parse_error = True
                printError(pkg, 'specfile-error', line)

        if not parse_error:
            # grab sources and patches from parsed spec object to get
            # them with macros expanded for URL checking

            spec_obj = None
            try:
                ts = rpm.TransactionSet()
                spec_obj = ts.parseSpec(self._spec_file)
            except:
                # errors logged above already
                pass
            if spec_obj:
                try:
                    # rpm < 4.8.0
                    sources = spec_obj.sources()
                except TypeError:
                    # rpm >= 4.8.0
                    sources = spec_obj.sources
                for src in sources:
                    (url, num, flags) = src
                    (scheme, netloc) = urlparse(url)[0:2]
                    if flags & 1:  # rpmspec.h, rpm.org ticket #123
                        srctype = "Source"
                    else:
                        srctype = "Patch"
                    tag = '%s%s' % (srctype, num)
                    if scheme and netloc:
                        info = self.check_url(pkg, tag, url)
                        if not info or not hasattr(pkg, 'files'):
                            continue
                        clen = info.get("Content-Length")
                        if clen is not None:
                            clen = int(clen)
                        cmd5 = info.get("Content-MD5")
                        if cmd5 is not None:
                            cmd5 = cmd5.lower()
                        if clen is not None or cmd5 is not None:
                            # Not using path from urlparse results to match how
                            # rpm itself parses the basename.
                            pkgfile = pkg.files().get(url.split("/")[-1])
                            if pkgfile:
                                if clen is not None and pkgfile.size != clen:
                                    printWarning(pkg, 'file-size-mismatch',
                                                 '%s = %s, %s = %s' %
                                                 (pkgfile.name, pkgfile.size,
                                                  url, clen))
                                # pkgfile.md5 could be some other digest than
                                # MD5, treat as MD5 only if it's 32 chars long
                                if cmd5 and len(pkgfile.md5) == 32 \
                                        and pkgfile.md5 != cmd5:
                                    printWarning(pkg, 'file-md5-mismatch',
                                                 '%s = %s, %s = %s' %
                                                 (pkgfile.name, pkgfile.md5,
                                                  url, cmd5))
                    elif srctype == "Source" and tarball_regex.search(url):
                        printWarning(pkg, 'invalid-url', '%s:' % tag, url)
Example #54
0
    def check_binary(self, pkg):
        initscript_list = []
        for fname, pkgfile in pkg.files().items():

            if not fname.startswith('/etc/init.d/') and \
                    not fname.startswith('/etc/rc.d/init.d/'):
                continue

            basename = os.path.basename(fname)
            initscript_list.append(basename)
            if pkgfile.mode & 0o500 != 0o500:
                printError(pkg, 'init-script-non-executable', fname)

            if "." in basename:
                printError(pkg, 'init-script-name-with-dot', fname)

            # check chkconfig call in %post and %preun
            postin = pkg[rpm.RPMTAG_POSTIN] or \
                pkg.scriptprog(rpm.RPMTAG_POSTINPROG)
            if not postin:
                printError(pkg, 'init-script-without-chkconfig-postin', fname)
            elif not chkconfig_regex.search(postin):
                printError(pkg, 'postin-without-chkconfig', fname)

            preun = pkg[rpm.RPMTAG_PREUN] or \
                pkg.scriptprog(rpm.RPMTAG_PREUNPROG)
            if not preun:
                printError(pkg, 'init-script-without-chkconfig-preun', fname)
            elif not chkconfig_regex.search(preun):
                printError(pkg, 'preun-without-chkconfig', fname)

            status_found = False
            reload_found = False
            chkconfig_content_found = False
            subsys_regex_found = False
            in_lsb_tag = False
            in_lsb_description = False
            lastline = ''
            lsb_tags = {}
            # check common error in file content
            content = None
            try:
                content = [x for x in Pkg.readlines(pkgfile.path)]
            except Exception as e:
                printWarning(pkg, 'read-error', e)
                continue
            content_str = "".join(content)
            for line in content:
                line = line[:-1]  # chomp
                # TODO check if there is only one line like this
                if line.startswith('### BEGIN INIT INFO'):
                    in_lsb_tag = True
                    continue
                if line.endswith('### END INIT INFO'):
                    in_lsb_tag = False
                    for kw, vals in lsb_tags.items():
                        if len(vals) != 1:
                            printError(pkg, 'redundant-lsb-keyword', kw)

                    for kw in RECOMMENDED_LSB_KEYWORDS:
                        if kw not in lsb_tags:
                            printWarning(pkg, 'missing-lsb-keyword',
                                         "%s in %s" % (kw, fname))
                if in_lsb_tag:
                    # TODO maybe we do not have to handle this ?
                    if lastline.endswith('\\'):
                        line = lastline + line
                    else:
                        res = lsb_tags_regex.search(line)
                        if not res:
                            cres = lsb_cont_regex.search(line)
                            if not (in_lsb_description and cres):
                                in_lsb_description = False
                                printError(
                                    pkg, 'malformed-line-in-lsb-comment-block',
                                    line)
                            else:
                                lsb_tags["Description"][-1] += \
                                    " " + cres.group(1)
                        else:
                            tag = res.group(1)
                            if not tag.startswith('X-') and \
                                    tag not in LSB_KEYWORDS:
                                printError(pkg, 'unknown-lsb-keyword', line)
                            else:
                                in_lsb_description = (tag == 'Description')
                                if tag not in lsb_tags:
                                    lsb_tags[tag] = []
                                lsb_tags[tag].append(res.group(2))
                    lastline = line

                if not status_found and status_regex.search(line):
                    status_found = True

                if not reload_found and reload_regex.search(line):
                    reload_found = True

                res = chkconfig_content_regex.search(line)
                if res:
                    chkconfig_content_found = True
                    if use_deflevels:
                        if res.group(1) == '-':
                            printWarning(pkg, 'no-default-runlevel', fname)
                    elif res.group(1) != '-':
                        printWarning(pkg, 'service-default-enabled', fname)

                res = subsys_regex.search(line)
                if res:
                    subsys_regex_found = True
                    name = res.group(1)
                    if use_subsys and name != basename:
                        error = True
                        if name[0] == '$':
                            value = Pkg.substitute_shell_vars(
                                name, content_str)
                            if value == basename:
                                error = False
                        else:
                            i = name.find('}')
                            if i != -1:
                                name = name[0:i]
                                error = name != basename
                        if error and len(name):
                            if name[0] == '$':
                                printWarning(pkg, 'incoherent-subsys', fname,
                                             name)
                            else:
                                printError(pkg, 'incoherent-subsys', fname,
                                           name)

            if "Default-Start" in lsb_tags:
                if "".join(lsb_tags["Default-Start"]):
                    printWarning(pkg, 'service-default-enabled', fname)

            if not status_found:
                printError(pkg, 'no-status-entry', fname)
            if not reload_found:
                printWarning(pkg, 'no-reload-entry', fname)
            if not chkconfig_content_found:
                printError(pkg, 'no-chkconfig-line', fname)
            if not subsys_regex_found and use_subsys:
                printError(pkg, 'subsys-not-used', fname)
            elif subsys_regex_found and not use_subsys:
                printError(pkg, 'subsys-unsupported', fname)

        if len(initscript_list) == 1:
            pkgname = re.sub("-sysvinit$", "", pkg.name.lower())
            goodnames = (pkgname, pkgname + 'd')
            if initscript_list[0] not in goodnames:
                printWarning(pkg, 'incoherent-init-script-name',
                             initscript_list[0], str(goodnames))
    def check_binary(self, pkg):
        files = pkg.files()
        exec_files = []
        has_lib = False
        version = None
        binary = False
        binary_in_usr_lib = False
        has_usr_lib_file = False

        multi_pkg = False
        srpm = pkg[rpm.RPMTAG_SOURCERPM]
        if srpm:
            res = srcname_regex.search(Pkg.b2s(srpm))
            if res:
                multi_pkg = (pkg.name != res.group(1))

        for fname, pkgfile in files.items():

            if not stat.S_ISDIR(pkgfile.mode) and usr_lib_regex.search(fname):
                has_usr_lib_file = True
                if not binary_in_usr_lib and \
                        usr_lib_exception_regex.search(fname):
                    # Fake that we have binaries there to avoid
                    # only-non-binary-in-usr-lib false positives
                    binary_in_usr_lib = True

            is_elf = 'ELF' in pkgfile.magic
            is_ar = 'current ar archive' in pkgfile.magic
            is_ocaml_native = 'Objective caml native' in pkgfile.magic
            is_lua_bytecode = 'Lua bytecode' in pkgfile.magic
            is_binary = is_elf or is_ar or is_ocaml_native or is_lua_bytecode

            if not is_binary:
                if reference_regex.search(fname):
                    lines = pkg.grep(invalid_dir_ref_regex, fname)
                    if lines:
                        printError(pkg, 'invalid-directory-reference', fname,
                                   '(line %s)' % ", ".join(lines))
                continue

            # binary files only from here on

            binary = True

            if has_usr_lib_file and not binary_in_usr_lib and \
                    usr_lib_regex.search(fname):
                binary_in_usr_lib = True

            if pkg.arch == 'noarch':
                printError(
                    pkg, 'arch-independent-package-contains-binary-or-object',
                    fname)
                continue

            # arch dependent packages only from here on

            # in /usr/share ?
            if fname.startswith('/usr/share/'):
                printError(pkg, 'arch-dependent-file-in-usr-share', fname)

            # in /etc ?
            if fname.startswith('/etc/'):
                printError(pkg, 'binary-in-etc', fname)

            if pkg.arch == 'sparc' and sparc_regex.search(pkgfile.magic):
                printError(pkg, 'non-sparc32-binary', fname)

            if is_ocaml_native or is_lua_bytecode or fname.endswith('.o') or \
                    fname.endswith('.static'):
                continue

            # stripped ?
            if 'not stripped' in pkgfile.magic:
                printWarning(pkg, 'unstripped-binary-or-object', fname)

            # inspect binary file
            is_shlib = so_regex.search(fname)
            bin_info = BinaryInfo(pkg, pkgfile.path, fname, is_ar, is_shlib)

            if is_shlib:
                has_lib = True

            # shared libs
            if is_shlib and not bin_info.readelf_error:

                # so name in library
                if not bin_info.soname:
                    printWarning(pkg, 'no-soname', fname)
                else:
                    if not validso_regex.search(bin_info.soname):
                        printError(pkg, 'invalid-soname', fname,
                                   bin_info.soname)
                    else:
                        (directory, base) = dir_base(fname)
                        try:
                            symlink = directory + bin_info.soname
                            link = files[symlink].linkto
                            if link not in (fname, base, ''):
                                printError(pkg, 'invalid-ldconfig-symlink',
                                           fname, link)
                        except KeyError:
                            if base.startswith("lib") or \
                               base.startswith("ld-"):
                                printError(pkg, 'no-ldconfig-symlink', fname)

                    res = soversion_regex.search(bin_info.soname)
                    if res:
                        soversion = res.group(1) or res.group(2)
                        if version is None:
                            version = soversion
                        elif version != soversion:
                            version = -1

                if bin_info.non_pic:
                    printError(pkg, 'shlib-with-non-pic-code', fname)

                # It could be useful to check these for others than shared
                # libs only, but that has potential to generate lots of
                # false positives and noise.
                for s in bin_info.undef:
                    printWarning(pkg, 'undefined-non-weak-symbol', fname, s)
                for s in bin_info.unused:
                    printWarning(pkg, 'unused-direct-shlib-dependency', fname,
                                 s)

                # calls exit() or _exit()?
                for ec in bin_info.exit_calls:
                    printWarning(pkg, 'shared-lib-calls-exit', fname, ec)

            # rpath ?
            if bin_info.rpath:
                for p in bin_info.rpath:
                    if p in system_lib_paths or not usr_lib_regex.search(p):
                        printError(pkg, 'binary-or-shlib-defines-rpath', fname,
                                   bin_info.rpath)
                        break

            is_exec = 'executable' in pkgfile.magic
            is_shobj = 'shared object' in pkgfile.magic

            if not is_exec and not is_shobj:
                continue

            if is_shobj and not is_exec and '.so' not in fname and \
                    bin_regex.search(fname):
                # pkgfile.magic does not contain "executable" for PIEs
                is_exec = True

            if is_exec:

                if bin_regex.search(fname):
                    exec_files.append(fname)

                if ocaml_mixed_regex.search(bin_info.tail):
                    printWarning(pkg, 'ocaml-mixed-executable', fname)

                if not is_shobj and pie_exec_re and pie_exec_re.search(fname):
                    printError(pkg, 'non-position-independent-executable',
                               fname)

            if bin_info.readelf_error:
                continue

            if not bin_info.needed and not (bin_info.soname
                                            and ldso_soname_regex.search(
                                                bin_info.soname)):
                if is_shobj:
                    printError(pkg,
                               'shared-lib-without-dependency-information',
                               fname)
                else:
                    printError(pkg, 'statically-linked-binary', fname)

            else:
                # linked against libc ?
                if "libc." not in fname and \
                   (not bin_info.soname or
                    ("libc." not in bin_info.soname and
                     not ldso_soname_regex.search(bin_info.soname))):

                    found_libc = False
                    for lib in bin_info.needed:
                        if "libc." in lib:
                            found_libc = True
                            break

                    if not found_libc:
                        if is_shobj:
                            printError(pkg, 'library-not-linked-against-libc',
                                       fname)
                        else:
                            printError(pkg, 'program-not-linked-against-libc',
                                       fname)

            if bin_info.stack:
                if bin_info.exec_stack:
                    printWarning(pkg, 'executable-stack', fname)
            elif not bin_info.readelf_error and (
                    pkg.arch.endswith("86") or pkg.arch.startswith("pentium")
                    or pkg.arch in ("athlon", "x86_64")):
                printError(pkg, 'missing-PT_GNU_STACK-section', fname)

            if bin_info.setgid and bin_info.setuid and not bin_info.setgroups:
                printError(pkg, 'missing-call-to-setgroups-before-setuid',
                           fname)

            if bin_info.chroot:
                if not bin_info.chdir or not bin_info.chroot_near_chdir:
                    printError(pkg, 'missing-call-to-chdir-with-chroot', fname)

            if bin_info.mktemp:
                printError(pkg, 'call-to-mktemp', fname)

        if has_lib:
            for f in exec_files:
                printError(pkg, 'executable-in-library-package', f)
            for f in files:
                res = numeric_dir_regex.search(f)
                fn = res and res.group(1) or f
                if f not in exec_files and not so_regex.search(f) and \
                        not versioned_dir_regex.search(fn):
                    printError(pkg, 'non-versioned-file-in-library-package', f)
            if version and version != -1 and version not in pkg.name:
                printError(pkg, 'incoherent-version-in-name', version)

        if not binary and not multi_pkg and pkg.arch != 'noarch':
            printError(pkg, 'no-binary')

        if has_usr_lib_file and not binary_in_usr_lib:
            printWarning(pkg, 'only-non-binary-in-usr-lib')
    def check_binary(self, pkg):
        initscript_list = []
        for fname, pkgfile in pkg.files().items():

            if not fname.startswith('/etc/init.d/') and \
                    not fname.startswith('/etc/rc.d/init.d/'):
                continue

            basename = os.path.basename(fname)
            initscript_list.append(basename)
            if pkgfile.mode & 0o500 != 0o500:
                printError(pkg, 'init-script-non-executable', fname)

            if "." in basename:
                printError(pkg, 'init-script-name-with-dot', fname)

            # check chkconfig call in %post and %preun
            postin = pkg[rpm.RPMTAG_POSTIN] or \
                pkg.scriptprog(rpm.RPMTAG_POSTINPROG)
            if not postin:
                printError(pkg, 'init-script-without-chkconfig-postin', fname)
            elif not chkconfig_regex.search(postin):
                printError(pkg, 'postin-without-chkconfig', fname)

            preun = pkg[rpm.RPMTAG_PREUN] or \
                pkg.scriptprog(rpm.RPMTAG_PREUNPROG)
            if not preun:
                printError(pkg, 'init-script-without-chkconfig-preun', fname)
            elif not chkconfig_regex.search(preun):
                printError(pkg, 'preun-without-chkconfig', fname)

            status_found = False
            reload_found = False
            chkconfig_content_found = False
            subsys_regex_found = False
            in_lsb_tag = False
            in_lsb_description = False
            lastline = ''
            lsb_tags = {}
            # check common error in file content
            content = None
            try:
                content = [x for x in Pkg.readlines(pkgfile.path)]
            except Exception as e:
                printWarning(pkg, 'read-error', e)
                continue
            content_str = "".join(content)
            for line in content:
                line = line[:-1]  # chomp
                # TODO check if there is only one line like this
                if line.startswith('### BEGIN INIT INFO'):
                    in_lsb_tag = True
                    continue
                if line.endswith('### END INIT INFO'):
                    in_lsb_tag = False
                    for kw, vals in lsb_tags.items():
                        if len(vals) != 1:
                            printError(pkg, 'redundant-lsb-keyword', kw)

                    for kw in RECOMMENDED_LSB_KEYWORDS:
                        if kw not in lsb_tags:
                            printWarning(pkg, 'missing-lsb-keyword',
                                         "%s in %s" % (kw, fname))
                if in_lsb_tag:
                    # TODO maybe we do not have to handle this ?
                    if lastline.endswith('\\'):
                        line = lastline + line
                    else:
                        res = lsb_tags_regex.search(line)
                        if not res:
                            cres = lsb_cont_regex.search(line)
                            if not (in_lsb_description and cres):
                                in_lsb_description = False
                                printError(
                                    pkg, 'malformed-line-in-lsb-comment-block',
                                    line)
                            else:
                                lsb_tags["Description"][-1] += \
                                    " " + cres.group(1)
                        else:
                            tag = res.group(1)
                            if not tag.startswith('X-') and \
                                    tag not in LSB_KEYWORDS:
                                printError(pkg, 'unknown-lsb-keyword', line)
                            else:
                                in_lsb_description = (tag == 'Description')
                                if tag not in lsb_tags:
                                    lsb_tags[tag] = []
                                lsb_tags[tag].append(res.group(2))
                    lastline = line

                if not status_found and status_regex.search(line):
                    status_found = True

                if not reload_found and reload_regex.search(line):
                    reload_found = True

                res = chkconfig_content_regex.search(line)
                if res:
                    chkconfig_content_found = True
                    if use_deflevels:
                        if res.group(1) == '-':
                            printWarning(pkg, 'no-default-runlevel', fname)
                    elif res.group(1) != '-':
                        printWarning(pkg, 'service-default-enabled', fname)

                res = subsys_regex.search(line)
                if res:
                    subsys_regex_found = True
                    name = res.group(1)
                    if use_subsys and name != basename:
                        error = True
                        if name[0] == '$':
                            value = Pkg.substitute_shell_vars(name,
                                                              content_str)
                            if value == basename:
                                error = False
                        else:
                            i = name.find('}')
                            if i != -1:
                                name = name[0:i]
                                error = name != basename
                        if error and len(name):
                            if name[0] == '$':
                                printWarning(pkg, 'incoherent-subsys', fname,
                                             name)
                            else:
                                printError(pkg, 'incoherent-subsys', fname,
                                           name)

            if "Default-Start" in lsb_tags:
                if "".join(lsb_tags["Default-Start"]):
                    printWarning(pkg, 'service-default-enabled', fname)

            if not status_found:
                printError(pkg, 'no-status-entry', fname)
            if not reload_found:
                printWarning(pkg, 'no-reload-entry', fname)
            if not chkconfig_content_found:
                printError(pkg, 'no-chkconfig-line', fname)
            if not subsys_regex_found and use_subsys:
                printError(pkg, 'subsys-not-used', fname)
            elif subsys_regex_found and not use_subsys:
                printError(pkg, 'subsys-unsupported', fname)

        if len(initscript_list) == 1:
            pkgname = re.sub("-sysvinit$", "", pkg.name.lower())
            goodnames = (pkgname, pkgname + 'd')
            if initscript_list[0] not in goodnames:
                printWarning(pkg, 'incoherent-init-script-name',
                             initscript_list[0], str(goodnames))
Example #57
0
    def check_binary(self, pkg):
        files = pkg.files()
        menus = []

        for fname, pkgfile in files.items():
            # Check menu files
            res = menu_file_regex.search(fname)
            mode = pkgfile.mode
            if res:
                basename = res.group(1)
                if not stat.S_ISREG(mode):
                    printError(pkg, 'non-file-in-menu-dir', fname)
                else:
                    if basename != pkg.name:
                        printWarning(pkg, 'non-coherent-menu-filename', fname)
                    if mode & 0o444 != 0o444:
                        printError(pkg, 'non-readable-menu-file', fname)
                    if mode & 0o111:
                        printError(pkg, 'executable-menu-file', fname)
                    menus.append(fname)
            else:
                # Check old menus from KDE and GNOME
                res = old_menu_file_regex.search(fname)
                if res:
                    if stat.S_ISREG(mode):
                        printError(pkg, 'old-menu-entry', fname)
                else:
                    # Check non transparent xpm files
                    res = xpm_ext_regex.search(fname)
                    if res:
                        if stat.S_ISREG(mode) and not pkg.grep('None",', fname):
                            printWarning(pkg, 'non-transparent-xpm', fname)
                if fname.startswith('/usr/lib64/menu'):
                    printError(pkg, 'menu-in-wrong-dir', fname)

        if menus:
            postin = pkg[rpm.RPMTAG_POSTIN] or \
                pkg.scriptprog(rpm.RPMTAG_POSTINPROG)
            if not postin:
                printError(pkg, 'menu-without-postin')
            elif not update_menus_regex.search(postin):
                printError(pkg, 'postin-without-update-menus')

            postun = pkg[rpm.RPMTAG_POSTUN] or \
                pkg.scriptprog(rpm.RPMTAG_POSTUNPROG)
            if not postun:
                printError(pkg, 'menu-without-postun')
            elif not update_menus_regex.search(postun):
                printError(pkg, 'postun-without-update-menus')

            directory = pkg.dirName()
            for f in menus:
                # remove comments and handle cpp continuation lines
                cmd = Pkg.getstatusoutput(('/lib/cpp', directory + f), True)[1]

                for line in cmd.splitlines():
                    if not line.startswith('?'):
                        continue
                    res = package_regex.search(line)
                    if res:
                        package = res.group(1)
                        if package != pkg.name:
                            printWarning(pkg,
                                         'incoherent-package-value-in-menu',
                                         package, f)
                    else:
                        printInfo(pkg, 'unable-to-parse-menu-entry', line)

                    command = True
                    res = command_regex.search(line)
                    if res:
                        command_line = (res.group(1) or res.group(2)).split()
                        command = command_line[0]
                        for launcher in launchers:
                            if not launcher[0].search(command):
                                continue
                            found = False
                            if launcher[1]:
                                found = '/bin/' + command_line[0] in files or \
                                    '/usr/bin/' + command_line[0] in files or \
                                    '/usr/X11R6/bin/' + command_line[0] \
                                    in files
                                if not found:
                                    for l in launcher[1]:
                                        if l in pkg.req_names():
                                            found = True
                                            break
                                if not found:
                                    printError(pkg,
                                               'use-of-launcher-in-menu-but-no-requires-on',
                                               launcher[1][0])
                            command = command_line[1]
                            break

                        if command[0] == '/':
                            if command not in files:
                                printWarning(
                                    pkg, 'menu-command-not-in-package',
                                    command)
                        elif not ('/bin/' + command in files or
                                  '/usr/bin/' + command in files or
                                  '/usr/X11R6/bin/' + command in files):
                            printWarning(pkg, 'menu-command-not-in-package',
                                         command)
                    else:
                        printWarning(pkg, 'missing-menu-command')
                        command = False

                    res = longtitle_regex.search(line)
                    if res:
                        grp = res.groups()
                        title = grp[1] or grp[2]
                        if title[0] != title[0].upper():
                            printWarning(pkg, 'menu-longtitle-not-capitalized',
                                         title)
                        res = version_regex.search(title)
                        if res:
                            printWarning(pkg, 'version-in-menu-longtitle',
                                         title)
                    else:
                        printError(pkg, 'no-longtitle-in-menu', f)
                        title = None

                    res = title_regex.search(line)
                    if res:
                        grp = res.groups()
                        title = grp[1] or grp[2]
                        if title[0] != title[0].upper():
                            printWarning(pkg, 'menu-title-not-capitalized',
                                         title)
                        res = version_regex.search(title)
                        if res:
                            printWarning(pkg, 'version-in-menu-title', title)
                        if '/' in title:
                            printError(pkg, 'invalid-title', title)
                    else:
                        printError(pkg, 'no-title-in-menu', f)
                        title = None

                    res = needs_regex.search(line)
                    if res:
                        grp = res.groups()
                        needs = (grp[1] or grp[2]).lower()
                        if needs in ('x11', 'text', 'wm'):
                            res = section_regex.search(line)
                            if res:
                                grp = res.groups()
                                section = grp[1] or grp[2]
                                # don't warn entries for sections
                                if command and section not in valid_sections:
                                    printError(pkg, 'invalid-menu-section',
                                               section, f)
                            else:
                                printInfo(pkg, 'unable-to-parse-menu-section',
                                          line)
                        elif needs not in standard_needs:
                            printInfo(pkg, 'strange-needs', needs, f)
                    else:
                        printInfo(pkg, 'unable-to-parse-menu-needs', line)

                    res = icon_regex.search(line)
                    if res:
                        icon = res.group(1)
                        if not icon_ext_regex.search(icon):
                            printWarning(pkg, 'invalid-menu-icon-type', icon)
                        if icon[0] == '/' and needs == 'x11':
                            printWarning(pkg, 'hardcoded-path-in-menu-icon',
                                         icon)
                        else:
                            for path in icon_paths:
                                if (path[0] + icon) not in files:
                                    printError(
                                        pkg, path[1] + '-icon-not-in-package',
                                        icon, f)
                    else:
                        printWarning(pkg, 'no-icon-in-menu', title)

                    res = xdg_migrated_regex.search(line)
                    if res:
                        if not res.group(1).lower() == "true":
                            printError(pkg, 'non-xdg-migrated-menu')
                    else:
                        printError(pkg, 'non-xdg-migrated-menu')
Example #58
0
 def check_file(self, pkg, filename):
     lines = pkg.grep(pam_stack_re, filename)
     if lines:
         printError(pkg, 'use-old-pam-stack', filename,
                    '(line %s)' % ", ".join(lines))