def registerErrorDetails(details): """details is expected to be a sequence of (id, description) pairs, where id is the error id like 'cronjob-unauthorized-file' and description is a human readable text describing the situation. The text may contain placeholders that will be replaced by the constants above.""" from Filter import addDetails for _id, desc in details: addDetails( _id, desc.format(url=AUDIT_BUG_URL, review_needed_text=REVIEW_NEEDED_TEXT, followup_needed_text=FOLLOWUP_NEEDED_TEXT, ghost_encountered_text=GHOST_ENCOUNTERED_TEXT))
def add_check(self, pkg_name, name_re, file_re): c = {} c['pkg_name'] = pkg_name c['name_re'] = re.compile(name_re) c['file_re'] = re.compile(file_re) self.checks_.append(c) if simple_naming_policy_re.search(name_re): details = "Its name should begin with " + name_re[1:] else: details = "Its name should match the regular expression " + name_re addDetails(pkg_name + '-naming-policy-not-applied', "This package doesn't respect the naming policy for %s " "packages.\n%s." % (pkg_name, details))
addDetails( 'permissions-unauthorized-file', """If the package is intended for inclusion in any SUSE product please open a bug report to request review of the package by the security team. Please refer to {} for more information.""".format(AUDIT_BUG_URL), 'permissions-symlink', """permissions handling for symlinks is useless. Please contact [email protected] to remove the entry. Please refer to {} for more information.""".format(AUDIT_BUG_URL), 'permissions-dir-without-slash', """the entry in the permissions file refers to a directory. Please contact [email protected] to append a slash to the entry in order to avoid security problems. Please refer to {} for more information.""".format(AUDIT_BUG_URL), 'permissions-file-as-dir', """the entry in the permissions file refers to a directory but the package actually contains a file. Please contact [email protected] to remove the slash. Please refer to {} for more information.""".format(AUDIT_BUG_URL), 'permissions-incorrect', """please use the %attr macro to set the correct permissions.""", 'permissions-incorrect-owner', """please use the %attr macro to set the correct ownership.""", 'permissions-file-setuid-bit', """If the package is intended for inclusion in any SUSE product please open a bug report to request review of the program by the security team. Please refer to {} for more information.""".format(AUDIT_BUG_URL), 'permissions-directory-setuid-bit', """If the package is intended for inclusion in any SUSE product please open a bug report to request review of the package by the security team. Please refer to {} for more information.""".format(AUDIT_BUG_URL), 'permissions-world-writable', """If the package is intended for inclusion in any SUSE product please open a bug report to request review of the package by the security team. Please refer to {} for more information.""".format(AUDIT_BUG_URL), 'permissions-fscaps', """Packaging file capabilities is currently not supported. Please use normal permissions instead. You may contact the security team to request an entry that sets capabilities in /etc/permissions instead.""", 'permissions-missing-postin', """Please add an appropriate %post section""", 'permissions-missing-requires', """Please add \"PreReq: permissions\"""", 'permissions-missing-verifyscript', """Please add a %verifyscript section""", 'permissions-suseconfig-obsolete', """The %run_permissions macro calls SuSEconfig which sets permissions for all files in the system. Please use %set_permissions <filename> instead to only set permissions for files contained in this package""", )
class BinaryInfo(object): needed_regex = re.compile('\s+\(NEEDED\).*\[(\S+)\]') rpath_regex = re.compile('\s+\(RPATH\).*\[(\S+)\]') soname_regex = re.compile('\s+\(SONAME\).*\[(\S+)\]') comment_regex = re.compile('^\s+\[\s*\d+\]\s+\.comment\s+') pic_regex = re.compile('^\s+\[\s*\d+\]\s+\.rela?\.(data|text)') # GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x4 stack_regex = re.compile('^\s+GNU_STACK\s+(?:(?:\S+\s+){5}(\S+)\s+)?') stack_exec_regex = re.compile('^..E$') undef_regex = re.compile('^undefined symbol:\s+(\S+)') unused_regex = re.compile('^\s+(\S+)') call_regex = re.compile('\s0\s+FUNC\s+(.*)') exit_call_regex = create_regexp_call('_?exit') fork_call_regex = create_regexp_call('fork') setgid_call_regex = create_regexp_call('set(?:res|e)?gid') setuid_call_regex = create_regexp_call('set(?:res|e)?uid') setgroups_call_regex = create_regexp_call('(?:ini|se)tgroups') chroot_call_regex = create_regexp_call('chroot') # 401eb8: e8 c3 f0 ff ff callq 400f80 <chdir@plt> objdump_call_regex = re.compile(b'callq?\s(.*)') forbidden_functions = Config.getOption("WarnOnFunction") if forbidden_functions: for name, func in forbidden_functions.items(): # precompile regexps f_name = func['f_name'] func['f_regex'] = create_nonlibc_regexp_call(f_name) if 'good_param' in func: func['waiver_regex'] = re.compile(func['good_param']) # register descriptions addDetails(name, func['description']) chdir_call_regex = create_regexp_call('chdir') mktemp_call_regex = create_regexp_call('mktemp') def __init__(self, pkg, path, file, is_ar, is_shlib): self.readelf_error = False self.needed = [] self.rpath = [] self.undef = [] self.unused = [] self.comment = False self.soname = False self.non_pic = True self.stack = False self.exec_stack = False self.exit_calls = [] self.forbidden_calls = [] fork_called = False self.tail = '' self.setgid = False self.setuid = False self.setgroups = False self.chroot = False self.chdir = False self.chroot_near_chdir = False self.mktemp = False is_debug = path.endswith('.debug') cmd = ['env', 'LC_ALL=C', 'readelf', '-W', '-S', '-l', '-d', '-s'] cmd.append(path) res = Pkg.getstatusoutput(cmd) if not res[0]: lines = res[1].splitlines() for l in lines: r = BinaryInfo.needed_regex.search(l) if r: self.needed.append(r.group(1)) continue r = BinaryInfo.rpath_regex.search(l) if r: for p in r.group(1).split(':'): self.rpath.append(p) continue if BinaryInfo.comment_regex.search(l): self.comment = True continue if BinaryInfo.pic_regex.search(l): self.non_pic = False continue r = BinaryInfo.soname_regex.search(l) if r: self.soname = r.group(1) continue r = BinaryInfo.stack_regex.search(l) if r: self.stack = True flags = r.group(1) if flags and BinaryInfo.stack_exec_regex.search(flags): self.exec_stack = True continue if l.startswith("Symbol table"): break for l in lines: r = BinaryInfo.call_regex.search(l) if not r: continue l = r.group(1) if BinaryInfo.mktemp_call_regex.search(l): self.mktemp = True if BinaryInfo.setgid_call_regex.search(l): self.setgid = True if BinaryInfo.setuid_call_regex.search(l): self.setuid = True if BinaryInfo.setgroups_call_regex.search(l): self.setgroups = True if BinaryInfo.chdir_call_regex.search(l): self.chdir = True if BinaryInfo.chroot_call_regex.search(l): self.chroot = True if BinaryInfo.forbidden_functions: for r_name, func in BinaryInfo.forbidden_functions.items(): ret = func['f_regex'].search(l) if ret: self.forbidden_calls.append(r_name) if is_shlib: r = BinaryInfo.exit_call_regex.search(l) if r: self.exit_calls.append(r.group(1)) continue r = BinaryInfo.fork_call_regex.search(l) if r: fork_called = True continue # check if we don't have a string that will automatically # waive the presence of a forbidden call if self.forbidden_calls: cmd = ['env', 'LC_ALL=C', 'strings'] cmd.append(path) res = Pkg.getstatusoutput(cmd) if not res[0]: for l in res[1].splitlines(): # as we need to remove elements, iterate backwards for i in range(len(self.forbidden_calls) - 1, -1, -1): func = self.forbidden_calls[i] f = BinaryInfo.forbidden_functions[func] if 'waiver_regex' not in f: continue r = f['waiver_regex'].search(l) if r: del self.forbidden_calls[i] if self.non_pic: self.non_pic = 'TEXTREL' in res[1] # Ignore all exit() calls if fork() is being called. # Does not have any context at all but without this kludge, the # number of false positives would probably be intolerable. if fork_called: self.exit_calls = [] # check if chroot is near chdir (since otherwise, chroot is called # without chdir) # Currently this implementation works only on x86_64 due to reliance # on x86_64 specific assembly. Skip it on other architectures if pkg.arch == 'x86_64' and self.chroot and self.chdir: p = subprocess.Popen( ['env', 'LC_ALL=C', 'objdump', '-d', path], stdout=subprocess.PIPE, bufsize=-1) with p.stdout: index = 0 chroot_index = -99 chdir_index = -99 for line in p.stdout: res = BinaryInfo.objdump_call_regex.search(line) if not res: continue if b'@plt' not in res.group(1): pass elif b'chroot@plt' in res.group(1): chroot_index = index if abs(chroot_index - chdir_index) <= 2: self.chroot_near_chdir = True break elif b'chdir@plt' in res.group(1): chdir_index = index if abs(chroot_index - chdir_index) <= 2: self.chroot_near_chdir = True break index += 1 if p.wait() and not self.chroot_near_chdir: printWarning(pkg, 'binaryinfo-objdump-failed', file) self.chroot_near_chdir = True # avoid false positive else: self.readelf_error = True printWarning(pkg, 'binaryinfo-readelf-failed', file, re.sub('\n.*', '', res[1])) try: with open(path, 'rb') as fobj: fobj.seek(-12, os.SEEK_END) self.tail = Pkg.b2s(fobj.read()) except Exception as e: printWarning(pkg, 'binaryinfo-tail-failed %s: %s' % (file, e)) # Undefined symbol and unused direct dependency checks make sense only # for installed packages. # skip debuginfo: https://bugzilla.redhat.com/190599 if not is_ar and not is_debug and isinstance(pkg, Pkg.InstalledPkg): # We could do this with objdump, but it's _much_ simpler with ldd. res = Pkg.getstatusoutput( ('env', 'LC_ALL=C', 'ldd', '-d', '-r', path)) if not res[0]: for l in res[1].splitlines(): undef = BinaryInfo.undef_regex.search(l) if undef: self.undef.append(undef.group(1)) if self.undef: cmd = self.undef[:] cmd.insert(0, 'c++filt') try: res = Pkg.getstatusoutput(cmd) if not res[0]: self.undef = res[1].splitlines() except: pass else: printWarning(pkg, 'ldd-failed', file) res = Pkg.getstatusoutput( ('env', 'LC_ALL=C', 'ldd', '-r', '-u', path)) if res[0]: # Either ldd doesn't grok -u (added in glibc 2.3.4) or we have # unused direct dependencies in_unused = False for l in res[1].splitlines(): if not l.rstrip(): pass elif l.startswith('Unused direct dependencies'): in_unused = True elif in_unused: unused = BinaryInfo.unused_regex.search(l) if unused: self.unused.append(unused.group(1)) else: in_unused = False
if docfile.endswith("/INSTALL"): printWarning(pkg, "install-file-in-docs", docfile) def check_binary(self, pkg): if not pkg.docFiles(): return self.__checkRequirements(pkg) self.__checkUnwantedFiles(pkg) check = DocFilesCheck() addDetails( 'doc-file-dependency', '''An included file marked as %doc creates a possible additional dependency in the package. Usually, this is not wanted and may be caused by eg. example scripts with executable bits set included in the package's documentation.''', 'install-file-in-docs', '''A file whose name suggests that it contains installation instructions is included in the package. Such instructions are often not relevant for already installed packages; if this is the case for this file and it does not contain any information that is of interest after the package has been built and installed, do not include the file in the binary package.''', ) # DocFilesCheck.py ends here # ex: ts=4 sw=4 et
if pkg.isSource(): return ghosts = pkg.ghostFiles() for filename in (x for x in pkg.files() if x not in ghosts): if self.__files_re.match(filename): self.check_file(pkg, filename) def check_file(self, pkg, filename): """Virtual method called for each file that match the regexp passed to the constructor. """ raise NotImplementedError('check must be implemented in subclass') addDetails( 'invalid-url', '''The value should be a valid, public HTTP, HTTPS, or FTP URL.''', 'network-checks-disabled', '''Checks requiring network access have not been enabled in configuration, see the NetworkEnabled option.''', ) # AbstractCheck.py ends here # Local variables: # indent-tabs-mode: nil # py-indent-offset: 4 # End: # ex: ts=4 sw=4 et
addDetails( 'summary-too-long', 'The "Summary:" must not exceed %d characters.' % max_line_len, 'invalid-version', '''The version string must not contain the pre, alpha, beta or rc suffixes because when the final version will be out, you will have to use an Epoch tag to make the package upgradable. Instead put it in the release tag, prefixed with something you have control over.''', 'spelling-error', '''The value of this tag appears to be misspelled. Please double-check.''', 'no-packager-tag', '''There is no Packager tag in your package. You have to specify a packager using the Packager tag. Ex: Packager: John Doe <*****@*****.**>.''', 'invalid-packager', '''The packager email must end with an email compatible with the Packager option of rpmlint. Please change it and rebuild your package.''', 'no-version-tag', '''There is no Version tag in your package. You have to specify a version using the Version tag.''', 'no-release-tag', '''There is no Release tag in your package. You have to specify a release using the Release tag.''', 'not-standard-release-extension', 'Your release tag must match the regular expression ' + release_ext + '.', 'no-name-tag', '''There is no Name tag in your package. You have to specify a name using the Name tag.''', 'non-coherent-filename', '''The file which contains the package should be named <NAME>-<VERSION>-<RELEASE>.<ARCH>.rpm.''', 'no-dependency-on', ''' ''', 'incoherent-version-dependency-on', ''' ''', 'no-version-dependency-on', ''' ''', 'no-major-in-name', '''The major number of the library isn't included in the package's name. ''', 'no-provides', '''Your library package doesn't provide the -devel name without the major version included.''', 'no-summary-tag', '''There is no Summary tag in your package. You have to describe your package using this tag. To insert it, just insert a tag 'Summary'.''', 'summary-on-multiple-lines', '''Your summary must fit on one line. Please make it shorter and rebuild the package.''', 'summary-not-capitalized', '''Summary doesn't begin with a capital letter.''', 'summary-ended-with-dot', '''Summary ends with a dot.''', 'summary-has-leading-spaces', '''Summary begins with whitespace which will waste space when displayed.''', 'no-description-tag', '''The description of the package is empty or missing. To add it, insert a %description section in your spec file, add a textual description of the package after it, and rebuild the package.''', 'description-line-too-long', '''Your description lines must not exceed %d characters. If a line is exceeding this number, cut it to fit in two lines.''' % max_line_len, 'tag-in-description', '''Something that looks like a tag was found in the package's description. This may indicate a problem where the tag was not actually parsed as a tag but just textual description content, thus being a no-op. Verify if this is the case, and move the tag to a place in the specfile where %description won't fool the specfile parser, and rebuild the package.''', 'no-group-tag', '''There is no Group tag in your package. You have to specify a valid group in your spec file using the Group tag.''', 'non-standard-group', '''The value of the Group tag in the package is not valid. Valid groups are: "%s".''' % '", "'.join(VALID_GROUPS), 'no-changelogname-tag', '''There is no %changelog tag in your spec file. To insert it, just insert a '%changelog' in your spec file and rebuild it.''', 'no-version-in-last-changelog', '''The latest changelog entry doesn't contain a version. Please insert the version that is coherent with the version of the package and rebuild it.''', 'incoherent-version-in-changelog', '''The latest entry in %changelog contains a version identifier that is not coherent with the epoch:version-release tuple of the package.''', 'changelog-time-overflow', '''The timestamp of the latest entry in %changelog is suspiciously far away in the past; it is possible that it is actually so much in the future that it has overflowed rpm's timestamp representation.''', 'changelog-time-in-future', '''The timestamp of the latest entry in %changelog is in the future.''', 'no-license', '''There is no License tag in your spec file. You have to specify one license for your program (eg. GPL). To insert this tag, just insert a 'License' in your specfile.''', 'invalid-license', '''The value of the License tag was not recognized. Known values are: "%s".''' % '", "'.join(VALID_LICENSES), 'obsolete-not-provided', '''If a package is obsoleted by a compatible replacement, the obsoleted package should also be provided in order to not cause unnecessary dependency breakage. If the obsoleting package is not a compatible replacement for the old one, leave out the Provides.''', 'invalid-dependency', '''An invalid dependency has been detected. It usually means that the build of the package was buggy.''', 'no-epoch-tag', '''There is no Epoch tag in your package. You have to specify an epoch using the Epoch tag.''', 'unreasonable-epoch', '''The value of your Epoch tag is unreasonably large (> 99).''', 'no-epoch-in-dependency', '''Your package contains a versioned dependency without an Epoch.''', 'devel-dependency', '''Your package has a dependency on a devel package but it's not a devel package itself.''', 'invalid-build-requires', '''Your source package contains a dependency not compliant with the lib64 naming. This BuildRequires dependency will not be resolved on lib64 platforms (eg. amd64).''', 'explicit-lib-dependency', '''You must let rpm find the library dependencies by itself. Do not put unneeded explicit Requires: tags.''', 'useless-provides', '''This package provides 2 times the same capacity. It should only provide it once.''', 'tag-not-utf8', '''The character encoding of the value of this tag is not UTF-8.''', 'requires-on-release', '''This rpm requires a specific release of another package.''', 'no-url-tag', '''The URL tag is missing.''', 'name-repeated-in-summary', '''The name of the package is repeated in its summary. This is often redundant information and looks silly in various programs' output. Make the summary brief and to the point without including redundant information in it.''', 'enchant-dictionary-not-found', '''A dictionary for the Enchant spell checking library is not available for the language given in the info message. Spell checking will proceed with rpmlint's built-in implementation for localized tags in this language. For better spell checking results in this language, install the appropriate dictionary that Enchant will use for this language, often for example hunspell-* or aspell-*.''', 'self-obsoletion', '''The package obsoletes itself. This is known to cause errors in various tools and should thus be avoided, usually by using appropriately versioned Obsoletes and/or Provides and avoiding unversioned ones.''', 'unexpanded-macro', '''This tag contains something that looks like an unexpanded macro; this is often the sign of a misspelling. Please check your specfile.''', 'private-shared-object-provides', '''A shared object soname provides is provided by a file in a path from which other packages should not directly load shared objects from. Such shared objects should thus not be depended on and they should not result in provides in the containing package. Get rid of the provides if appropriate, for example by filtering it out during build. Note that in some cases this may require disabling rpmbuild's internal dependency generator.''', )
# Create an object to enable the auto registration of the test check = DistributionCheck() addDetails( 'invalid-vendor', '''In the "%s" distribution, vendor should be "%s".''' % (distribution, vendor), 'invalid-distribution', 'The distribution value should be "' + distribution + '".', 'manpage-not-compressed', '''This manual page is not compressed with the %s compression method (does not have the %s extension). If the compression does not happen automatically when the package is rebuilt, make sure that you have the appropriate rpm helper and/or config packages for your target distribution installed and try rebuilding again; if it still does not happen automatically, you can compress this file in the %%install section of the spec file.''' \ % (compress_ext, compress_ext), 'infopage-not-compressed', '''This info page is not compressed with the %s compression method (does not have the %s extension). If the compression does not happen automatically when the package is rebuilt, make sure that you have the appropriate rpm helper and/or config packages for your target distribution installed and try rebuilding again; if it still does not happen automatically, you can compress this file in the %%install section of the spec file.''' \ % (compress_ext, compress_ext), ) # DistributionCheck.py ends here
# handled by systemd-tmpfiles ghost_files = set(pkg.ghostFiles()) - tmp_files if ghost_files: for f in ghost_files: if f in pkg.missingOkFiles(): continue if not postin and not prein: printWarning(pkg, 'ghost-files-without-postin') if (not postin or f not in postin) and \ (not prein or f not in prein): printWarning(pkg, 'postin-without-ghost-file-creation', f) check = TmpFilesCheck() addDetails( 'postin-without-ghost-file-creation', '''A file tagged as ghost is not created during %prein nor during %postin.''', 'postin-without-tmpfile-creation', '''Please use the %tmpfiles_create macro in %post for each of your tmpfiles.d files''', 'tmpfile-not-regular-file', '''files in tmpfiles.d need to be regular files''', 'tmpfile-not-in-filelist', '''please add the specified file to your %files section as %ghost so users can easily query who created the file, it gets uninstalled on package removal and finally other rpmlint checks see it''', 'tmpfile-not-ghost', '''the specified file is not marked as %ghost although created at runtime via tmpfiles mechanism.''') # vim: sw=4 et
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) check = CheckSysVinitOnSystemd() if Config.info: addDetails( insserv_tag, '''In systemd based distributions insserv is obsolete. Please remove dependencies on insserv.''', etcinit_tag, '''SysV init scripts are deprecated. Please migrate to systemd service files.''', bootscr_tag, '''SysV boot scripts are deprecated. Please migrate to systemd service files.''', ) # Local variables: # indent-tabs-mode: nil # py-indent-offset: 4 # End: # -*- coding: utf-8 -*- # vim:sw=4:et:
for docfile in pkg.docFiles(): if docfile.endswith("/INSTALL"): printWarning(pkg, "install-file-in-docs", docfile) def check_binary(self, pkg): if not pkg.docFiles(): return self.__checkRequirements(pkg) self.__checkUnwantedFiles(pkg) check = DocFilesCheck() addDetails( 'doc-file-dependency', '''An included file marked as %doc creates a possible additional dependency in the package. Usually, this is not wanted and may be caused by eg. example scripts with executable bits set included in the package's documentation.''', 'install-file-in-docs', '''A file whose name suggests that it contains installation instructions is included in the package. Such instructions are often not relevant for already installed packages; if this is the case for this file and it does not contain any information that is of interest after the package has been built and installed, do not include the file in the binary package.''', ) # DocFilesCheck.py ends here
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) check = SourceCheck() addDetails( 'multiple-specfiles', '''Your package contains multiple spec files. To build a correct package, you need to have only one spec file containing all your RPM information.''', 'source-or-patch-not-compressed', '''A source archive or file in your package is not compressed using the %s compression method (doesn't have the %s extension).''' % (compress_ext, compress_ext), 'strange-permission', '''A file that you listed to include in your package has strange permissions. Usually, a file should have 0644 permissions.''', ) # SourceCheck.py ends here # ex: ts=4 sw=4 et
'suse-alternative-generic-name-missing', af) elif not stat.S_ISLNK(files[af].mode): printWarning(pkg, 'suse-alternative-generic-name-not-symlink', af) ### check that %post contains --install call ### check that %preun contains --remove call check = CheckUpdateAlternatives() if Config.info: addDetails( 'suse-alternative-generic-name-not-symlink', '''The update-alternative generic-name is not a symlink pointing to %{_sysconfdir}/alternatives/$(basename generic-name). ''', 'suse-alternative-link-not-ghost', '''The %{_sysconfdir}/alternatives/$(basename generic-name) link exists but is not marked as ghost. Mark it as %ghost.''', 'suse-alternative-link-missing', '''The file %{_sysconfdir}/alternatives/$(basename generic-name) is missing in the file list. Mark it as %ghost and add it to the file list.''', 'suse-alternative-generic-name-missing', '''The update-alternatives generic name is not in the filelist. Create it as a symlink to %{_sysconfdir}/alternatives/$(basename generic-name) and add it to the file list.''')
for f in invalidfhs: printError(pkg, 'suse-filelist-forbidden-fhs23', "%(file)s is not allowed in FHS 2.3" % {'file': f}) for f in invalidopt: printError( pkg, 'suse-filelist-forbidden-opt', '%(file)s is not allowed for official SUSE packages' % {'file': f}) check = FilelistCheck() if Config.info: for check in _checks: if 'details' not in check: continue if 'error' not in check: continue addDetails( 'suse-filelist-forbidden', """ Your package installs files or directories in a location that have previously been blacklisted. Please have a look at the particular file and see if the SUSE Packaging Guidelines propose a better place on where to install the file or not install it at all.""") addDetails(check['error'], check['details'])
check = I18NCheck() addDetails( # Need to add a function to list all the locales 'incorrect-i18n-tag-', """ """, 'incorrect-locale-subdir', """ """, 'incorrect-locale-', """ """, 'invalid-lc-messages-dir', """ """, 'invalid-locale-man-dir', """ """, 'file-not-in-lang', """ """, 'no-dependency-on', """ """, 'subfile-not-in-%lang', """ If /foo/bar is not tagged %lang(XX) whereas /foo is, the package won't be installable if XX is not in %_install_langs.""", ) # I18NCheck.py ends here
z and z.close() check = ZipCheck() addDetails( 'bad-crc-in-zip', '''The reported file in the zip fails the CRC check. Usually this is a sign of a corrupt zip file.''', 'uncompressed-zip', '''The zip file is not compressed.''', 'class-path-in-manifest', '''The META-INF/MANIFEST.MF file in the jar contains a hardcoded Class-Path. These entries do not work with older Java versions and even if they do work, they are inflexible and usually cause nasty surprises.''', 'jar-indexed', '''The jar file is indexed, ie. it contains the META-INF/INDEX.LIST file. These files are known to cause problems with some older Java versions.''', 'jar-not-indexed', '''The jar file is not indexed, ie. it does not contain the META-INF/INDEX.LIST file. Indexed jars speed up the class searching process of classloaders in some situations.''', ) # ZipCheck.py ends here # Local variables: # indent-tabs-mode: nil
# You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import os import AbstractCheck from Filter import addDetails, printWarning class RpmFileCheck(AbstractCheck.AbstractCheck): def __init__(self): AbstractCheck.AbstractCheck.__init__(self, "RpmFileCheck") def check(self, pkg): # http://en.wikipedia.org/wiki/Joliet_(file_system) rpmfile_name = os.path.basename(pkg.filename) if len(rpmfile_name) > 64: printWarning(pkg, 'filename-too-long-for-joliet', rpmfile_name) check = RpmFileCheck() addDetails( 'filename-too-long-for-joliet', '''This filename is too long to fit on a joliet filesystem (limit is 64 unicode chars).''', ) # ex: ts=4 sw=4 et
if line.endswith('{'): for logfile in line.split(' '): logfile = logfile.strip() if len(logfile) == 0 or logfile == '{': continue dn = os.path.dirname(logfile) if dn not in dirs: currentdirs.append(dn) dirs[dn] = None else: if line.endswith('}'): currentdirs = [] elif line.startswith("su "): a = line.split(" ") for dn in currentdirs: dirs[dn] = (a[1], a[2]) return dirs check = LogrotateCheck() addDetails( 'suse-logrotate-duplicate', """There are dupliated logrotate entries with different settings for the specified file""", 'suse-logrotate-user-writable-log-dir', """The log directory is writable by unprivileged users. Please fix the permissions so only root can write there or add the 'su' option to your logrotate config""", 'suse-logrotate-log-dir-not-packaged', """Please add the specified directory to the file list to be able to check permissions""")
if not processed['pre']: printWarning( pkg, 'systemd-service-without-service_add_pre', os.path.basename( fname ) ) if not processed['post']: printWarning( pkg, 'systemd-service-without-service_add_post', os.path.basename( fname ) ) if not processed['preun']: printWarning( pkg, 'systemd-service-without-service_del_preun', os.path.basename( fname ) ) if not processed['postun']: printWarning( pkg, 'systemd-service-without-service_del_postun', os.path.basename( fname ) ) # Create an object to enable the auto registration of the test check = CheckSystemdInstall() addDetails( 'systemd-service-without-service_add_pre', '''The package contains a systemd service but doesn't contain a %pre with a call to service_add_pre.''', 'systemd-service-without-service_add_post', '''The package contains a systemd service but doesn't contain a %post with a call to service_add_post.''', 'systemd-service-without-service_del_preun', '''The package contains a systemd service but doesn't contain a %preun with a call to service_del_preun.''', 'systemd-service-without-service_del_postun', '''The package contains a systemd service but doesn't contain a %postun with a call to service_del_postun.''', )
addDetails( 'shlib-policy-missing-suffix', """Your package containing shared libraries does not end in a digit and should probably be split.""", 'shlib-policy-devel-file', """Your shared library package contains development files. Split them into a -devel subpackage.""", 'shlib-policy-name-error', """Your package contains a single shared library but is not named after its SONAME.""", 'shlib-policy-nonversioned-dir', """Your shared library package contains non-versioned directories. Those will not allow to install multiple versions of the package in parallel.""", 'shlib-legacy-policy-name-error', """Your shared library package is not named after its SONAME, but it has been added to the list of legacy exceptions. Please do not rename the package until SONAME changes, but if you have to rename it for another reason, make sure you name it correctly.""", 'shlib-policy-excessive-dependency', """Your package starts with 'lib' as part of its name, but also contains binaries that have more dependencies than those that already required by the libraries. Those binaries should probably not be part of the library package, but split into a seperate one to reduce the additional dependencies for other users of this library.""", 'shlib-policy-missing-lib', """Your package starts with 'lib' as part of its name, but does not provide any libraries. It must not be called a lib-package then. Give it a more sensible name.""", 'shlib-fixed-dependency', """Your shared library package requires a fixed version of another package. The intention of the Shared Library Policy is to allow parallel installation of multiple versions of the same shared library, hard dependencies likely make that impossible. Please remove this dependency and instead move it to the runtime uses of your library.""", 'shlib-unversioned-lib', """Your package matches the Shared Library Policy Naming Scheme but contains an unversioned library. Therefore it is very unlikely that your package can be installed in parallel to another version of this library package. Consider moving unversioned parts into a runtime package.""" )
class AbstractFilesCheck(AbstractCheck): def __init__(self, name, file_regexp): self.__files_re = re.compile(file_regexp) AbstractCheck.__init__(self, name) def check_binary(self, pkg): ghosts = pkg.ghostFiles() for filename in (x for x in pkg.files() if x not in ghosts): if self.__files_re.match(filename): self.check_file(pkg, filename) def check_file(self, pkg, filename): """Virtual method called for each file that match the regexp passed to the constructor. """ raise NotImplementedError('check must be implemented in subclass') addDetails( 'invalid-url', '''The value should be a valid, public HTTP, HTTPS, or FTP URL.''', 'network-checks-disabled', '''Checks requiring network access have not been enabled in configuration, see the NetworkEnabled option.''', ) # AbstractCheck.py ends here
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") # Create an object to enable the auto registration of the test check = SignatureCheck() addDetails( 'no-signature', '''You have to include your pgp or gpg signature in your package. For more information on signatures, please refer to www.gnupg.org.''', 'unknown-key', '''The package was signed, but with an unknown key. See the rpm --import option for more information.''', ) # SignatureCheck.py ends here # Local variables: # indent-tabs-mode: nil # py-indent-offset: 4 # End: # ex: ts=4 sw=4 et
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) check = MenuXDGCheck() addDetails( 'invalid-desktopfile', '''.desktop file is not valid, check with desktop-file-validate''', 'non-utf8-desktopfile', '''.desktop file is not encoded in UTF-8''', 'desktopfile-without-binary', '''the .desktop file is for a file not present in the package. You should check the requires or see if this is not a error''', 'desktopfile-duplicate-section', '''The .desktop file contains the mentioned section name twice, which can trigger parsing ambiguities. Remove the duplicate.''', 'desktopfile-duplicate-option', '''The .desktop file contains the mentioned option key twice, which can trigger parsing ambiguities. Remove the duplicate.''', 'desktopfile-missing-header', '''The .desktop file should start with a section header.''', )
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) # Create an object to enable the auto registration of the test check = PostCheck() # Add information about checks addDetails( "postin-without-ghost-file-creation", """A file tagged as ghost is not created during %prein nor during %postin.""" ) for scriptlet in map(lambda x: "%" + x, RPM_SCRIPTLETS): addDetails( "one-line-command-in-%s" % scriptlet, """You should use %s -p <command> instead of using: %s <command> It will avoid the fork of a shell interpreter to execute your command as well as allows rpm to automatically mark the dependency on your command for the execution of the scriptlet.""" % (scriptlet, scriptlet), "percent-in-%s" % scriptlet, """The %s scriptlet contains a "%%" in a context which might indicate it being
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) check = SourceCheck() addDetails( 'multiple-specfiles', '''Your package contains multiple spec files. To build a correct package, you need to have only one spec file containing all your RPM information.''', 'source-or-patch-not-compressed', '''A source archive or file in your package is not compressed using the %s compression method (doesn't have the %s extension).''' % (compress_ext, compress_ext), 'strange-permission', '''A file that you listed to include in your package has strange permissions. Usually, a file should have 0644 permissions.''', ) # SourceCheck.py ends here # ex: ts=4 sw=4 et
addDetails( 'arch-independent-package-contains-binary-or-object', '''The package contains a binary or object file but is tagged noarch.''', 'arch-dependent-file-in-usr-share', '''This package installs an ELF binary in the /usr/share hierarchy, which is reserved for architecture-independent files.''', 'binary-in-etc', '''This package installs an ELF binary in /etc. Both the FHS and the FSSTND forbid this.''', # 'non-sparc32-binary', # '', 'invalid-soname', '''The soname of the library is neither of the form lib<libname>.so.<major> or lib<libname>-<major>.so.''', 'invalid-ldconfig-symlink', '''The symbolic link references the wrong file. It should reference the shared library.''', 'no-ldconfig-symlink', '''The package should not only include the shared library itself, but also the symbolic link which ldconfig would produce. (This is necessary, so that the link gets removed by rpm automatically when the package gets removed, even if for some reason ldconfig would not be run at package postinstall phase.)''', 'shlib-with-non-pic-code', '''The listed shared libraries contain object code that was compiled without -fPIC. All object code in shared libraries should be recompiled separately from the static libraries with the -fPIC option. Use the ``eu-findtextrel'' command on a library with debugging symbols to list code compiled without -fPIC. Another common mistake that causes this problem is linking with ``gcc -Wl,-shared'' instead of ``gcc -shared''.''', 'binary-or-shlib-defines-rpath', '''The binary or shared library defines `RPATH'. Usually this is a bad thing because it hardcodes the path to search libraries and so makes it difficult to move libraries around. Most likely you will find a Makefile with a line like: gcc test.o -o test -Wl,--rpath. Also, sometimes configure scripts provide a --disable-rpath flag to avoid this.''', 'statically-linked-binary', '''The package installs a statically linked binary or object file. Usually this is a packaging bug. If not, contact your rpmlint distributor about this so that this error gets included in the exception file for rpmlint and will not be flagged as a packaging bug in the future (or add it to your local configuration if you installed rpmlint from the source tarball).''', 'executable-in-library-package', '''The package mixes up libraries and executables. Mixing up these both types of files makes upgrades quite impossible.''', 'non-versioned-file-in-library-package', '''The package contains files in non versioned directories. This makes it impossible to have multiple major versions of the libraries installed. One solution can be to change the directories which contain the files to subdirs of /usr/lib/<name>-<version> or /usr/share/<name>-<version>. Another solution can be to include a version number in the file names themselves.''', 'incoherent-version-in-name', '''The package name should contain the major version of the library.''', 'invalid-directory-reference', 'This file contains a reference to /tmp or /home.', 'no-binary', '''The package should be of the noarch architecture because it doesn't contain any binaries.''', # http://sources.redhat.com/ml/libc-alpha/2003-05/msg00034.html 'undefined-non-weak-symbol', '''The binary contains undefined non-weak symbols. This may indicate improper linkage; check that the binary has been linked as expected.''', # http://www.redhat.com/archives/fedora-maintainers/2006-June/msg00176.html 'unused-direct-shlib-dependency', '''The binary contains unused direct shared library dependencies. This may indicate gratuitously bloated linkage; check that the binary has been linked with the intended shared libraries only.''', 'only-non-binary-in-usr-lib', '''There are only non binary files in /usr/lib so they should be in /usr/share.''', 'binaryinfo-readelf-failed', '''Executing readelf on this file failed, all checks could not be run.''', 'binaryinfo-objdump-failed', '''Executing objdump on this file failed, all checks could not be run.''', 'binaryinfo-tail-failed', '''Reading trailing bytes of this file failed, all checks could not be run.''', 'ldd-failed', '''Executing ldd on this file failed, all checks could not be run.''', 'executable-stack', '''The binary declares the stack as executable. Executable stack is usually an error as it is only needed if the code contains GCC trampolines or similar constructs which uses code on the stack. One common source for needlessly executable stack cases are object files built from assembler files which don\'t define a proper .note.GNU-stack section.''', 'missing-PT_GNU_STACK-section', '''The binary lacks a PT_GNU_STACK section. This forces the dynamic linker to make the stack executable. Usual suspects include use of a non-GNU linker or an old GNU linker version.''', 'shared-lib-calls-exit', '''This library package calls exit() or _exit(), probably in a non-fork() context. Doing so from a library is strongly discouraged - when a library function calls exit(), it prevents the calling program from handling the error, reporting it to the user, closing files properly, and cleaning up any state that the program has. It is preferred for the library to return an actual error code and let the calling program decide how to handle the situation.''', 'ocaml-mixed-executable', '''Executables built with ocamlc -custom are deprecated. Packagers should ask upstream maintainers to build these executables without the -custom option. If this cannot be changed and the executable needs to be packaged in its current form, make sure that rpmbuild does not strip it during the build, and on setups that use prelink, make sure that prelink does not strip it either, usually by placing a blacklist file in /etc/prelink.conf.d. For more information, see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=256900#49''', 'non-position-independent-executable', '''This executable must be position independent. Check that it is built with -fPIE/-fpie in compiler flags and -pie in linker flags.''', 'missing-call-to-setgroups-before-setuid', '''This executable is calling setuid and setgid without setgroups or initgroups. There is a high probability this means it didn't relinquish all groups, and this would be a potential security issue to be fixed. Seek POS36-C on the web for details about the problem.''', 'missing-call-to-chdir-with-chroot', '''This executable appears to call chroot without using chdir to change the current directory. This is likely an error and permits an attacker to break out of the chroot by using fchdir. While that's not always a security issue, this has to be checked.''', 'call-to-mktemp', '''This executable calls mktemp. As advised by the manpage (mktemp(3)), this function should be avoided. Some implementations are deeply insecure, and there is a race condition between the time of check and time of use (TOCTOU). See http://capec.mitre.org/data/definitions/29.html for details, and contact upstream to have this issue fixed.''', 'unstripped-binary-or-object', '''This executable should be stripped from debugging symbols, in order to take less space and be loaded faster. This is usually done automatically at buildtime by rpm. Check the build logs and the permission on the file (some implementations only strip if the permission is 0755).''' )
printWarning(pkg, "non-standard-dir-in-var", d) var_list.append(d) # Create an object to enable the auto registration of the test check = FHSCheck() addDetails( "non-standard-dir-in-usr", """Your package is creating a non-standard subdirectory in /usr. The standard directories are: %s.""" % ", ".join(FHSCheck.usr_subdir), "FSSTND-dir-in-var", """Your package is creating an illegal directory in /var. The FSSTND (illegal) ones are: %s.""" % ", ".join(FHSCheck.var_fsstnd), "non-standard-dir-in-var", """Your package is creating a non-standard subdirectory in /var. The standard directories are: %s.""" % ", ".join(FHSCheck.var_subdir), ) # FHSCheck.py ends here # Local variables: # indent-tabs-mode: nil # py-indent-offset: 4 # End:
def check_file(self, pkg, filename): pkgfile = pkg.files()[filename] if not (stat.S_ISREG(pkgfile.mode) and pkgfile.magic.startswith('POSIX shell script')): return try: status, output = Pkg.getstatusoutput(["dash", "-n", filename]) if status == 2: printWarning(pkg, "bin-sh-syntax-error", filename) status, output = Pkg.getstatusoutput( ["checkbashisms", filename]) if status == 1: printInfo(pkg, "potential-bashisms", filename) except (FileNotFoundError, UnicodeDecodeError): pass check = BashismsCheck() addDetails( 'bin-sh-syntax-error', '''A /bin/sh shell script contains a POSIX shell syntax error. This might indicate a potential bash-specific feature being used, try dash -n <file> for more detailed error message.''', 'potential-bashisms', '''checkbashisms reported potential bashisms in a /bin/sh shell script, you might want to manually check this script for bashisms.''')
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)) check = IconSizesCheck() addDetails( 'wrong-icon-size', """Your icon file is installed in a fixed-size directory, but has a largely incorrect size. Some desktop environments (e.g. GNOME) display them incorrectly.""")
addDetails( 'no-spec-file', '''No spec file was specified in your RPM building. Please specify a valid SPEC file to build a valid RPM package.''', 'invalid-spec-name', '''Your spec filename must end with '.spec'. If it's not the case, rename your file and rebuild your package.''', 'non-utf8-spec-file', '''The character encoding of the spec file is not UTF-8. Convert it for example using iconv(1).''', 'use-of-RPM_SOURCE_DIR', '''You use $RPM_SOURCE_DIR or %{_sourcedir} in your spec file. If you have to use a directory for building, use $RPM_BUILD_ROOT instead.''', 'patch-not-applied', '''A patch is included in your package but was not applied. Refer to the patches documentation to see what's wrong.''', 'obsolete-tag', '''The following tags are obsolete: Copyright and Serial. They must be replaced by License and Epoch respectively.''', 'deprecated-grep', '''Direct use of grep as egrep or fgrep is deprecated in GNU grep and historical in POSIX, use grep -E and grep -F instead.''', 'no-buildroot-tag', '''The BuildRoot tag isn't used in your spec. It must be used in order to allow building the package as non root on some systems. For some rpm versions (e.g. rpm.org >= 4.6) the BuildRoot tag is not necessary in specfiles and is ignored by rpmbuild; if your package is only going to be built with such rpm versions you can ignore this warning.''', 'hardcoded-path-in-buildroot-tag', '''A path is hardcoded in your Buildroot tag. It should be replaced by something like %{_tmppath}/%name-root.''', 'hardcoded-packager-tag', '''The Packager tag is hardcoded in your spec file. It should be removed, so as to use rebuilder's own defaults.''', 'buildarch-instead-of-exclusivearch-tag', '''Use ExclusiveArch instead of BuildArch (or BuildArchitectures) to restrict build on some specific architectures. Only use BuildArch with noarch''', 'hardcoded-prefix-tag', '''The Prefix tag is hardcoded in your spec file. It should be removed, so as to allow package relocation.''', 'hardcoded-library-path', '''A library path is hardcoded to one of the following paths: /lib, /usr/lib. It should be replaced by something like /%{_lib} or %{_libdir}.''', 'configure-without-libdir-spec', '''A configure script is run without specifying the libdir. configure options must be augmented with something like --libdir=%{_libdir} whenever the script supports it.''', 'no-%prep-section', '''The spec file does not contain a %prep section. Even if some packages don't directly need it, section markers may be overridden in rpm's configuration to provide additional "under the hood" functionality. Add the section, even if empty.''', 'no-%build-section', '''The spec file does not contain a %build section. Even if some packages don't directly need it, section markers may be overridden in rpm's configuration to provide additional "under the hood" functionality, such as injection of automatic -debuginfo subpackages. Add the section, even if empty.''', 'no-%install-section', '''The spec file does not contain an %install section. Even if some packages don't directly need it, section markers may be overridden in rpm's configuration to provide additional "under the hood" functionality. Add the section, even if empty.''', 'no-%clean-section', '''The spec file doesn't contain a %clean section to remove the files installed by the %install section.''', 'more-than-one-%changelog-section', '''The spec file unnecessarily contains more than one %changelog section; remove the extra ones.''', 'lib-package-without-%mklibname', '''The package name must be built using %mklibname to allow lib64 and lib32 coexistence.''', '%ifarch-applied-patch', '''A patch is applied inside an %ifarch block. Patches must be applied on all architectures and may contain necessary configure and/or code patch to be effective only on a given arch.''', 'prereq-use', '''The use of PreReq is deprecated. In the majority of cases, a plain Requires is enough and the right thing to do. Sometimes Requires(pre), Requires(post), Requires(preun) and/or Requires(postun) can also be used instead of PreReq.''', 'buildprereq-use', '''The use of BuildPreReq is deprecated, build dependencies are always required before a package can be built. Use plain BuildRequires instead.''', 'broken-syntax-in-scriptlet-requires', '''Comma separated context marked dependencies are silently broken in some versions of rpm. One way to work around it is to split them into several ones, eg. replace "Requires(post,preun): foo" with "Requires(post): foo" and "Requires(preun): foo".''', 'setup-not-in-prep', '''The %setup macro should only be used within the %prep section because it may not expand to anything outside of it and can break the build in unpredictable ways.''', 'setup-not-quiet', '''Use the -q option to the %setup macro to avoid useless build output from unpacking the sources.''', 'no-cleaning-of-buildroot', '''You should clean $RPM_BUILD_ROOT in the %clean section and in the beginning of the %install section. Use "rm -rf $RPM_BUILD_ROOT". Some rpm configurations do this automatically; if your package is only going to be built in such configurations, you can ignore this warning for the section(s) where your rpm takes care of it.''', 'rpm-buildroot-usage', '''$RPM_BUILD_ROOT should not be touched during %build or %prep stage, as it may break short circuit builds.''', 'make-check-outside-check-section', '''Make check or other automated regression test should be run in %check, as they can be disabled with a rpm macro for short circuiting purposes.''', 'macro-in-%changelog', '''Macros are expanded in %changelog too, which can in unfortunate cases lead to the package not building at all, or other subtle unexpected conditions that affect the build. Even when that doesn\'t happen, the expansion results in possibly "rewriting history" on subsequent package revisions and generally odd entries eg. in source rpms, which is rarely wanted. Avoid use of macros in %changelog altogether, or use two '%'s to escape them, like '%%foo'.''', 'depscript-without-disabling-depgen', '''In some common rpm configurations/versions, defining __find_provides and/or __find_requires has no effect if rpm's internal dependency generator has not been disabled for the build. %define _use_internal_dependency_generator to 0 to disable it in the specfile, or don't define __find_provides/requires.''', 'mixed-use-of-spaces-and-tabs', '''The specfile mixes use of spaces and tabs for indentation, which is a cosmetic annoyance. Use either spaces or tabs for indentation, not both.''', 'unversioned-explicit-provides', '''The specfile contains an unversioned Provides: token, which will match all older, equal, and newer versions of the provided thing. This may cause update problems and will make versioned dependencies, obsoletions and conflicts on the provided thing useless -- make the Provides versioned if possible.''', 'unversioned-explicit-obsoletes', '''The specfile contains an unversioned Obsoletes: token, which will match all older, equal and newer versions of the obsoleted thing. This may cause update problems, restrict future package/provides naming, and may match something it was originally not inteded to match -- make the Obsoletes versioned if possible.''', 'libdir-macro-in-noarch-package', '''The %{_libdir} or %{_lib} macro was found in a noarch package in a section that gets included in binary packages. This is most likely an error because these macros are expanded on the build host and their values vary between architectures, probably resulting in a package that does not work properly on all architectures at runtime. Investigate whether the package is really architecture independent or if some other dir/macro should be instead.''', 'non-break-space', '''The spec file contains a non-break space, which looks like a regular space in some editors but can lead to obscure errors. It should be replaced by a regular space.''', 'files-attr-not-set', '''A file or a directory entry in a %files section does not have attributes set which may result in unexpected file permissions and thus security issues in the resulting binary package depending on the build environment and rpmbuild version (typically < 4.4). Add default attributes using %defattr before it in the %files section, or use per entry %attr's.''', 'non-standard-group', '''The value of the Group tag in the package is not valid. Valid groups are: "%s".''' % '", "'.join(VALID_GROUPS), 'specfile-error', '''This error occurred when rpmlint used rpm to query the specfile. The error is output by rpm and the message should contain more information.''', 'comparison-operator-in-deptoken', '''This dependency token contains a comparison operator (<, > or =). This is usually not intended and may be caused by missing whitespace between the token's name, the comparison operator and the version string.''', 'macro-in-comment', '''There is a unescaped macro after a shell style comment in the specfile. Macros are expanded everywhere, so check if it can cause a problem in this case and escape the macro with another leading % if appropriate.''', 'file-size-mismatch', '''The size of the file in the package does not match the size indicated by peeking at its URL. Verify that the file in the package has the intended contents.''', 'file-md5-mismatch', '''The MD5 hash of the file in the package does not match the MD5 hash indicated by peeking at its URL. Verify that the file in the package has the intended contents.''', 'patch-fuzz-is-changed', '''The internal patch fuzz value was changed, and could hide patchs issues, or could lead to applying a patch at the wrong location. Usually, this is often the sign that someone didn't check if a patch is still needed and do not want to rediff it. It is usually better to rediff the patch and try to send it upstream.''' )
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) check = SourceCheck() addDetails( 'multiple-specfiles', '''Your package contains multiple spec files. To build a correct package, you need to have only one spec file containing all your RPM information.''', 'source-or-patch-not-compressed', '''A source archive or file in your package is not compressed using the %s compression method (doesn't have the %s extension).''' % (compress_ext, compress_ext), 'strange-permission', '''A file that you listed to include in your package has strange permissions. Usually, a file should have 0644 permissions.''', 'inconsistent-file-extension', '''The file name extension indicates a different compression format than what is actually used (as checked by file(1))''', ) # SourceCheck.py ends here
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") # Create an object to enable the auto registration of the test check = SignatureCheck() addDetails( 'no-signature', '''You have to include your pgp or gpg signature in your package. For more information on signatures, please refer to www.gnupg.org.''', 'unknown-key', '''The package was signed, but with an unknown key. See the rpm --import option for more information.''', ) # SignatureCheck.py ends here # ex: ts=4 sw=4 et
continue if d in FHSCheck.var_fsstnd: printWarning(pkg, "FSSTND-dir-in-var", fname) var_list.append(d) elif d not in FHSCheck.var_subdir: printWarning(pkg, "non-standard-dir-in-var", d) var_list.append(d) # Create an object to enable the auto registration of the test check = FHSCheck() addDetails( 'non-standard-dir-in-usr', """Your package is creating a non-standard subdirectory in /usr. The standard directories are: %s.""" % ", ".join(FHSCheck.usr_subdir), 'FSSTND-dir-in-var', """Your package is creating an illegal directory in /var. The FSSTND (illegal) ones are: %s.""" % ", ".join(FHSCheck.var_fsstnd), 'non-standard-dir-in-var', """Your package is creating a non-standard subdirectory in /var. The standard directories are: %s.""" % ", ".join(FHSCheck.var_subdir), ) # FHSCheck.py ends here # ex: ts=4 sw=4 et
printWarning(pkg, 'suse-alternative-link-not-ghost', etc_alt_file) # generic-name should be a symlink to /etc/alternatives/$(basename) if af not in files: printWarning(pkg, 'suse-alternative-generic-name-missing', af) elif not stat.S_ISLNK(files[af].mode): printWarning(pkg, 'suse-alternative-generic-name-not-symlink', af) # TODO check that %post contains --install call # TODO check that %preun contains --remove call check = CheckUpdateAlternatives() if Config.info: addDetails( 'suse-alternative-generic-name-not-symlink', '''The update-alternative generic-name is not a symlink pointing to %{_sysconfdir}/alternatives/$(basename generic-name). ''', 'suse-alternative-link-not-ghost', '''The %{_sysconfdir}/alternatives/$(basename generic-name) link exists but is not marked as ghost. Mark it as %ghost.''', 'suse-alternative-link-missing', '''The file %{_sysconfdir}/alternatives/$(basename generic-name) is missing in the file list. Mark it as %ghost and add it to the file list.''', 'suse-alternative-generic-name-missing', '''The update-alternatives generic name is not in the filelist. Create it as a symlink to %{_sysconfdir}/alternatives/$(basename generic-name) and add it to the file list.''')
invalidopt.add(d) for f in invalidfhs: printError(pkg, 'suse-filelist-forbidden-fhs23', "%(file)s is not allowed in FHS 2.3" % {'file': f}) for f in invalidopt: printError(pkg, 'suse-filelist-forbidden-opt', '%(file)s is not allowed for official SUSE packages' % {'file': f}) check = FilelistCheck() if Config.info: for check in _checks: if 'details' not in check: continue if 'error' not in check: continue addDetails('suse-filelist-forbidden', """ Your package installs files or directories in a location that have previously been blacklisted. Please have a look at the particular file and see if the SUSE Packaging Guidelines propose a better place on where to install the file or not install it at all.""") addDetails(check['error'], check['details'])
addDetails( 'init-script-without-chkconfig-postin', '''The package contains an init script but doesn't contain a %post with a call to chkconfig.''', 'postin-without-chkconfig', '''The package contains an init script but doesn't call chkconfig in its %post script.''', 'init-script-without-chkconfig-preun', '''The package contains an init script but doesn't contain a %preun with a call to chkconfig.''', 'preun-without-chkconfig', '''The package contains an init script but doesn't call chkconfig in its %preun script.''', 'missing-lsb-keyword', '''The package contains an init script that does not contain one of the LSB init script comment block convention keywords that are recommendable for all init scripts. If there is nothing to add to a keyword's value, include the keyword in the script with an empty value. Note that as of version 3.2, the LSB specification does not mandate presence of any keywords.''', 'no-status-entry', '''In your init script (/etc/rc.d/init.d/your_file), you don't have a 'status' entry, which is necessary for good functionality.''', 'no-reload-entry', '''In your init script (/etc/rc.d/init.d/your_file), you don't have a 'reload' entry, which is necessary for good functionality.''', 'no-chkconfig-line', '''The init script doesn't contain a chkconfig line to specify the runlevels at which to start and stop it.''', 'no-default-runlevel', '''The default runlevel isn't specified in the init script.''', 'service-default-enabled', '''The service is enabled by default after "chkconfig --add"; for security reasons, most services should not be. Use "-" as the default runlevel in the init script's "chkconfig:" line and/or remove the "Default-Start:" LSB keyword to fix this if appropriate for this service.''', 'subsys-unsupported', '''The init script uses /var/lock/subsys which is not supported by this distribution.''', 'subsys-not-used', '''While your daemon is running, you have to put a lock file in /var/lock/subsys/. To see an example, look at this directory on your machine and examine the corresponding init scripts.''', 'incoherent-subsys', '''The filename of your lock file in /var/lock/subsys/ is incoherent with your actual init script name. For example, if your script name is httpd, you have to use 'httpd' as the filename in your subsys directory. It is also possible that rpmlint gets this wrong, especially if the init script contains nontrivial shell variables and/or assignments. These cases usually manifest themselves when rpmlint reports that the subsys name starts a with '$'; in these cases a warning instead of an error is reported and you should check the script manually.''', 'incoherent-init-script-name', '''The init script name should be the same as the package name in lower case, or one with 'd' appended if it invokes a process by that name.''', 'init-script-name-with-dot', '''The init script name should not contain a dot in its name. Some versions of chkconfig don't work as expected with init script names like that.''', 'init-script-non-executable', '''The init script should have at least the execution bit set for root in order for it to run at boot time.''', )
addDetails( 'suse-branding-branding-conflict', '''Branding packages should conflict with other flavors of the branding package by using Conflicts: pkg-branding = brandingversion and not directly by numerating a name with -branding- in it.''', 'suse-branding-specific-branding-req', """packages must not require a specific branding or theme package to allow for different themes""", 'suse-branding-no-branding-provides', """Please add a provides entry similar to 'Provides: %name-branding = %version'.""", 'suse-branding-unversioned-provides', """Please make sure that your provides entry reads like: Provides: %name-branding = %version'.""", 'suse-branding-wrong-branding-supplement', """For consistency, the branding package should be in the form Supplements: packageand(basebackage:branding-<flavor> """, 'suse-branding-supplement-missing', """branding packages should provide a supplement in the form Supplements: packageand(basepackage:branding-<flavour>) """, 'suse-branding-unversioned-requires', """Please make sure that your requires entry is similar to: Requires: %name-branding = <versionnumber>'.""", 'suse-branding-missing-conflicts', """Any branding flavor package that provides the generic branding must also conflict with all other branding packages via conflict on the generic branding name""", )
addDetails( 'no-spec-file', '''No spec file was specified in your RPM building. Please specify a valid SPEC file to build a valid RPM package.''', 'invalid-spec-name', '''Your spec filename must end with '.spec'. If it's not the case, rename your file and rebuild your package.''', 'non-utf8-spec-file', '''The character encoding of the spec file is not UTF-8. Convert it for example using iconv(1).''', 'use-of-RPM_SOURCE_DIR', '''You use $RPM_SOURCE_DIR or %{_sourcedir} in your spec file. If you have to use a directory for building, use $RPM_BUILD_ROOT instead.''', 'patch-not-applied', '''A patch is included in your package but was not applied. Refer to the patches documentation to see what's wrong.''', 'obsolete-tag', '''The following tags are obsolete: Copyright and Serial. They must be replaced by License and Epoch respectively.''', 'deprecated-grep', '''Direct use of grep as egrep or fgrep is deprecated in GNU grep and historical in POSIX, use grep -E and grep -F instead.''', 'no-buildroot-tag', '''The BuildRoot tag isn't used in your spec. It must be used in order to allow building the package as non root on some systems. For some rpm versions (e.g. rpm.org >= 4.6) the BuildRoot tag is not necessary in specfiles and is ignored by rpmbuild; if your package is only going to be built with such rpm versions you can ignore this warning.''', 'hardcoded-path-in-buildroot-tag', '''A path is hardcoded in your Buildroot tag. It should be replaced by something like %{_tmppath}/%name-root.''', 'hardcoded-packager-tag', '''The Packager tag is hardcoded in your spec file. It should be removed, so as to use rebuilder's own defaults.''', 'buildarch-instead-of-exclusivearch-tag', '''Use ExclusiveArch instead of BuildArch (or BuildArchitectures) to restrict build on some specific architectures. Only use BuildArch with noarch''', 'hardcoded-prefix-tag', '''The Prefix tag is hardcoded in your spec file. It should be removed, so as to allow package relocation.''', 'hardcoded-library-path', '''A library path is hardcoded to one of the following paths: /lib, /usr/lib. It should be replaced by something like /%{_lib} or %{_libdir}.''', 'configure-without-libdir-spec', '''A configure script is run without specifying the libdir. configure options must be augmented with something like --libdir=%{_libdir} whenever the script supports it.''', 'no-%prep-section', '''The spec file does not contain a %prep section. Even if some packages don't directly need it, section markers may be overridden in rpm's configuration to provide additional "under the hood" functionality. Add the section, even if empty.''', 'no-%build-section', '''The spec file does not contain a %build section. Even if some packages don't directly need it, section markers may be overridden in rpm's configuration to provide additional "under the hood" functionality, such as injection of automatic -debuginfo subpackages. Add the section, even if empty.''', 'no-%install-section', '''The spec file does not contain an %install section. Even if some packages don't directly need it, section markers may be overridden in rpm's configuration to provide additional "under the hood" functionality. Add the section, even if empty.''', 'no-%clean-section', '''The spec file doesn't contain a %clean section to remove the files installed by the %install section.''', 'more-than-one-%changelog-section', '''The spec file unnecessarily contains more than one %changelog section; remove the extra ones.''', 'lib-package-without-%mklibname', '''The package name must be built using %mklibname to allow lib64 and lib32 coexistence.''', '%ifarch-applied-patch', '''A patch is applied inside an %ifarch block. Patches must be applied on all architectures and may contain necessary configure and/or code patch to be effective only on a given arch.''', 'prereq-use', '''The use of PreReq is deprecated. In the majority of cases, a plain Requires is enough and the right thing to do. Sometimes Requires(pre), Requires(post), Requires(preun) and/or Requires(postun) can also be used instead of PreReq.''', 'buildprereq-use', '''The use of BuildPreReq is deprecated, build dependencies are always required before a package can be built. Use plain BuildRequires instead.''', 'broken-syntax-in-scriptlet-requires', '''Comma separated context marked dependencies are silently broken in some versions of rpm. One way to work around it is to split them into several ones, eg. replace "Requires(post,preun): foo" with "Requires(post): foo" and "Requires(preun): foo".''', 'setup-not-in-prep', '''The %setup macro should only be used within the %prep section because it may not expand to anything outside of it and can break the build in unpredictable ways.''', 'setup-not-quiet', '''Use the -q option to the %setup macro to avoid useless build output from unpacking the sources.''', 'no-cleaning-of-buildroot', '''You should clean $RPM_BUILD_ROOT in the %clean section and in the beginning of the %install section. Use "rm -rf $RPM_BUILD_ROOT". Some rpm configurations do this automatically; if your package is only going to be built in such configurations, you can ignore this warning for the section(s) where your rpm takes care of it.''', 'rpm-buildroot-usage', '''$RPM_BUILD_ROOT should not be touched during %build or %prep stage, as it may break short circuit builds.''', 'make-check-outside-check-section', '''Make check or other automated regression test should be run in %check, as they can be disabled with a rpm macro for short circuiting purposes.''', 'macro-in-%changelog', '''Macros are expanded in %changelog too, which can in unfortunate cases lead to the package not building at all, or other subtle unexpected conditions that affect the build. Even when that doesn\'t happen, the expansion results in possibly "rewriting history" on subsequent package revisions and generally odd entries eg. in source rpms, which is rarely wanted. Avoid use of macros in %changelog altogether, or use two '%'s to escape them, like '%%foo'.''', 'depscript-without-disabling-depgen', '''In some common rpm configurations/versions, defining __find_provides and/or __find_requires has no effect if rpm's internal dependency generator has not been disabled for the build. %define _use_internal_dependency_generator to 0 to disable it in the specfile, or don't define __find_provides/requires.''', 'mixed-use-of-spaces-and-tabs', '''The specfile mixes use of spaces and tabs for indentation, which is a cosmetic annoyance. Use either spaces or tabs for indentation, not both.''', 'unversioned-explicit-provides', '''The specfile contains an unversioned Provides: token, which will match all older, equal, and newer versions of the provided thing. This may cause update problems and will make versioned dependencies, obsoletions and conflicts on the provided thing useless -- make the Provides versioned if possible.''', 'unversioned-explicit-obsoletes', '''The specfile contains an unversioned Obsoletes: token, which will match all older, equal and newer versions of the obsoleted thing. This may cause update problems, restrict future package/provides naming, and may match something it was originally not inteded to match -- make the Obsoletes versioned if possible.''', 'libdir-macro-in-noarch-package', '''The %{_libdir} or %{_lib} macro was found in a noarch package in a section that gets included in binary packages. This is most likely an error because these macros are expanded on the build host and their values vary between architectures, probably resulting in a package that does not work properly on all architectures at runtime. Investigate whether the package is really architecture independent or if some other dir/macro should be instead.''', 'non-break-space', '''The spec file contains a non-break space, which looks like a regular space in some editors but can lead to obscure errors. It should be replaced by a regular space.''', 'non-standard-group', '''The value of the Group tag in the package is not valid. Valid groups are: "%s".''' % '", "'.join(VALID_GROUPS), 'specfile-error', '''This error occurred when rpmlint used rpm to query the specfile. The error is output by rpm and the message should contain more information.''', 'comparison-operator-in-deptoken', '''This dependency token contains a comparison operator (<, > or =). This is usually not intended and may be caused by missing whitespace between the token's name, the comparison operator and the version string.''', 'macro-in-comment', '''There is a unescaped macro after a shell style comment in the specfile. Macros are expanded everywhere, so check if it can cause a problem in this case and escape the macro with another leading % if appropriate.''', 'file-size-mismatch', '''The size of the file in the package does not match the size indicated by peeking at its URL. Verify that the file in the package has the intended contents.''', 'file-md5-mismatch', '''The MD5 hash of the file in the package does not match the MD5 hash indicated by peeking at its URL. Verify that the file in the package has the intended contents.''', 'patch-fuzz-is-changed', '''The internal patch fuzz value was changed, and could hide patchs issues, or could lead to applying a patch at the wrong location. Usually, this is often the sign that someone didn't check if a patch is still needed and do not want to rediff it. It is usually better to rediff the patch and try to send it upstream.''' )
# Create an object to enable the auto registration of the test check = DistributionCheck() addDetails( 'invalid-vendor', 'In the "%s" distribution, vendor should be "%s".' % (distribution, vendor), 'invalid-distribution', 'The distribution value should be "' + distribution + '".', 'manpage-not-compressed', '''This manual page is not compressed with the %s compression method (does not have the %s extension). If the compression does not happen automatically when the package is rebuilt, make sure that you have the appropriate rpm helper and/or config packages for your target distribution installed and try rebuilding again; if it still does not happen automatically, you can compress this file in the %%install section of the spec file.''' % (compress_ext, compress_ext), 'infopage-not-compressed', '''This info page is not compressed with the %s compression method (does not have the %s extension). If the compression does not happen automatically when the package is rebuilt, make sure that you have the appropriate rpm helper and/or config packages for your target distribution installed and try rebuilding again; if it still does not happen automatically, you can compress this file in the %%install section of the spec file.''' % (compress_ext, compress_ext), ) # DistributionCheck.py ends here
addDetails( 'non-file-in-menu-dir', '''/usr/lib/menu must not contain anything else than normal files.''', 'non-coherent-menu-filename', '''The menu file name should be /usr/lib/menu/<package>.''', 'non-readable-menu-file', '''The menu file isn't readable. Check the permissions.''', 'old-menu-entry', ''' ''', 'non-transparent-xpm', '''xpm icon should be transparent for use in menus.''', 'menu-without-postin', '''A menu file exists in the package but no %post scriptlet is present to call update-menus.''', 'postin-without-update-menus', '''A menu file exists in the package but its %post scriptlet doesn't call update-menus.''', 'menu-without-postun', '''A menu file exists in the package but no %postun scriptlet is present to call update-menus.''', 'postun-without-update-menus', '''A menu file exists in the package but its %postun scriptlet doesn't call update-menus.''', 'incoherent-package-value-in-menu', '''The package field of the menu entry isn't the same as the package name.''', 'use-of-launcher-in-menu-but-no-requires-on', '''The menu command uses a launcher but there is no dependency in the package that contains it.''', 'menu-command-not-in-package', '''The command used in the menu isn't included in the package.''', 'menu-longtitle-not-capitalized', '''The longtitle field of the menu doesn't start with a capital letter.''', 'version-in-menu-longtitle', '''The longtitle filed of the menu entry contains a version. This is bad because it will be prone to error when the version of the package changes.''', 'no-longtitle-in-menu', '''The longtitle field isn't present in the menu entry.''', 'menu-title-not-capitalized', '''The title field of the menu entry doesn't start with a capital letter.''', 'version-in-menu-title', '''The title filed of the menu entry contains a version. This is bad because it will be prone to error when the version of the package changes.''', 'no-title-in-menu', '''The title field isn't present in the menu entry.''', 'invalid-menu-section', '''The section field of the menu entry isn't standard.''', 'unable-to-parse-menu-section', '''rpmlint wasn't able to parse the menu section. Please report.''', 'hardcoded-path-in-menu-icon', '''The path of the icon is hardcoded in the menu entry. This prevent multiple sizes of the icon from being found.''', 'normal-icon-not-in-package', '''The normal icon isn't present in the package.''', 'mini-icon-not-in-package', '''The mini icon isn't present in the package.''', 'large-icon-not-in-package', '''The large icon isn't present in the package.''', 'no-icon-in-menu', '''The menu entry doesn't contain an icon field.''', 'invalid-title', '''The menu title contains invalid characters like /.''', 'missing-menu-command', '''The menu file doesn't contain a command.''', 'menu-in-wrong-directory', '''The menu files must be under /usr/lib/menu.''', 'non-xdg-migrated-menu', '''The menu file has not been migrated to new XDG menu system.''', )
addDetails( 'undeclared-scl', '''Specfile contains %scl* macros, but was not recognized as SCL metapackage or SCL ready package. If this should be an SCL metapackage, don't forget to define the %scl macro. If this should be an SCL ready package, run %scl conditionalized %scl_package macro, e.g. %{?scl:%scl_package foo}.''', 'no-runtime-in-scl-metapackage', 'SCL metapackage must have runtime subpackage.', 'no-build-in-scl-metapackage', 'SCL metapackage must have build subpackage.', 'weird-subpackage-in-scl-metapackage', 'Only allowed subpackages in SCL metapackage are build and runtime.', 'scl-metapackage-without-scl-utils-build-br', 'SCL metapackage must BuildRequire scl-utils-build.', 'scl-build-without-requiring-scl-utils-build', 'SCL runtime package should Require scl-utils-build.', 'scl-metapackage-without-%scl_install', 'SCL metapackage must call %scl_install in the %install section.', 'noarch-scl-metapackage-with-libdir', '''If "enable" script of SCL metapackage contains %{_libdir}, the package must be arch specific, otherwise it may be noarch.''', 'scl-main-metapackage-contains-files', 'Main package of SCL metapackage should not contain any files.', 'scl-runtime-package-without-%scl_files', 'SCL runtime package must contain %scl_files in %files section.', 'scl-build-package-without-rpm-macros', '''SCL build package must contain %{_root_sysconfdir}/rpm/macros. %{scl}-config in %files section.''', 'missing-pkg_name-definition', '%{!?scl:%global pkg_name %{name}} is missing in the specfile.', 'name-without-scl-prefix', 'Name of SCL package must start with %{?scl_prefix}.', 'scl-prefix-without-condition', '''The SCL prefix is used without condition - this won't work if the package is build outside of SCL - use %{?scl_prefix} with questionmark.''', 'obsoletes-or-conflicts-without-scl-prefix', '''Obsoletes, Conflicts and Build Conflicts must always be prefixed with %{?scl_prefix}. This is extremely important, as the SCLs are often used for deploying new packages on older systems (that may contain old packages, now obsoleted by the new ones), but they shouldn't Obsolete or Conflict with the non-SCL RPMs installed on the system (that's the idea of SCL).''', 'provides-without-scl-prefix', 'Provides tag must always be prefixed with %{?scl_prefix}.', 'doesnt-require-scl-runtime-or-other-scl-package', '''The package must require %{scl}-runtime, unless it depends on another package that requires %{scl}-runtime. It's impossible to check what other packages require, so this simply checks if this package requires at least something from its collection.''', 'subpackage-with-n-without-scl-prefix', '''If (and only if) a package defines its name with -n, the name must be prefixed with %{?scl_prefix}.''', 'scl-setup-without-n', '''The %setup macro needs the -n argument for SCL builds, because the directory with source probably doesn't include SCL prefix in its name.''', 'scl-name-screwed-up', '''SCL package's name starts with SCL prefix. That prefix is used as a directory, where files are stored: If the prefix is foo, the directory is /opt/provides/foo. This package doesn't respect that. This means either the name of the package is wrong, or the directory.''', 'file-outside-of-scl-tree', '''SCL package should only contain files in /opt/provider/scl-name directory or in other allowed directories such as some directories in /etc or /var. Wrapper scripts in /usr/bin are also allowed.''', 'scl-rpm-macros-outside-of-build', '''RPM macros in SCL packages should belong to -build subpackage of the SCL metapackage.''', )
appdata_checker = Config.getOption("AppDataChecker", DEFAULT_APPDATA_CHECKER) class AppDataCheck(AbstractCheck.AbstractFilesCheck): def __init__(self): # desktop file need to be in $XDG_DATA_DIRS # $ echo $XDG_DATA_DIRS/applications # /var/lib/menu-xdg:/usr/share AbstractCheck.AbstractFilesCheck.__init__( self, "AppDataCheck", r"/usr/share/appdata/.*\.appdata.xml$") 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) check = AppDataCheck() addDetails( 'invalid-appdata-file', '''appdata file is not valid, check with %s''' % (" ".join(appdata_checker)), )
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) # Create an object to enable the auto registration of the test check = LSBCheck() addDetails( 'non-lsb-compliant-package-name', """Your package name contains an illegal character. Use only alphanumeric symbols in your package name.""", 'non-lsb-compliant-version', """Your version number contains an illegal character. Use only lowercase letters and/or numbers.""", 'non-lsb-compliant-release', """Your version number contains an illegal character. Use only lowercase letters and/or numbers.""", ) # LSBCheck.py ends here # Local variables: # indent-tabs-mode: nil # py-indent-offset: 4 # End: # ex: ts=4 sw=4 et
printWarning(pkg, 'ghost-files-without-postin') if (not postin or f not in postin) and \ (not prein or f not in prein): printWarning(pkg, 'postin-without-ghost-file-creation', f) check = TmpFilesCheck() addDetails( 'postin-without-ghost-file-creation', '''A file tagged as ghost is not created during %prein nor during %postin.''', 'postin-without-tmpfile-creation', '''Please use the %tmpfiles_create macro in %post for each of your tmpfiles.d files''', 'tmpfile-not-regular-file', '''files in tmpfiles.d need to be regular files''', 'tmpfile-not-in-filelist', '''please add the specified file to your %files section as %ghost so users can easily query who created the file, it gets uninstalled on package removal and finally other rpmlint checks see it''', 'tmpfile-not-ghost', '''the specified file is not marked as %ghost although created at runtime via tmpfiles mechanism.''' ) # vim: sw=4 et
addDetails( "shlib-policy-missing-suffix", """Your package containing shared libraries does not end in a digit and should probably be split.""", "shlib-policy-devel-file", """Your shared library package contains development files. Split them into a -devel subpackage.""", "shlib-policy-name-error", """Your package contains a single shared library but is not named after its SONAME.""", "shlib-policy-nonversioned-dir", """Your shared library package contains non-versioned directories. Those will not allow to install multiple versions of the package in parallel.""", "shlib-legacy-policy-name-error", """Your shared library package is not named after its SONAME, but it has been added to the list of legacy exceptions. Please do not rename the package until SONAME changes, but if you have to rename it for another reason, make sure you name it correctly.""", "shlib-policy-excessive-dependency", """Your package starts with 'lib' as part of its name, but also contains binaries that have more dependencies than those that already required by the libraries. Those binaries should probably not be part of the library package, but split into a seperate one to reduce the additional dependencies for other users of this library.""", "shlib-policy-missing-lib", """Your package starts with 'lib' as part of its name, but does not provide any libraries. It must not be called a lib-package then. Give it a more sensible name.""", "shlib-fixed-dependency", """Your shared library package requires a fixed version of another package. The intention of the Shared Library Policy is to allow parallel installation of multiple versions of the same shared library, hard dependencies likely make that impossible. Please remove this dependency and instead move it to the runtime uses of your library.""", "shlib-unversioned-lib", """Your package matches the Shared Library Policy Naming Scheme but contains an unversioned library. Therefore it is very unlikely that your package can be installed in parallel to another version of this library package. Consider moving unversioned parts into a runtime package.""", )
# GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import os import AbstractCheck from Filter import addDetails, printWarning class RpmFileCheck(AbstractCheck.AbstractCheck): def __init__(self): AbstractCheck.AbstractCheck.__init__(self, "RpmFileCheck") def check(self, pkg): # http://en.wikipedia.org/wiki/Joliet_(file_system) rpmfile_name = os.path.basename(pkg.filename) if len(rpmfile_name) > 64: printWarning(pkg, 'filename-too-long-for-joliet', rpmfile_name) check = RpmFileCheck() addDetails( 'filename-too-long-for-joliet', '''This filename is too long to fit on a joliet filesystem (limit is 64 unicode chars).''', )
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) # Create an object to enable the auto registration of the test check = PostCheck() # Add information about checks addDetails( 'postin-without-ghost-file-creation', '''A file tagged as ghost is not created during %prein nor during %postin.''', ) for scriptlet in map(lambda x: '%' + x, RPM_SCRIPTLETS): addDetails( 'one-line-command-in-%s' % scriptlet, '''You should use %s -p <command> instead of using: %s <command> It will avoid the fork of a shell interpreter to execute your command as well as allows rpm to automatically mark the dependency on your command for the execution of the scriptlet.''' % (scriptlet, scriptlet), 'percent-in-%s' % scriptlet, '''The %s scriptlet contains a "%%" in a context which might indicate it being fallout from an rpm macro/variable which was not expanded during build.
# Purpose : Apply pam policy ############################################################################# import re import AbstractCheck from Filter import addDetails, printError pam_stack_re = re.compile(r'^\s*[^#].*pam_stack\.so\s*service') class PamCheck(AbstractCheck.AbstractFilesCheck): def __init__(self): AbstractCheck.AbstractFilesCheck.__init__(self, "PamCheck", r"/etc/pam\.d/.*") 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)) check = PamCheck() addDetails( 'use-old-pam-stack', '''Update pam file to use include instead of pam_stack.''', )
addDetails( 'arch-independent-package-contains-binary-or-object', '''The package contains a binary or object file but is tagged noarch.''', 'arch-dependent-file-in-usr-share', '''This package installs an ELF binary in the /usr/share hierarchy, which is reserved for architecture-independent files.''', 'binary-in-etc', '''This package installs an ELF binary in /etc. Both the FHS and the FSSTND forbid this.''', # 'non-sparc32-binary', # '', 'invalid-soname', '''The soname of the library is neither of the form lib<libname>.so.<major> or lib<libname>-<major>.so.''', 'invalid-ldconfig-symlink', '''The symbolic link references the wrong file. It should reference the shared library.''', 'no-ldconfig-symlink', '''The package should not only include the shared library itself, but also the symbolic link which ldconfig would produce. (This is necessary, so that the link gets removed by rpm automatically when the package gets removed, even if for some reason ldconfig would not be run at package postinstall phase.)''', 'shlib-with-non-pic-code', '''The listed shared libraries contain object code that was compiled without -fPIC. All object code in shared libraries should be recompiled separately from the static libraries with the -fPIC option. Another common mistake that causes this problem is linking with ``gcc -Wl,-shared'' instead of ``gcc -shared''.''', 'binary-or-shlib-defines-rpath', '''The binary or shared library defines `RPATH'. Usually this is a bad thing because it hardcodes the path to search libraries and so makes it difficult to move libraries around. Most likely you will find a Makefile with a line like: gcc test.o -o test -Wl,--rpath. Also, sometimes configure scripts provide a --disable-rpath flag to avoid this.''', 'statically-linked-binary', '''The package installs a statically linked binary or object file. Usually this is a packaging bug. If not, contact your rpmlint distributor about this so that this error gets included in the exception file for rpmlint and will not be flagged as a packaging bug in the future (or add it to your local configuration if you installed rpmlint from the source tarball).''', 'executable-in-library-package', '''The package mixes up libraries and executables. Mixing up these both types of files makes upgrades quite impossible.''', 'non-versioned-file-in-library-package', '''The package contains files in non versioned directories. This makes it impossible to have multiple major versions of the libraries installed. One solution can be to change the directories which contain the files to subdirs of /usr/lib/<name>-<version> or /usr/share/<name>-<version>. Another solution can be to include a version number in the file names themselves.''', 'incoherent-version-in-name', '''The package name should contain the major version of the library.''', 'invalid-directory-reference', 'This file contains a reference to /tmp or /home.', 'no-binary', '''The package should be of the noarch architecture because it doesn't contain any binaries.''', # http://sources.redhat.com/ml/libc-alpha/2003-05/msg00034.html 'undefined-non-weak-symbol', '''The binary contains undefined non-weak symbols. This may indicate improper linkage; check that the binary has been linked as expected.''', # http://www.redhat.com/archives/fedora-maintainers/2006-June/msg00176.html 'unused-direct-shlib-dependency', '''The binary contains unused direct shared library dependencies. This may indicate gratuitously bloated linkage; check that the binary has been linked with the intended shared libraries only.''', 'only-non-binary-in-usr-lib', '''There are only non binary files in /usr/lib so they should be in /usr/share.''', 'binaryinfo-readelf-failed', '''Executing readelf on this file failed, all checks could not be run.''', 'binaryinfo-tail-failed', '''Reading trailing bytes of this file failed, all checks could not be run.''', 'ldd-failed', '''Executing ldd on this file failed, all checks could not be run.''', 'executable-stack', '''The binary declares the stack as executable. Executable stack is usually an error as it is only needed if the code contains GCC trampolines or similar constructs which uses code on the stack. One common source for needlessly executable stack cases are object files built from assembler files which don\'t define a proper .note.GNU-stack section.''', 'missing-PT_GNU_STACK-section', '''The binary lacks a PT_GNU_STACK section. This forces the dynamic linker to make the stack executable. Usual suspects include use of a non-GNU linker or an old GNU linker version.''', 'shared-lib-calls-exit', '''This library package calls exit() or _exit(), probably in a non-fork() context. Doing so from a library is strongly discouraged - when a library function calls exit(), it prevents the calling program from handling the error, reporting it to the user, closing files properly, and cleaning up any state that the program has. It is preferred for the library to return an actual error code and let the calling program decide how to handle the situation.''', 'ocaml-mixed-executable', '''Executables built with ocamlc -custom are deprecated. Packagers should ask upstream maintainers to build these executables without the -custom option. If this cannot be changed and the executable needs to be packaged in its current form, make sure that rpmbuild does not strip it during the build, and on setups that use prelink, make sure that prelink does not strip it either, usually by placing a blacklist file in /etc/prelink.conf.d. For more information, see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=256900#49''', 'non-position-independent-executable', '''This executable must be position independent. Check that it is built with -fPIE/-fpie in compiler flags and -pie in linker flags.''', 'missing-call-to-setgroups-before-setuid', '''This executable is calling setuid and setgid without setgroups or initgroups. There is a high probability this means it didn't relinquish all groups, and this would be a potential security issue to be fixed. Seek POS36-C on the web for details about the problem.''', 'missing-call-to-chdir-with-chroot', '''This executable appears to call chroot without using chdir to change the current directory. This is likely an error and permits an attacker to break out of the chroot by using fchdir. While that's not always a security issue, this has to be checked.''', 'call-to-mktemp', '''This executable calls mktemp. As advised by the manpage (mktemp(3)), this function should be avoided. Some implementations are deeply insecure, and there is a race condition between the time of check and time of use (TOCTOU). See http://capec.mitre.org/data/definitions/29.html for details, and contact upstream to have this issue fixed.''', 'unstripped-binary-or-object', '''This executable should be stripped from debugging symbols, in order to take less space and be loaded faster. This is usually done automatically at buildtime by rpm. Check the build logs and the permission on the file (some implementations only strip if the permission is 0755).''')
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) check = MenuXDGCheck() addDetails( 'invalid-desktopfile', '''.desktop file is not valid, check with desktop-file-validate''', 'non-utf8-desktopfile', '''.desktop file is not encoded in UTF-8''', 'desktopfile-without-binary', '''the .desktop file is for a file not present in the package. You should check the requires or see if this is not a error''', ) # ex: ts=4 sw=4 et
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) # Create an object to enable the auto registration of the test check = ConfigCheck() # Add information about checks addDetails( 'score-file-must-not-be-conffile', """A file in /var/lib/games/ is a configuration file. Store your conf files in /etc instead.""", 'non-etc-or-var-file-marked-as-conffile', """A file not in /etc or /var is marked as being a configuration file. Please put your conf files in /etc or /var.""", 'conffile-without-noreplace-flag', """A configuration file is stored in your package without the noreplace flag. A way to resolve this is to put the following in your SPEC file: %config(noreplace) /etc/your_config_file_here """, ) # ConfigCheck.py ends here
STANDARD_BIN_DIRS = ["/bin/", "/sbin/", "/usr/bin/", "/usr/sbin/"] DEFAULT_APPDATA_CHECKER = ("appstream-util", "validate-relax") appdata_checker = Config.getOption("AppDataChecker", DEFAULT_APPDATA_CHECKER) class AppDataCheck(AbstractCheck.AbstractFilesCheck): def __init__(self): # desktop file need to be in $XDG_DATA_DIRS # $ echo $XDG_DATA_DIRS/applications # /var/lib/menu-xdg:/usr/share AbstractCheck.AbstractFilesCheck.__init__(self, "AppDataCheck", "/usr/share/appdata/.*\.appdata.xml$") 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) check = AppDataCheck() addDetails("invalid-appdata-file", """appdata file is not valid, check with %s""" % (" ".join(appdata_checker))) # ex: ts=4 sw=4 et
printWarning(pkg, 'systemd-service-without-service_add_post', os.path.basename(fname)) if not processed['preun']: printWarning(pkg, 'systemd-service-without-service_del_preun', os.path.basename(fname)) if not processed['postun']: printWarning(pkg, 'systemd-service-without-service_del_postun', os.path.basename(fname)) # Create an object to enable the auto registration of the test check = CheckSystemdInstall() addDetails( 'systemd-service-without-service_add_pre', '''The package contains a systemd service but doesn't contain a %pre with a call to service_add_pre.''', 'systemd-service-without-service_add_post', '''The package contains a systemd service but doesn't contain a %post with a call to service_add_post.''', 'systemd-service-without-service_del_preun', '''The package contains a systemd service but doesn't contain a %preun with a call to service_del_preun.''', 'systemd-service-without-service_del_postun', '''The package contains a systemd service but doesn't contain a %postun with a call to service_del_postun.''', )
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)) check = IconSizesCheck() if Config.info: addDetails( 'wrong-icon-size', """Your icon file is installed in a fixed-size directory, but has a largely incorrect size. Some desktop environments (e.g. GNOME) display them incorrectly.""" )