Control: tags 1001603 + patch Control: tags 1001603 + pending
Dear maintainer,
I've prepared an NMU for rpmlint (versioned as 2.3.0+ds1-0.1) and
uploaded it to DELAYED/7. Please feel free to tell me if I
should delay it longer.
Regards.
diff -Nru rpmlint-2.2.0+ds1/configs/Fedora/users-groups.toml rpmlint-
2.3.0+ds1/configs/Fedora/users-groups.toml
--- rpmlint-2.2.0+ds1/configs/Fedora/users-groups.toml 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/configs/Fedora/users-groups.toml 2022-05-29
12:46:55.000000000 -0400
@@ -1,5 +1,5 @@
# generated using tools/generate-fedora-users-groups.py on 2021-03-23
-StandardUsers = ['pkiuser', 'sshd', 'cyrus', 'keystone', 'squid', 'lp',
'root', 'dovecot', 'oprofile', 'ldap', 'arpwatch', 'retrace', 'vdsm', 'nut',
'hacluster', 'polkituser', 'mailman', 'saned', 'adm', 'mailnull', 'rtkit',
'postfix', 'cimsrvr', 'postgres', 'vhostmd', 'smmsp', 'dbus', 'rpcuser',
'nslcd', 'named', 'radvd', 'ntp', 'systemd-resolve', 'nova', 'tss',
'wildfly', 'ricci', 'mysql', 'apache', 'usbmuxd', 'systemd-network', 'vcsa',
'sabayon', 'quantum', 'katello', 'xfs', 'halt', 'tomcat', 'beagleindex',
'jbosson-agent', 'fax', 'haldaemon', 'pulse', 'hsqldb', 'cassandra',
'pegasus', 'clamav', 'piranha', 'mongodb', 'netdump', 'activemq',
'ovirtagent', 'bin', 'aeolus', 'sync', 'radiusd', 'rpm', 'webalizer',
'nocpulse', 'elasticsearch', 'games', 'pvm', 'wnn', 'snortd', 'privoxy',
'nscd', 'gdm', 'prelude-manager', 'cinder', 'shutdown', 'rpc', 'condor',
'#systemd-journal-gateway', 'heat', 'qemu', 'myproxy', 'avahi', 'operator',
'majordomo', 'puppet', 'exim', 'sanlock', 'rhevm', 'swift', 'wallaby',
'ftp', 'ident', 'frontpage', 'ats', 'mail', 'ceilometer', 'news',
'distcache', 'ovirt', 'ceph', 'desktop', 'uucp', 'glance', 'jonas',
'haproxy', 'abrt', 'quagga', 'stap-server', 'dhcpd', 'nobody', 'luci',
'bacula', 'avahi-autoipd', 'gopher', 'tcpdump', 'daemon', 'amandabackup',
'jetty']
+StandardUsers = ['pkiuser', 'sshd', 'cyrus', 'keystone', 'squid', 'lp',
'root', 'dovecot', 'oprofile', 'ldap', 'arpwatch', 'retrace', 'vdsm', 'nut',
'hacluster', 'polkituser', 'mailman', 'saned', 'adm', 'mailnull', 'rtkit',
'postfix', 'cimsrvr', 'postgres', 'vhostmd', 'smmsp', 'dbus', 'rpcuser',
'nslcd', 'named', 'radvd', 'ntp', 'systemd-resolve', 'nova', 'tss',
'wildfly', 'ricci', 'mysql', 'apache', 'usbmuxd', 'systemd-network', 'vcsa',
'sabayon', 'quantum', 'katello', 'xfs', 'halt', 'tomcat', 'beagleindex',
'jbosson-agent', 'fax', 'haldaemon', 'pulse', 'hsqldb', 'cassandra',
'pegasus', 'clamav', 'piranha', 'mongodb', 'netdump', 'activemq',
'ovirtagent', 'bin', 'aeolus', 'sync', 'radiusd', 'rpm', 'webalizer',
'nocpulse', 'elasticsearch', 'games', 'pvm', 'wnn', 'snortd', 'privoxy',
'nscd', 'gdm', 'prelude-manager', 'cinder', 'shutdown', 'rpc', 'condor',
'#systemd-journal-gateway', 'heat', 'qemu', 'myproxy', 'avahi', 'operator',
'majordomo', 'puppet', 'exim', 'sanlock', 'rhevm', 'swift', 'wallaby',
'ftp', 'ident', 'frontpage', 'ats', 'mail', 'ceilometer', 'news',
'distcache', 'ovirt', 'ceph', 'desktop', 'uucp', 'glance', 'jonas',
'haproxy', 'abrt', 'quagga', 'stap-server', 'dhcpd', 'nobody', 'luci',
'bacula', 'avahi-autoipd', 'gopher', 'tcpdump', 'daemon', 'amandabackup',
'jetty', 'ergo']
-StandardGroups = ['pkiuser', 'sshd', 'keystone', 'squid', 'pppusers',
'kvm', 'popusers', 'lp', 'root', 'dovecot', 'oprofile', 'disk', 'ldap',
'arpwatch', 'retrace', 'nut', 'polkituser', 'audio', 'mailman', 'stapusr',
'saned', 'adm', 'mailnull', 'rtkit', 'postfix', 'utmp', 'cimsrvr', 'wheel',
'postgres', 'vhostmd', 'smmsp', 'realtime', 'kmem', 'rpcuser', 'dbus',
'screen', 'utempter', 'video', 'named', 'radvd', 'ntp', 'man', 'systemd-
resolve', 'nova', 'tss', 'sys', 'cdrom', 'wildfly', 'ricci', 'mysql',
'apache', 'usbmuxd', 'jbosson', 'systemd-network', 'vcsa', 'console',
'sabayon', 'quantum', 'katello', 'haclient', 'xfs', 'tomcat', 'beagleindex',
'fax', 'haldaemon', 'systemd-journal', 'pulse', 'hsqldb', 'cassandra',
'pegasus', 'clamav', 'piranha', 'mongodb', 'netdump', 'activemq',
'ovirtagent', 'bin', 'saslauth', 'aeolus', 'radiusd', 'mem', 'rpm',
'webalizer', 'floppy', 'nocpulse', 'elasticsearch', 'games', 'pvm', 'wnn',
'tty', 'snortd', 'slipusers', 'nscd', 'gdm', 'privoxy', 'mock', 'prelude-
manager', 'cinder', 'dialout', 'rpc', 'condor', '#systemd-journal-gateway',
'heat', 'qemu', 'stapsys', 'avahi', 'myproxy', 'majordomo', 'tape',
'puppet', 'exim', 'sanlock', 'rhevm', 'swift', 'wallaby', 'ftp', 'ident',
'frontpage', 'ats', 'mail', 'ceilometer', 'news', 'distcache', 'stapdev',
'users', 'ovirt', 'ceph', 'desktop', 'uucp', 'glance', 'jonas', 'postdrop',
'haproxy', 'abrt', 'quagga', 'stap-server', 'lock', 'dhcpd', 'nobody',
'wbpriv', 'luci', 'quaggavt', 'bacula', 'avahi-autoipd', 'gopher', 'wine',
'tcpdump', 'dip', 'daemon', 'slocate', 'jetty']
+StandardGroups = ['pkiuser', 'sshd', 'keystone', 'squid', 'pppusers',
'kvm', 'popusers', 'lp', 'root', 'dovecot', 'oprofile', 'disk', 'ldap',
'arpwatch', 'retrace', 'nut', 'polkituser', 'audio', 'mailman', 'stapusr',
'saned', 'adm', 'mailnull', 'rtkit', 'postfix', 'utmp', 'cimsrvr', 'wheel',
'postgres', 'vhostmd', 'smmsp', 'realtime', 'kmem', 'rpcuser', 'dbus',
'screen', 'utempter', 'video', 'named', 'radvd', 'ntp', 'man', 'systemd-
resolve', 'nova', 'tss', 'sys', 'cdrom', 'wildfly', 'ricci', 'mysql',
'apache', 'usbmuxd', 'jbosson', 'systemd-network', 'vcsa', 'console',
'sabayon', 'quantum', 'katello', 'haclient', 'xfs', 'tomcat', 'beagleindex',
'fax', 'haldaemon', 'systemd-journal', 'pulse', 'hsqldb', 'cassandra',
'pegasus', 'clamav', 'piranha', 'mongodb', 'netdump', 'activemq',
'ovirtagent', 'bin', 'saslauth', 'aeolus', 'radiusd', 'mem', 'rpm',
'webalizer', 'floppy', 'nocpulse', 'elasticsearch', 'games', 'pvm', 'wnn',
'tty', 'snortd', 'slipusers', 'nscd', 'gdm', 'privoxy', 'mock', 'prelude-
manager', 'cinder', 'dialout', 'rpc', 'condor', '#systemd-journal-gateway',
'heat', 'qemu', 'stapsys', 'avahi', 'myproxy', 'majordomo', 'tape',
'puppet', 'exim', 'sanlock', 'rhevm', 'swift', 'wallaby', 'ftp', 'ident',
'frontpage', 'ats', 'mail', 'ceilometer', 'news', 'distcache', 'stapdev',
'users', 'ovirt', 'ceph', 'desktop', 'uucp', 'glance', 'jonas', 'postdrop',
'haproxy', 'abrt', 'quagga', 'stap-server', 'lock', 'dhcpd', 'nobody',
'wbpriv', 'luci', 'quaggavt', 'bacula', 'avahi-autoipd', 'gopher', 'wine',
'tcpdump', 'dip', 'daemon', 'slocate', 'jetty', 'ergo']
diff -Nru rpmlint-2.2.0+ds1/configs/openSUSE/opensuse.toml rpmlint-
2.3.0+ds1/configs/openSUSE/opensuse.toml
--- rpmlint-2.2.0+ds1/configs/openSUSE/opensuse.toml 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/configs/openSUSE/opensuse.toml 2022-05-29
12:46:55.000000000 -0400
@@ -146,7 +146,6 @@
'ldconfig-post.*/ddiwrapper/wine/',
'glibc\.\S+: \w: statically-linked-binary
/usr/sbin/glibc_post_upgrade',
' symlink-should-be-relative ',
- ' binary-or-shlib-defines-rpath .*ORIGIN',
'libzypp.*shlib-policy-name-error.*libzypp',
'libtool.*shlib-policy.*',
@@ -177,7 +176,6 @@
' preun-without-chkconfig ',
' no-dependency-on locales',
' shlib-policy-name-error',
- ' binary-or-shlib-defines-rpath',
' executable-marked-as-config-file',
' log-files-without-logrotate',
' hardcoded-prefix-tag',
diff -Nru rpmlint-2.2.0+ds1/configs/openSUSE/scoring.toml rpmlint-
2.3.0+ds1/configs/openSUSE/scoring.toml
--- rpmlint-2.2.0+ds1/configs/openSUSE/scoring.toml 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/configs/openSUSE/scoring.toml 2022-05-29
12:46:55.000000000 -0400
@@ -34,3 +34,5 @@
obsolete-insserv-requirement = 10000
deprecated-init-script = 10000
deprecated-boot-script = 10000
+# Temporarily disable once boo#1199268 gets fixed
+# binary-or-shlib-defines-rpath = 10000
diff -Nru rpmlint-2.2.0+ds1/configs/openSUSE/users-groups.toml rpmlint-
2.3.0+ds1/configs/openSUSE/users-groups.toml
--- rpmlint-2.2.0+ds1/configs/openSUSE/users-groups.toml 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/configs/openSUSE/users-groups.toml 2022-05-29
12:46:55.000000000 -0400
@@ -45,6 +45,7 @@
'dovecot',
'elasticsearch',
'epmd',
+ 'ergo',
'festival',
'ffums',
'firebird',
@@ -68,6 +69,7 @@
'icecream',
'icinga',
'icingacmd',
+ 'icingaweb2',
'ifdrwww',
'intermezzo',
'iouyap',
@@ -265,6 +267,7 @@
'ec2-api',
'elasticsearch',
'epmd',
+ 'ergo',
'fax',
'festival',
'fetchmail',
diff -Nru rpmlint-2.2.0+ds1/debian/changelog rpmlint-
2.3.0+ds1/debian/changelog
--- rpmlint-2.2.0+ds1/debian/changelog 2021-12-15 14:25:08.000000000 -0500
+++ rpmlint-2.3.0+ds1/debian/changelog 2022-05-29 12:54:07.000000000 -0400
@@ -1,3 +1,14 @@
+rpmlint (2.3.0+ds1-0.1) unstable; urgency=medium
+
+ * Non-maintainer upload.
+ * New upstream release.
+ * debian/control: Update dependency accordingly.
+ * debian/copyright: Update Files-Excluded field.
+ * debian/rpmlint.maintscript: Purge obsolete conffile on upgrade.
+ (Closes: #1001603)
+
+ -- Boyuan Yang <[email protected]> Sun, 29 May 2022 12:54:07 -0400
+
rpmlint (2.2.0+ds1-0.2) unstable; urgency=high
* Non-maintainer upload.
diff -Nru rpmlint-2.2.0+ds1/debian/control rpmlint-2.3.0+ds1/debian/control
--- rpmlint-2.2.0+ds1/debian/control 2021-12-15 14:20:28.000000000 -0500
+++ rpmlint-2.3.0+ds1/debian/control 2022-05-29 12:49:09.000000000 -0400
@@ -13,6 +13,8 @@
dh-sequence-python3,
iso-codes,
pkg-config,
+ python3-enchant,
+ python3-magic,
python3-pybeam,
python3-pytest <!nocheck>,
python3-pytest-cov <!nocheck>,
@@ -26,7 +28,7 @@
python3,
rpm,
rpm2cpio,
-Standards-Version: 4.5.0
+Standards-Version: 4.6.1
Homepage: https://github.com/rpm-software-management/rpmlint
Vcs-Git: https://salsa.debian.org/pkg-rpm-team/rpmlint.git
Vcs-Browser: https://salsa.debian.org/pkg-rpm-team/rpmlint
@@ -41,11 +43,11 @@
rpm2cpio,
${misc:Depends},
${python3:Depends},
+ python3-enchant,
+ python3-magic,
Recommends:
appstream-util,
groff-base,
- python3-enchant,
- python3-magic,
rpm,
Description: RPM package checker
rpmlint is a tool for checking common errors in rpm packages. rpmlint
diff -Nru rpmlint-2.2.0+ds1/debian/copyright rpmlint-
2.3.0+ds1/debian/copyright
--- rpmlint-2.2.0+ds1/debian/copyright 2021-12-15 14:20:28.000000000 -0500
+++ rpmlint-2.3.0+ds1/debian/copyright 2022-05-29 12:46:37.000000000 -0400
@@ -2,10 +2,10 @@
Upstream-Name: rpmlint
Source: https://github.com/rpm-software-management/rpmlint
Files-Excluded:
- tests/binary/*
- tests/ldd/*
- tests/pyc/*
- tests/readelf/*
+ test/binary/*
+ test/ldd/*
+ test/pyc/*
+ test/readelf/*
Files: *
Copyright: 2013 Frédéric Lepied <[email protected]>
diff -Nru rpmlint-2.2.0+ds1/debian/rpmlint.maintscript rpmlint-
2.3.0+ds1/debian/rpmlint.maintscript
--- rpmlint-2.2.0+ds1/debian/rpmlint.maintscript 1969-12-31
19:00:00.000000000 -0500
+++ rpmlint-2.3.0+ds1/debian/rpmlint.maintscript 2022-05-29
12:53:45.000000000 -0400
@@ -0,0 +1 @@
+rm_conffile /etc/rpmlint/config 2.3.0~ rpmlint
diff -Nru rpmlint-2.2.0+ds1/.github/workflows/main.yml rpmlint-
2.3.0+ds1/.github/workflows/main.yml
--- rpmlint-2.2.0+ds1/.github/workflows/main.yml 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/.github/workflows/main.yml 2022-05-29
12:46:55.000000000 -0400
@@ -16,12 +16,11 @@
container:
- 'registry.fedoraproject.org/fedora:latest'
- 'registry.fedoraproject.org/fedora:rawhide'
- - 'registry.opensuse.org/opensuse/leap-dnf:15.3'
- - 'registry.opensuse.org/opensuse/tumbleweed-dnf:latest'
+ - 'registry.opensuse.org/opensuse/tumbleweed:latest'
include:
- container: 'registry.fedoraproject.org/fedora:latest'
build-type: 'no-optional-deps'
- - container: 'registry.opensuse.org/opensuse/tumbleweed-
dnf:latest'
+ - container: 'registry.opensuse.org/opensuse/tumbleweed:latest'
build-type: 'no-optional-deps'
fail-fast: false
@@ -30,7 +29,7 @@
options: --security-opt seccomp=unconfined
steps:
- - run: dnf --assumeyes install
+ - run: zypper -n install
cpio gzip
bzip2 xz
binutils glibc glibc-32bit glibc-locale
@@ -49,21 +48,21 @@
python3-flake8-import-order
python3-flake8-quotes
python3-pyxdg
- python3-zstd
+ python3-zstandard
python3-toml
python3-pip
rpm-build
git
if: ${{ contains(matrix.container, 'opensuse') }}
- - run: dnf --assumeyes install
+ - run: zypper -n install
checkbashisms dash
desktop-file-utils
appstream-glib
myspell-en_US myspell-cs_CZ
python3-pyenchant
if: ${{ contains(matrix.container, 'opensuse') && matrix.build-type
== 'normal' }}
- - run: dnf --assumeyes install python3-flake8-comprehensions
- if: ${{ contains(matrix.container, 'opensuse/tumbleweed') }}
+ - run: zypper -n install python3-flake8-comprehensions
+ if: ${{ contains(matrix.container, 'opensuse') }}
- run: dnf --nogpgcheck --assumeyes install
/usr/bin/cpio
/usr/bin/bzip2
@@ -86,7 +85,7 @@
python3-flake8-import-order
python3-pyxdg
python3-toml
- python3-zstd
+ python3-zstandard
python3-pip
rpm-build
git
diff -Nru rpmlint-2.2.0+ds1/.packit/rpmlint.spec rpmlint-
2.3.0+ds1/.packit/rpmlint.spec
--- rpmlint-2.2.0+ds1/.packit/rpmlint.spec 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/.packit/rpmlint.spec 2022-05-29
12:46:55.000000000 -0400
@@ -1,7 +1,7 @@
%{!?python3: %global python3 %{__python3}}
Name: rpmlint
-Version: 2.2.0
+Version: 2.3.0
Release: 0%{?dist}
Summary: Tool for checking common errors in RPM packages
@@ -28,7 +28,7 @@
BuildRequires: python3-pyxdg
BuildRequires: python3-rpm
BuildRequires: python3-toml
-BuildRequires: python3-zstd
+BuildRequires: python3-zstandard
%else
BuildRequires: python3dist(setuptools)
# For tests
@@ -42,7 +42,7 @@
BuildRequires: python3dist(pyxdg)
BuildRequires: python3dist(rpm)
BuildRequires: python3dist(toml)
-BuildRequires: python3dist(zstd)
+BuildRequires: python3dist(zstandard)
%endif
# Rest of the test dependencies
diff -Nru rpmlint-2.2.0+ds1/README.md rpmlint-2.3.0+ds1/README.md
--- rpmlint-2.2.0+ds1/README.md 2021-12-12 09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/README.md 2022-05-29 12:46:55.000000000 -0400
@@ -5,6 +5,7 @@
[](https://lgtm.com/projects/g/rpm-software-management/rpmlint/alerts/
)
[](https://lgtm.com/projects/g/rpm-software-management/rpmlint/context:python
)
[](https://coveralls.io/github/rpm-software-management/rpmlint?branch=main
)
+[](https://repology.org/project/rpmlint/versions
)
`rpmlint` is a tool for checking common errors in RPM packages.
`rpmlint` can be used to test individual packages before uploading or to
check
@@ -26,8 +27,8 @@
For installation on your machine you will need the following packages:
Mandatory:
-- Python 3.6 or newer
-- python3-setuptools, python3-toml, python3-pyxdg, python3-beam
+- Python 3.8 or newer
+- python3-setuptools, python3-toml, python3-pyxdg, python3-pybeam
- rpm and its python bindings
- binutils, cpio, gzip, bzip, xz and zstd
@@ -121,8 +122,11 @@
setBadness('check', 0)
addFilter('test-i-ignore')
-The location of `rpmlintrc` can be set using `--rpmlintrc` option. Or you
can have any `*.rpmlintrc` or
-`*-rpmlintrc` file in the current working directory. The best practice is
to store the name in `$PACKAGE_NAME.rpmlintrc`.
+The location of `rpmlintrc` can be set using `--rpmlintrc` option.
+Or it can load any `*.rpmlintrc` or `*-rpmlintrc` that are located in the
same
+folder as check RPM file (or a specfile). Note the auto-loading happens
only
+when one RPM file (or a specfile) is used.
+The best practice is to store the name in `$PACKAGE_NAME.rpmlintrc`.
`setBadness` overrides a default badness for a given check and `addFilter`
ignores all errors
that match the given regular expression (one cannot filter out errors that
are listed in `BlockedFilters`
diff -Nru rpmlint-2.2.0+ds1/rpmlint/checks/AbstractCheck.py rpmlint-
2.3.0+ds1/rpmlint/checks/AbstractCheck.py
--- rpmlint-2.2.0+ds1/rpmlint/checks/AbstractCheck.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/checks/AbstractCheck.py 2022-05-29
12:46:55.000000000 -0400
@@ -43,6 +43,9 @@
if self.use_threads:
# NOTE: the speed benefit of the ThreadPoolExecutor is limited
due to
# Global Interpreter Lock (GIL).
+
+ # start with the biggest files first
+ filenames = sorted(filenames, key=lambda x: pkg.files[x].size,
reverse=True)
with concurrent.futures.ThreadPoolExecutor() as executor:
futures = []
for filename in filenames:
diff -Nru rpmlint-2.2.0+ds1/rpmlint/checks/AlternativesCheck.py rpmlint-
2.3.0+ds1/rpmlint/checks/AlternativesCheck.py
--- rpmlint-2.2.0+ds1/rpmlint/checks/AlternativesCheck.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/checks/AlternativesCheck.py 2022-05-29
12:46:55.000000000 -0400
@@ -30,11 +30,6 @@
def __init__(self, config, output):
super().__init__(config, output)
- # Containers for scriptlets as they will be used on multiple places
- self.post = None
- self.postun = None
- self.install_binaries = {}
- self.slave_binaries = []
def check(self, pkg):
if pkg.is_source:
@@ -45,6 +40,8 @@
self._check_libalternatives_requirements(pkg)
self._check_libalternatives_filelist(pkg)
+ self.install_binaries = {}
+ self.slave_binaries = []
# populate scriptlets
self.post = byte_to_string(pkg.header[rpm.RPMTAG_POSTIN])
self.postun = byte_to_string(pkg.header[rpm.RPMTAG_POSTUN])
diff -Nru rpmlint-2.2.0+ds1/rpmlint/checks/AppDataCheck.py rpmlint-
2.3.0+ds1/rpmlint/checks/AppDataCheck.py
--- rpmlint-2.2.0+ds1/rpmlint/checks/AppDataCheck.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/checks/AppDataCheck.py 2022-05-29
12:46:55.000000000 -0400
@@ -17,7 +17,7 @@
super().__init__(config, output,
r'/usr/share/appdata/.*\.(appdata|metainfo).xml$')
def check_file(self, pkg, filename):
- root = pkg.dirName()
+ root = pkg.dir_name()
f = root + filename
cmd = self.cmd + f
diff -Nru rpmlint-2.2.0+ds1/rpmlint/checks/BashismsCheck.py rpmlint-
2.3.0+ds1/rpmlint/checks/BashismsCheck.py
--- rpmlint-2.2.0+ds1/rpmlint/checks/BashismsCheck.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/checks/BashismsCheck.py 2022-05-29
12:46:55.000000000 -0400
@@ -10,16 +10,16 @@
super().__init__(config, output, r'.*')
self.use_threads = True
self._detect_early_fail_option()
+ self.file_cache = {}
def _detect_early_fail_option(self):
- self.has_early_fail_option = False
output = subprocess.check_output(['checkbashisms', '--help'],
shell=True, encoding='utf8')
# FIXME: remove in the future
self.use_early_fail = '[-e]' in output
def check_file(self, pkg, filename):
- root = pkg.dirName()
+ root = pkg.dir_name()
pkgfile = pkg.files[filename]
filepath = root + filename
@@ -28,7 +28,14 @@
pkgfile.magic.startswith('POSIX shell script')):
return
- self.check_bashisms(pkg, filepath, filename)
+ # There are package likes Linux kernel where there are common
+ # shell scripts present in multiple packages
+ # (kernel-source, kernel-source-vanilla).
+ if pkgfile.md5 not in self.file_cache:
+ self.file_cache[pkgfile.md5] = list(self.check_bashisms(pkg,
filepath, filename))
+
+ for warning in self.file_cache[pkgfile.md5]:
+ self.output.add_info('W', pkg, warning, filename)
def check_bashisms(self, pkg, filepath, filename):
"""
@@ -36,13 +43,14 @@
We need to see if it is valid syntax of bash and if there are no
potential bash issues.
+ Return a warning message or None if there is no problem.
"""
try:
r = subprocess.run(['dash', '-n', filepath],
stderr=subprocess.DEVNULL,
env=ENGLISH_ENVIROMENT)
if r.returncode == 2:
- self.output.add_info('W', pkg, 'bin-sh-syntax-error',
filename)
+ yield 'bin-sh-syntax-error'
elif r.returncode == 127:
raise FileNotFoundError(filename)
except UnicodeDecodeError:
@@ -57,7 +65,7 @@
stderr=subprocess.DEVNULL,
env=ENGLISH_ENVIROMENT)
if r.returncode == 1:
- self.output.add_info('W', pkg, 'potential-bashisms',
filename)
+ yield 'potential-bashisms'
elif r.returncode == 2:
raise FileNotFoundError(filename)
except UnicodeDecodeError:
diff -Nru rpmlint-2.2.0+ds1/rpmlint/checks/BinariesCheck.py rpmlint-
2.3.0+ds1/rpmlint/checks/BinariesCheck.py
--- rpmlint-2.2.0+ds1/rpmlint/checks/BinariesCheck.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/checks/BinariesCheck.py 2022-05-29
12:46:55.000000000 -0400
@@ -3,7 +3,6 @@
import re
import stat
-import rpm
from rpmlint.arparser import ArParser
from rpmlint.checks.AbstractCheck import AbstractCheck
from rpmlint.lddparser import LddParser
@@ -20,7 +19,6 @@
"""
Checks for binary files in the package.
"""
- srcname_regex = re.compile(r'(.*?)-[0-9]')
validso_regex = re.compile(r'(\.so\.\d+(\.\d+)*|\d\.so)$')
soversion_regex = re.compile(r'.*?(-(?P<pkgversion>[0-9][.0-
9]*))?\.so(\.(?P<soversion>[0-9][.0-9]*))?')
usr_lib_regex = re.compile(r'^/usr/lib(64)?/')
@@ -33,18 +31,21 @@
la_file_regex = re.compile(r'\.la$')
invalid_dir_ref_regex = re.compile(r'/(home|tmp)(\W|$)')
usr_arch_share_regex =
re.compile(r'/share/.*/(?:x86|i.86|x86_64|ppc|ppc64|s390|s390x|ia64|m68k|arm
|aarch64|mips|riscv)')
+ python_module_regex = re.compile(r'.*\.\w*(python|pypy)\w*(-
\w+){4}\.so')
lto_text_like_sections = {'.preinit_array', '.init_array',
'.fini_array'}
# The following sections are part of the RX ABI and do correspond to
.text, .data and .bss
lto_text_like_sections |= {'P', 'D_1', 'B_1'}
# The list is taken from glibc: sysdeps/${arch}/stackinfo.h
- default_executable_stack_archs =
re.compile(r'alpha|arm.*|hppa|i.86|m68k|microblaze|mips|ppc64|ppc64le|s390|s
390x|sh|sparc|x86_64')
+ default_executable_stack_archs =
re.compile(r'alpha|arm.*|hppa|i.86|m68k|microblaze|mips|ppc|s390|s390x|sh|sp
arc|x86_64')
+
+ rpath_origin = '$ORIGIN'
def __init__(self, config, output):
super().__init__(config, output)
self.checked_files = 0
- self.system_lib_paths = config.configuration['SystemLibPaths']
+ self.system_lib_paths =
tuple(config.configuration['SystemLibPaths'])
self.pie_exec_regex_list = []
for regex in config.configuration['PieExecutables']:
self.pie_exec_regex_list.append(re.compile(regex))
@@ -108,10 +109,10 @@
If so then print a corresponding error with the matching line
numbers.
"""
if self.la_file_regex.search(fname):
- lines = pkg.grep(self.invalid_dir_ref_regex, fname)
- if lines:
+ line = pkg.grep(self.invalid_dir_ref_regex, fname)
+ if line:
self.output.add_info('E', pkg, 'invalid-la-file', fname,
- '(line %s)' % ', '.join(lines))
+ f'(line {line})')
def _check_binary_in_noarch(self, pkg, bin_name):
"""
@@ -197,13 +198,13 @@
not self.versioned_dir_regex.search(fn):
self.output.add_info('E', pkg, 'non-versioned-file-in-
library-package', f)
- def _check_no_binary(self, pkg, has_binary, multi_pkg,
has_file_in_lib64):
+ def _check_no_binary(self, pkg, has_binary, has_file_in_lib64):
"""
Check if the arch dependent package contains any binaries.
Print an error if there is no binary and it's not noarch.
"""
- if not has_binary and not multi_pkg and not has_file_in_lib64 and \
+ if not has_binary and not has_file_in_lib64 and \
pkg.arch != 'noarch':
self.output.add_info('E', pkg, 'no-binary')
@@ -226,7 +227,7 @@
if has_usr_lib_file and not has_binary_in_usr_lib:
self.output.add_info('W', pkg, 'only-non-binary-in-usr-lib')
- def _check_no_text_in_archive(self, pkg, pkgfile_path, path):
+ def _check_no_text_in_archive(self, pkg, pkgfile):
"""
For an archive, test if any .text sections is non-empty.
"""
@@ -238,7 +239,7 @@
# Starting with glibc 2.34, some static libraries were moved to
libc
# and there are empty archives for backward compatibility. Skip
these
# libraries.
- stem = Path(path).stem
+ stem = Path(pkgfile.name).stem
if stem in GLIBC_EMPTY_ARCHIVES or (stem.endswith('_p') and
stem[:-2] in GLIBC_EMPTY_ARCHIVES):
return
@@ -250,10 +251,10 @@
sn.startswith('.data')) and
section.size > 0):
return
- self.output.add_info('E', pkg, 'lto-no-text-in-archive', path)
+ self.output.add_info('E', pkg, 'lto-no-text-in-archive',
pkgfile.name)
return
- def _check_missing_symtab_in_archive(self, pkg, pkgfile_path, path):
+ def _check_missing_symtab_in_archive(self, pkg, pkgfile):
"""
FIXME Add test coverage.
"""
@@ -263,29 +264,27 @@
if section.name == '.symtab':
return
- self.output.add_info('E', pkg, 'static-library-without-symtab',
path)
+ self.output.add_info('E', pkg, 'static-library-without-symtab',
pkgfile.name)
- def _check_missing_debug_info_in_archive(self, pkg, pkgfile_path,
path):
+ def _check_missing_debug_info_in_archive(self, pkg, pkgfile):
if self.is_archive:
for elf_file in self.readelf_parser.section_info.elf_files:
for section in elf_file:
if section.name.startswith('.debug_'):
return
- self.output.add_info('E', pkg, 'static-library-without-
debuginfo', path)
+ self.output.add_info('E', pkg, 'static-library-without-
debuginfo', pkgfile.name)
# Check for LTO sections
- def _check_lto_section(self, pkg, pkgfile_path, path):
+ def _check_lto_section(self, pkg, pkgfile):
for elf_file in self.readelf_parser.section_info.elf_files:
for section in elf_file:
if '.gnu.lto_.' in section.name:
- self.output.add_info('E', pkg, 'lto-bytecode', path)
+ self.output.add_info('E', pkg, 'lto-bytecode',
pkgfile.name)
return
- def _check_executable_stack(self, pkg, pkgfile_path, path):
+ def _check_executable_stack(self, pkg, pkgfile):
"""
Check if the stack is declared as executable which is usually an
error.
-
- FIXME Add test coverage.
"""
# Skip architectures that have non-executable stack by default
@@ -293,12 +292,12 @@
return
# Do not check kernel modules and archives
- if not self.is_archive and not any(path.startswith(p) for p in
KERNEL_MODULES_PATHS):
+ if not self.is_archive and not any(pkgfile.name.startswith(p) for p
in KERNEL_MODULES_PATHS):
stack_headers = [h for h in
self.readelf_parser.program_header_info.headers if h.name == 'GNU_STACK']
if not stack_headers:
- self.output.add_info('E', pkg, 'missing-PT_GNU_STACK-
section', path)
+ self.output.add_info('E', pkg, 'missing-PT_GNU_STACK-
section', pkgfile.name)
elif 'E' in stack_headers[0].flags:
- self.output.add_info('E', pkg, 'executable-stack', path)
+ self.output.add_info('E', pkg, 'executable-stack',
pkgfile.name)
def _check_soname_symlink(self, pkg, shlib, soname):
"""
@@ -319,7 +318,7 @@
if path.name.startswith('lib') or path.name.startswith('ld-'):
self.output.add_info('E', pkg, 'no-ldconfig-symlink',
shlib)
- def _check_shared_library(self, pkg, pkgfile_path, path):
+ def _check_shared_library(self, pkg, pkgfile):
"""
Various checks for the shared library.
@@ -335,12 +334,12 @@
soname = self.readelf_parser.dynamic_section_info.soname
if not soname:
- self.output.add_info('W', pkg, 'no-soname', path)
+ self.output.add_info('W', pkg, 'no-soname', pkgfile.name)
else:
if not self.validso_regex.search(soname):
- self.output.add_info('E', pkg, 'invalid-soname', path,
soname)
+ self.output.add_info('E', pkg, 'invalid-soname',
pkgfile.name, soname)
else:
- self._check_soname_symlink(pkg, path, soname)
+ self._check_soname_symlink(pkg, pkgfile.name, soname)
# check if the major version of the library is in the
package
# name (check only for lib* packages)
@@ -362,9 +361,9 @@
# check if the object code in the library is compiled with PIC
if self.readelf_parser.dynamic_section_info['TEXTREL']:
- self.output.add_info('E', pkg, 'shlib-with-non-pic-code', path)
+ self.output.add_info('E', pkg, 'shlib-with-non-pic-code',
pkgfile.name)
- def _check_dependency(self, pkg, pkgfile_path, path):
+ def _check_dependency(self, pkg, pkgfile):
"""
FIXME Add test coverage.
"""
@@ -375,15 +374,20 @@
# following issues are errors for shared libs and warnings for
executables
if not self.is_dynamically_linked:
return
+
+ # Skip python packages
+ if self.python_module_regex.fullmatch(pkgfile.name):
+ return
+
if not self.is_archive and not self.readelf_parser.is_debug:
info_type = 'E' if self.readelf_parser.is_shlib else 'W'
for symbol in self.ldd_parser.undefined_symbols:
- self.output.add_info(info_type, pkg, 'undefined-non-weak-
symbol', path, symbol)
+ self.output.add_info(info_type, pkg, 'undefined-non-weak-
symbol', pkgfile.name, symbol)
for dependency in self.ldd_parser.unused_dependencies:
self.output.add_info(info_type, pkg, 'unused-direct-shlib-
dependency',
- path, dependency)
+ pkgfile.name, dependency)
- def _check_library_dependency_location(self, pkg, pkgfile_path, path):
+ def _check_library_dependency_location(self, pkg, pkgfile):
"""
FIXME Add test coverage.
"""
@@ -393,17 +397,17 @@
if not self.is_archive:
for dependency in self.ldd_parser.dependencies:
if dependency.startswith('/opt/'):
- self.output.add_info('E', pkg, 'linked-against-opt-
library', path, dependency)
+ self.output.add_info('E', pkg, 'linked-against-opt-
library', pkgfile.name, dependency)
break
nonusr = ('/bin', '/lib', '/sbin')
- if path.startswith(nonusr):
+ if pkgfile.name.startswith(nonusr):
for dependency in self.ldd_parser.dependencies:
if dependency.startswith('/usr/'):
- self.output.add_info('W', pkg, 'linked-against-usr-
library', path, dependency)
+ self.output.add_info('W', pkg, 'linked-against-usr-
library', pkgfile.name, dependency)
break
- def _check_security_functions(self, pkg, pkgfile_path, path):
+ def _check_security_functions(self, pkg, pkgfile):
setgid =
any(self.readelf_parser.symbol_table_info.get_functions_for_regex(self.setgi
d_call_regex))
setuid =
any(self.readelf_parser.symbol_table_info.get_functions_for_regex(self.setui
d_call_regex))
setgroups =
any(self.readelf_parser.symbol_table_info.get_functions_for_regex(self.setgr
oups_call_regex))
@@ -411,24 +415,31 @@
gethostbyname =
any(self.readelf_parser.symbol_table_info.get_functions_for_regex(self.getho
stbyname_call_regex))
if setgid and setuid and not setgroups:
- self.output.add_info('E', pkg, 'missing-call-to-setgroups-
before-setuid', path)
+ is_uid = stat.S_ISUID & pkgfile.mode
+ self.output.add_info('W' if is_uid else 'E', pkg, 'missing-
call-to-setgroups-before-setuid', pkgfile.name)
if mktemp:
- self.output.add_info('E', pkg, 'call-to-mktemp', path)
+ self.output.add_info('E', pkg, 'call-to-mktemp', pkgfile.name)
if gethostbyname:
- self.output.add_info('W', pkg, 'binary-or-shlib-calls-
gethostbyname', path)
+ self.output.add_info('W', pkg, 'binary-or-shlib-calls-
gethostbyname', pkgfile.name)
- def _check_rpath(self, pkg, pkgfile_path, path):
- for runpath in self.readelf_parser.dynamic_section_info.runpath:
- if runpath in self.system_lib_paths or not
self.usr_lib_regex.search(runpath):
- self.output.add_info('E', pkg, 'binary-or-shlib-defines-
rpath', path, runpath)
- return
+ def _check_rpath(self, pkg, pkgfile):
+ for runpaths in self.readelf_parser.dynamic_section_info.runpaths:
+ for runpath in runpaths.split(':'):
+ if self.rpath_origin in runpath:
+ runpath = runpath.replace(self.rpath_origin,
str(Path(pkgfile.name).parent))
+ runpath = str(Path(runpath).resolve())
+ if not runpath.startswith(self.system_lib_paths) and not
self.usr_lib_regex.search(runpath):
+ self.output.add_info('E', pkg, 'binary-or-shlib-
defines-rpath', pkgfile.name, runpath)
+ return
- def _check_library_dependency(self, pkg, pkgfile_path, path):
+ def _check_library_dependency(self, pkg, pkgfile):
if self.is_archive:
return
- if any(path.startswith(p) for p in KERNEL_MODULES_PATHS):
+ elif any(pkgfile.name.startswith(p) for p in KERNEL_MODULES_PATHS):
+ return
+ elif self.python_module_regex.fullmatch(pkgfile.name):
return
dyn_section = self.readelf_parser.dynamic_section_info
@@ -438,10 +449,10 @@
msg = 'shared-library-without-dependency-information'
else:
msg = 'statically-linked-binary'
- self.output.add_info('E', pkg, msg, path)
+ self.output.add_info('E', pkg, msg, pkgfile.name)
else:
# linked against libc ?
- if 'libc.' not in dyn_section.runpath and \
+ if 'libc.' not in dyn_section.runpaths and \
(not dyn_section.soname or
('libc.' not in dyn_section.soname and
not self.ldso_soname_regex.search(dyn_section.soname))):
@@ -452,9 +463,9 @@
msg = 'library-not-linked-against-libc'
else:
msg = 'program-not-linked-against-libc'
- self.output.add_info('W', pkg, msg, path)
+ self.output.add_info('W', pkg, msg, pkgfile.name)
- def _check_forbidden_functions(self, pkg, pkgfile_path, path):
+ def _check_forbidden_functions(self, pkg, pkgfile):
forbidden_functions = self.config.configuration['WarnOnFunction']
if forbidden_functions:
for name, func in forbidden_functions.items():
@@ -474,10 +485,10 @@
if not forbidden_calls:
return
- strings_parser = StringsParser(pkgfile_path)
+ strings_parser = StringsParser(pkgfile.path)
failed_reason = strings_parser.parsing_failed_reason
if failed_reason:
- self.output.add_info('E', pkg, 'strings-failed', path,
failed_reason)
+ self.output.add_info('E', pkg, 'strings-failed', pkgfile.name,
failed_reason)
return
forbidden_functions_filtered = []
@@ -493,15 +504,13 @@
forbidden_functions_filtered.append(fn)
for fn in forbidden_functions_filtered:
- self.output.add_info('W', pkg, fn, path,
forbidden_functions[fn]['f_name'])
+ self.output.add_info('W', pkg, fn, pkgfile.name,
forbidden_functions[fn]['f_name'])
- def _check_executable_shlib(self, pkg, pkgfile_path, path):
- if not self.is_exec and self.readelf_parser.is_shlib:
- interp = [h for h in
self.readelf_parser.program_header_info.headers if h.name == 'INTERP']
- if interp:
- self.output.add_info('E', pkg, 'shared-library-not-
executable', path)
+ def _check_executable_shlib(self, pkg, pkgfile):
+ if not (pkgfile.mode & stat.S_IEXEC) and
self.readelf_parser.is_shlib:
+ self.output.add_info('E', pkg, 'shared-library-not-executable',
pkgfile.name)
- def _check_optflags(self, pkg, pkgfile_path, path):
+ def _check_optflags(self, pkg, pkgfile):
if self.is_archive:
return
@@ -515,21 +524,21 @@
missing = [mo for mo in mandatory_optflags if mo not in tokens]
forbidden = [f for f in forbidden_optflags if f in tokens]
if missing:
- self.output.add_info('W', pkg, 'missing-mandatory-
optflags', path, ' '.join(missing))
+ self.output.add_info('W', pkg, 'missing-mandatory-
optflags', pkgfile.name, ' '.join(missing))
if forbidden:
- self.output.add_info('E', pkg, 'forbidden-optflags', path,
' '.join(forbidden))
+ self.output.add_info('E', pkg, 'forbidden-optflags',
pkgfile.name, ' '.join(forbidden))
- def _is_standard_archive(self, pkg, pkgfile_path, path):
+ def _is_standard_archive(self, pkg, pkgfile):
# skip Klee bytecode archives
- if pkgfile_path.endswith('.bca'):
+ if pkgfile.path.endswith('.bca'):
return False
# return false for e.g. Rust or Go packages that are archives
# but files in the archive are not an ELF container
- ar_parser = ArParser(pkgfile_path)
+ ar_parser = ArParser(pkgfile.path)
failed_reason = ar_parser.parsing_failed_reason
if failed_reason:
- self.output.add_info('E', pkg, 'ar-failed', path,
failed_reason)
+ self.output.add_info('E', pkg, 'ar-failed', pkgfile.name,
failed_reason)
return False
needles = ('__.PKGDEF', '_go_.o', 'lib.rmeta')
@@ -543,38 +552,40 @@
self.is_pie_exec = 'pie executable' in magic
self.is_nonstandard_archive = False
- def run_elf_checks(self, pkg, pkgfile_path, path):
- if self.is_archive and not self._is_standard_archive(pkg,
pkgfile_path, path):
+ def run_elf_checks(self, pkg, pkgfile):
+ if self.is_archive and not self._is_standard_archive(pkg, pkgfile):
self.is_nonstandard_archive = True
return
- self.readelf_parser = ReadelfParser(pkgfile_path, path)
+ self.readelf_parser = ReadelfParser(pkgfile.path, pkgfile.name)
failed_reason = self.readelf_parser.parsing_failed_reason()
if failed_reason:
- self.output.add_info('E', pkg, 'readelf-failed', path,
failed_reason)
+ self.output.add_info('E', pkg, 'readelf-failed', pkgfile.name,
failed_reason)
return
if not self.is_archive:
if self.is_dynamically_linked:
is_installed_pkg = isinstance(pkg, InstalledPkg) or
isinstance(pkg, FakePkg)
- self.ldd_parser = LddParser(pkgfile_path, path,
is_installed_pkg)
+ self.ldd_parser = LddParser(pkgfile.path, pkgfile.name,
is_installed_pkg)
failed_reason = self.ldd_parser.parsing_failed_reason
if failed_reason:
- self.output.add_info('E', pkg, 'ldd-failed', path,
failed_reason)
+ self.output.add_info('E', pkg, 'ldd-failed',
pkgfile.name, failed_reason)
return
- self.objdump_parser = ObjdumpParser(pkgfile_path, path)
- failed_reason = self.objdump_parser.parsing_failed_reason
- if failed_reason:
- self.output.add_info('E', pkg, 'objdump-failed', path,
failed_reason)
- return
+ if (self.config.configuration['MandatoryOptflags'] or
+ self.config.configuration['ForbiddenOptflags']):
+ self.objdump_parser = ObjdumpParser(pkgfile.path,
pkgfile.name)
+ failed_reason = self.objdump_parser.parsing_failed_reason
+ if failed_reason:
+ self.output.add_info('E', pkg, 'objdump-failed',
pkgfile.name, failed_reason)
+ return
# NOTE: the speed benefit of the ThreadPoolExecutor is limited due
to
# Global Interpreter Lock (GIL).
with concurrent.futures.ThreadPoolExecutor() as executor:
futures = []
for fn in self.check_functions:
- futures.append(executor.submit(fn, pkg, pkgfile_path,
path))
+ futures.append(executor.submit(fn, pkg, pkgfile))
concurrent.futures.wait(futures)
for future in futures:
err = future.exception()
@@ -583,7 +594,6 @@
def check_binary(self, pkg):
exec_files = []
- multi_pkg = False
pkg_has_lib = False
pkg_has_binary = False
pkg_has_binary_in_usrlib = False
@@ -593,7 +603,6 @@
# go through the all files, run files checks and collect data that
are
# needed later
for fname, pkgfile in pkg.files.items():
-
# Common tests first
self._check_libtool_wrapper(pkg, fname, pkgfile)
self._check_invalid_la_file(pkg, fname)
@@ -658,7 +667,7 @@
self._detect_attributes(pkgfile.magic)
# run ELF checks
- self.run_elf_checks(pkg, pkgfile.path, fname)
+ self.run_elf_checks(pkg, pkgfile)
if self.is_nonstandard_archive:
continue
@@ -686,19 +695,11 @@
self._check_non_pie(pkg, fname)
- # find out if we have a multi-package
- srpm = pkg[rpm.RPMTAG_SOURCERPM]
- if srpm:
- srcname = self.srcname_regex.search(srpm)
- if srcname:
- multi_pkg = (pkg.name != srcname.group(1))
-
# run checks for the whole package
# it uses data collected in the previous for-cycle
self._check_exec_in_library(pkg, pkg_has_lib, exec_files)
self._check_non_versioned(pkg, pkg_has_lib, exec_files)
- self._check_no_binary(pkg, pkg_has_binary, multi_pkg,
- pkg_has_file_in_lib64)
+ self._check_no_binary(pkg, pkg_has_binary, pkg_has_file_in_lib64)
self._check_noarch_with_lib64(pkg, pkg_has_file_in_lib64)
self._check_only_non_binary_in_usrlib(pkg, pkg_has_usrlib_file,
pkg_has_binary_in_usrlib)
diff -Nru rpmlint-2.2.0+ds1/rpmlint/checks/BuildDateCheck.py rpmlint-
2.3.0+ds1/rpmlint/checks/BuildDateCheck.py
--- rpmlint-2.2.0+ds1/rpmlint/checks/BuildDateCheck.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/checks/BuildDateCheck.py 1969-12-31
19:00:00.000000000 -0500
@@ -1,32 +0,0 @@
-import re
-import stat
-import time
-
-from rpmlint.checks.AbstractCheck import AbstractFilesCheck
-
-
-class BuildDateCheck(AbstractFilesCheck):
- """
- Check that the file doesn't contain the current date or time.
-
- If so, it causes the package to rebuild when it's not needed.
- """
- def __init__(self, config, output):
- super().__init__(config, output, r'.*')
- self.looksliketime = re.compile('(2[0-3]|[01]?[0-9]):([0-5]?[0-
9]):([0-5]?[0-9])')
- self.istoday = re.compile(time.strftime('%b %e %Y'))
-
- def check_file(self, pkg, filename):
- if filename.startswith('/usr/lib/debug') or pkg.is_source or \
- not stat.S_ISREG(pkg.files[filename].mode):
- return
-
- grep_date = pkg.grep(self.istoday, filename)
-
- if len(grep_date):
- grep_time = pkg.grep(self.looksliketime, filename)
-
- if len(grep_time):
- self.output.add_info('E', pkg, 'file-contains-date-and-
time', filename)
- else:
- self.output.add_info('E', pkg, 'file-contains-current-
date', filename)
diff -Nru rpmlint-2.2.0+ds1/rpmlint/checks/BuildRootAndDateCheck.py rpmlint-
2.3.0+ds1/rpmlint/checks/BuildRootAndDateCheck.py
--- rpmlint-2.2.0+ds1/rpmlint/checks/BuildRootAndDateCheck.py 1969-12-31
19:00:00.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/checks/BuildRootAndDateCheck.py 2022-05-29
12:46:55.000000000 -0400
@@ -0,0 +1,39 @@
+import re
+import stat
+import time
+
+import rpm
+from rpmlint.checks.AbstractCheck import AbstractFilesCheck
+
+
+class BuildRootAndDateCheck(AbstractFilesCheck):
+ """
+ Check that the file doesn't contain the current date or time.
+ And check the file does not contain build root reference.
+
+ If so, it causes the package to rebuild when it's not needed.
+ """
+ def __init__(self, config, output):
+ super().__init__(config, output, r'.*')
+ self.looksliketime = re.compile('(2[0-3]|[01]?[0-9]):([0-5]?[0-
9]):([0-5]?[0-9])')
+ self.istoday = re.compile(time.strftime('%b %e %Y'))
+ self.prepare_regex(rpm.expandMacro('%buildroot'))
+
+ def prepare_regex(self, buildroot):
+ for m in ('name', 'version', 'release', 'NAME', 'VERSION',
'RELEASE'):
+ buildroot = buildroot.replace('%%{%s}' % (m), r'[\w\!-
\.]{1,20}')
+ self.build_root_re = re.compile(buildroot)
+
+ def check_file(self, pkg, filename):
+ if filename.startswith('/usr/lib/debug') or pkg.is_source or \
+ not stat.S_ISREG(pkg.files[filename].mode):
+ return
+
+ data = pkg.read_with_mmap(filename)
+ if self.istoday.search(data):
+ if self.looksliketime.search(data):
+ self.output.add_info('E', pkg, 'file-contains-date-and-
time', filename)
+ else:
+ self.output.add_info('E', pkg, 'file-contains-current-
date', filename)
+ if self.build_root_re.search(data):
+ self.output.add_info('E', pkg, 'file-contains-buildroot',
filename)
diff -Nru rpmlint-2.2.0+ds1/rpmlint/checks/BuildRootCheck.py rpmlint-
2.3.0+ds1/rpmlint/checks/BuildRootCheck.py
--- rpmlint-2.2.0+ds1/rpmlint/checks/BuildRootCheck.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/checks/BuildRootCheck.py 1969-12-31
19:00:00.000000000 -0500
@@ -1,25 +0,0 @@
-import re
-import stat
-
-import rpm
-from rpmlint.checks.AbstractCheck import AbstractFilesCheck
-
-
-class BuildRootCheck(AbstractFilesCheck):
- def __init__(self, config, output):
- super().__init__(config, output, r'.*')
- self.prepare_regex(rpm.expandMacro('%buildroot'))
-
- def prepare_regex(self, buildroot):
- for m in ('name', 'version', 'release', 'NAME', 'VERSION',
'RELEASE'):
- buildroot = buildroot.replace('%%{%s}' % (m), r'[\w\!-
\.]{1,20}')
- self.build_root_re = re.compile(buildroot)
-
- def check_file(self, pkg, filename):
- if filename.startswith('/usr/lib/debug') or pkg.is_source:
- return
- if not stat.S_ISREG(pkg.files[filename].mode):
- return
-
- if pkg.grep(self.build_root_re, filename):
- self.output.add_info('E', pkg, 'file-contains-buildroot',
filename)
diff -Nru rpmlint-2.2.0+ds1/rpmlint/checks/DBusPolicyCheck.py rpmlint-
2.3.0+ds1/rpmlint/checks/DBusPolicyCheck.py
--- rpmlint-2.2.0+ds1/rpmlint/checks/DBusPolicyCheck.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/checks/DBusPolicyCheck.py 2022-05-29
12:46:55.000000000 -0400
@@ -18,7 +18,7 @@
try:
if any(f.startswith(d) for d in DBUS_DIRECTORIES):
send_policy_seen = False
- lf = pkg.dirName() + f
+ lf = pkg.dir_name() + f
xml = parse(lf)
for policy in xml.getElementsByTagName('policy'):
send_policy_seen =
self._check_allow_policy_element(pkg, f, policy) or send_policy_seen
diff -Nru rpmlint-2.2.0+ds1/rpmlint/checks/ErlangCheck.py rpmlint-
2.3.0+ds1/rpmlint/checks/ErlangCheck.py
--- rpmlint-2.2.0+ds1/rpmlint/checks/ErlangCheck.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/checks/ErlangCheck.py 2022-05-29
12:46:55.000000000 -0400
@@ -20,7 +20,7 @@
self.output.add_info('W', pkg, 'beam-compile-info-missed',
filename)
return
- compile_state = byte_to_string(str(beam.compileinfo['source']))
+ compile_state = byte_to_string(beam.compileinfo['source'])
if 'debug_info' not in beam.compileinfo['options']:
self.output.add_info('E', pkg, 'beam-compiled-without-
debuginfo', filename)
diff -Nru rpmlint-2.2.0+ds1/rpmlint/checks/FilesCheck.py rpmlint-
2.3.0+ds1/rpmlint/checks/FilesCheck.py
--- rpmlint-2.2.0+ds1/rpmlint/checks/FilesCheck.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/checks/FilesCheck.py 2022-05-29
12:46:55.000000000 -0400
@@ -327,7 +327,6 @@
class FilesCheck(AbstractCheck):
-
man_regex = re.compile(r'/man(?:\d[px]?|n)/')
info_regex = re.compile(r'(/usr/share|/usr)/info/')
@@ -421,7 +420,6 @@
return (chunk, istext)
def check(self, pkg):
-
for filename in pkg.header[rpm.RPMTAG_FILENAMES] or ():
if not is_utf8_bytestr(filename):
self.output.add_info('E', pkg, 'filename-not-utf8',
byte_to_string(filename))
@@ -588,7 +586,6 @@
# normal file check
if stat.S_ISREG(mode):
-
# set[ug]id bit check
if stat.S_ISGID & mode or stat.S_ISUID & mode:
if stat.S_ISUID & mode:
diff -Nru rpmlint-2.2.0+ds1/rpmlint/checks/InitScriptCheck.py rpmlint-
2.3.0+ds1/rpmlint/checks/InitScriptCheck.py
--- rpmlint-2.2.0+ds1/rpmlint/checks/InitScriptCheck.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/checks/InitScriptCheck.py 2022-05-29
12:46:55.000000000 -0400
@@ -60,7 +60,6 @@
class InitScriptCheck(AbstractCheck):
-
def __init__(self, config, output):
super().__init__(config, output)
self.use_deflevels =
self.config.configuration['UseDefaultRunlevels']
@@ -69,7 +68,6 @@
def check_binary(self, pkg):
initscript_list = []
for fname, pkgfile in pkg.files.items():
-
if not fname.startswith('/etc/init.d/') and \
not fname.startswith('/etc/rc.d/init.d/'):
continue
diff -Nru rpmlint-2.2.0+ds1/rpmlint/checks/LogrotateCheck.py rpmlint-
2.3.0+ds1/rpmlint/checks/LogrotateCheck.py
--- rpmlint-2.2.0+ds1/rpmlint/checks/LogrotateCheck.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/checks/LogrotateCheck.py 2022-05-29
12:46:55.000000000 -0400
@@ -17,7 +17,7 @@
if f.startswith('/etc/logrotate.d/'):
try:
- for n, o in self.parselogrotateconf(pkg.dirName(),
f).items():
+ for n, o in self.parselogrotateconf(pkg.dir_name(),
f).items():
if n in dirs and dirs[n] != o:
self.output.add_info('E', pkg, 'logrotate-
duplicate', n)
else:
diff -Nru rpmlint-2.2.0+ds1/rpmlint/checks/MenuCheck.py rpmlint-
2.3.0+ds1/rpmlint/checks/MenuCheck.py
--- rpmlint-2.2.0+ds1/rpmlint/checks/MenuCheck.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/checks/MenuCheck.py 2022-05-29
12:46:55.000000000 -0400
@@ -31,7 +31,6 @@
class MenuCheck(AbstractCheck):
-
def __init__(self, config, output):
super().__init__(config, output)
self.valid_sections =
self.config.configuration['ValidMenuSections']
@@ -93,7 +92,7 @@
elif not update_menus_regex.search(postun):
self.output.add_info('E', pkg, 'postun-without-update-
menus')
- directory = pkg.dirName()
+ directory = pkg.dir_name()
for f in menus:
# remove comments and handle cpp continuation lines
text = subprocess.run(('/lib/cpp', directory + f),
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
env=ENGLISH_ENVIROMENT).stdout.decode()
diff -Nru rpmlint-2.2.0+ds1/rpmlint/checks/MenuXDGCheck.py rpmlint-
2.3.0+ds1/rpmlint/checks/MenuXDGCheck.py
--- rpmlint-2.2.0+ds1/rpmlint/checks/MenuXDGCheck.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/checks/MenuXDGCheck.py 2022-05-29
12:46:55.000000000 -0400
@@ -42,7 +42,7 @@
self._has_binary(pkg, root, cfp, filename)
def check_file(self, pkg, filename):
- root = pkg.dirName()
+ root = pkg.dir_name()
f = root + filename
try:
command = subprocess.run(('desktop-file-validate', f),
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=ENGLISH_ENVIROMENT)
diff -Nru rpmlint-2.2.0+ds1/rpmlint/checks/PkgConfigCheck.py rpmlint-
2.3.0+ds1/rpmlint/checks/PkgConfigCheck.py
--- rpmlint-2.2.0+ds1/rpmlint/checks/PkgConfigCheck.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/checks/PkgConfigCheck.py 2022-05-29
12:46:55.000000000 -0400
@@ -27,7 +27,7 @@
return
try:
- with open(pkg.dirName() + '/' + filename, 'r', encoding='utf-
8') as pc_file:
+ with open(pkg.dir_name() + '/' + filename, 'r', encoding='utf-
8') as pc_file:
for line in pc_file:
self._check_invalid_pkgconfig_file(pkg, filename, line)
self._check_invalid_libs_dir(pkg, filename, line)
diff -Nru rpmlint-2.2.0+ds1/rpmlint/checks/PostCheck.py rpmlint-
2.3.0+ds1/rpmlint/checks/PostCheck.py
--- rpmlint-2.2.0+ds1/rpmlint/checks/PostCheck.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/checks/PostCheck.py 2022-05-29
12:46:55.000000000 -0400
@@ -71,7 +71,6 @@
class PostCheck(AbstractCheck):
-
def __init__(self, config, output):
super().__init__(config, output)
self.valid_shells = config.configuration['ValidShells']
diff -Nru rpmlint-2.2.0+ds1/rpmlint/checks/SharedLibraryPolicyCheck.py
rpmlint-2.3.0+ds1/rpmlint/checks/SharedLibraryPolicyCheck.py
--- rpmlint-
2.2.0+ds1/rpmlint/checks/SharedLibraryPolicyCheck.py 2021-12-12
09:54:14.000000000-0500
+++ rpmlint-
2.3.0+ds1/rpmlint/checks/SharedLibraryPolicyCheck.py 2022-05-29
12:46:55.000000000-0400
@@ -23,7 +23,7 @@
self.re_soname_strongly_versioned = re.compile(r'-[\d\.]+\.so$')
# the pkgname is based on soname if ending with number; special
option is flavor build
self.re_soname_pkg = re.compile(r'^lib\S+(\d+(-(32|64)bit)?)$')
- self.re_so_files = re.compile(r'\S+.so((.(\d+))+)?$')
+ self.re_so_files = re.compile(r'\S+.so((\.(\d+))*)$')
def _check_missing_policy_lib(self, pkg):
# check the pkg has any libname
diff -Nru rpmlint-2.2.0+ds1/rpmlint/checks/SignatureCheck.py rpmlint-
2.3.0+ds1/rpmlint/checks/SignatureCheck.py
--- rpmlint-2.2.0+ds1/rpmlint/checks/SignatureCheck.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/checks/SignatureCheck.py 2022-05-29
12:46:55.000000000 -0400
@@ -17,11 +17,11 @@
invalid_sig_regex = re.compile(r'invalid OpenPGP signature')
def check(self, pkg):
- retcode, output = pkg.checkSignature()
+ retcode, output = pkg.check_signature()
- # Skip all signature checks if checkSignature output is empty
+ # Skip all signature checks if check_signature output is empty
if output is None:
- print_warning(f'No output from checkSignature() for '
+ print_warning(f'No output from check_signature() for '
f'{pkg.filename}. Skipping signature checks.')
return
diff -Nru rpmlint-2.2.0+ds1/rpmlint/checks/SourceCheck.py rpmlint-
2.3.0+ds1/rpmlint/checks/SourceCheck.py
--- rpmlint-2.2.0+ds1/rpmlint/checks/SourceCheck.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/checks/SourceCheck.py 2022-05-29
12:46:55.000000000 -0400
@@ -33,7 +33,6 @@
def check_source(self, pkg):
# process file list
for fname, pkgfile in pkg.files.items():
-
self._check_file_ext(fname, pkgfile, pkg)
self._check_permissions(fname, pkgfile, pkg)
self._check_compressed_source(fname, pkg)
diff -Nru rpmlint-2.2.0+ds1/rpmlint/checks/SpecCheck.py rpmlint-
2.3.0+ds1/rpmlint/checks/SpecCheck.py
--- rpmlint-2.2.0+ds1/rpmlint/checks/SpecCheck.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/checks/SpecCheck.py 2022-05-29
12:46:55.000000000 -0400
@@ -172,14 +172,12 @@
self._check_non_utf8_spec_file(pkg)
# gather info from spec lines
-
pkg.current_linenum = 0
nbsp = UNICODE_NBSP
# Analyse specfile line by line to check for (E)rrors or (W)arnings
for line in spec_lines:
-
pkg.current_linenum += 1
char = line.find(nbsp)
diff -Nru rpmlint-2.2.0+ds1/rpmlint/checks/TagsCheck.py rpmlint-
2.3.0+ds1/rpmlint/checks/TagsCheck.py
--- rpmlint-2.2.0+ds1/rpmlint/checks/TagsCheck.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/checks/TagsCheck.py 2022-05-29
12:46:55.000000000 -0400
@@ -32,7 +32,6 @@
class TagsCheck(AbstractCheck):
-
def __init__(self, config, output):
super().__init__(config, output)
self.valid_groups = config.configuration['ValidGroups']
diff -Nru rpmlint-2.2.0+ds1/rpmlint/cli.py rpmlint-2.3.0+ds1/rpmlint/cli.py
--- rpmlint-2.2.0+ds1/rpmlint/cli.py 2021-12-12 09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/cli.py 2022-05-29 12:46:55.000000000 -0400
@@ -82,6 +82,10 @@
parser.add_argument('-i', '--installed', nargs='+', default='',
help='installed packages to be validated by rpmlint')
parser.add_argument('-t', '--time-report', action='store_true',
help='print time report for run checks')
parser.add_argument('-T', '--profile', action='store_true', help='print
cProfile report')
+ parser.add_argument('--ignore-unused-rpmlintrc', action='store_true',
+ help='Do not report "unused-rpmlintrc-filter"
errors')
+ parser.add_argument('--checks',
+ help='Debugging option that enables only selected
checks (separated by comma)')
lint_modes_parser = parser.add_mutually_exclusive_group()
lint_modes_parser.add_argument('-s', '--strict', action='store_true',
help='treat all messages as errors')
lint_modes_parser.add_argument('-P', '--permissive',
action='store_true', help='treat individual errors as non-fatal')
diff -Nru rpmlint-2.2.0+ds1/rpmlint/configdefaults.toml rpmlint-
2.3.0+ds1/rpmlint/configdefaults.toml
--- rpmlint-2.2.0+ds1/rpmlint/configdefaults.toml 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/configdefaults.toml 2022-05-29
12:46:55.000000000 -0400
@@ -3,8 +3,7 @@
"AlternativesCheck",
"AppDataCheck",
"BinariesCheck",
- "BuildDateCheck",
- 'BuildRootCheck',
+ 'BuildRootAndDateCheck',
"ConfigFilesCheck",
"DBusPolicyCheck",
'DuplicatesCheck',
diff -Nru rpmlint-2.2.0+ds1/rpmlint/descriptions/BinariesCheck.toml rpmlint-
2.3.0+ds1/rpmlint/descriptions/BinariesCheck.toml
--- rpmlint-2.2.0+ds1/rpmlint/descriptions/BinariesCheck.toml 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/descriptions/BinariesCheck.toml 2022-05-29
12:46:55.000000000 -0400
@@ -46,7 +46,7 @@
to install the relinked file.
"""
binary-or-shlib-defines-rpath="""
-The binary or shared library defines `RPATH'.
+The binary or shared library defines `RPATH' (or `RUNPATH').
"""
statically-linked-binary="""
The package installs a statically linked binary or object file.
diff -Nru rpmlint-2.2.0+ds1/rpmlint/filter.py rpmlint-
2.3.0+ds1/rpmlint/filter.py
--- rpmlint-2.2.0+ds1/rpmlint/filter.py 2021-12-12 09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/filter.py 2022-05-29 12:46:55.000000000 -0400
@@ -43,6 +43,8 @@
self.error_details.update(self._load_descriptions())
# Counter of how many issues we encountered
self.printed_messages = {'I': 0, 'W': 0, 'E': 0}
+ # Number of promoted warnings and infos to errors
+ self.promoted_to_error = 0
# Messages
self.results = []
@@ -99,6 +101,8 @@
# allow strict reporting where we override levels and treat
everything
# as an error
if self.strict:
+ if level != 'E':
+ self.promoted_to_error += 1
level = 'E'
if badness is None:
diff -Nru rpmlint-2.2.0+ds1/rpmlint/helpers.py rpmlint-
2.3.0+ds1/rpmlint/helpers.py
--- rpmlint-2.2.0+ds1/rpmlint/helpers.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/helpers.py 2022-05-29
12:46:55.000000000 -0400
@@ -7,7 +7,8 @@
from rpmlint.color import Color
-ENGLISH_ENVIROMENT = dict(os.environ, LC_ALL='en_US.UTF-8')
+ENGLISH_ENVIROMENT = dict(os.environ, LC_ALL='en_US.UTF-8',
+ LANGUAGE='en_US')
def string_center(message, filler=' '):
diff -Nru rpmlint-2.2.0+ds1/rpmlint/__isocodes__.py rpmlint-
2.3.0+ds1/rpmlint/__isocodes__.py
--- rpmlint-2.2.0+ds1/rpmlint/__isocodes__.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/__isocodes__.py 2022-05-29
12:46:55.000000000 -0400
@@ -2525,6 +2525,7 @@
'hwo',
'hy',
'hya',
+ 'hye',
'hz',
'ia',
'iai',
diff -Nru rpmlint-2.2.0+ds1/rpmlint/lint.py rpmlint-
2.3.0+ds1/rpmlint/lint.py
--- rpmlint-2.2.0+ds1/rpmlint/lint.py 2021-12-12 09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/lint.py 2022-05-29 12:46:55.000000000 -0400
@@ -1,3 +1,4 @@
+from collections import defaultdict
import cProfile
import importlib
import operator
@@ -10,7 +11,7 @@
from rpmlint.config import Config
from rpmlint.filter import Filter
from rpmlint.helpers import print_warning, string_center
-from rpmlint.pkg import FakePkg, getInstalledPkgs, Pkg
+from rpmlint.pkg import FakePkg, get_installed_pkgs, Pkg
from rpmlint.version import __version__
@@ -25,7 +26,7 @@
self.options = options
self.packages_checked = 0
self.specfiles_checked = 0
- self.check_duration = {}
+ self.check_duration = defaultdict(lambda: 0)
if options['config']:
self.config = Config(options['config'])
else:
@@ -86,7 +87,8 @@
retcode = 66
elif self.output.printed_messages['E'] > 0 and not
self.config.permissive:
quit_color = Color.Red
- retcode = 64
+ all_promoted = self.output.printed_messages['E'] ==
self.output.promoted_to_error
+ retcode = 65 if all_promoted else 64
self._maybe_print_reports()
@@ -121,26 +123,30 @@
return f'{color}{fraction:17.1f}{Color.Reset}'
def _print_time_report(self):
- THRESHOLD = 1
+ PERCENT_THRESHOLD = 1
+ TIME_THRESHOLD = 0.1
total = sum(self.check_duration.values())
checked_files = [check.checked_files for check in
self.checks.values() if check.checked_files]
total_checked_files = max(checked_files) if checked_files else ''
- print(f'\n{Color.Bold}Check time report{Color.Reset}
(>{THRESHOLD}%):')
+ print(f'{Color.Bold}Check time report{Color.Reset}
(>{PERCENT_THRESHOLD}% & >{TIME_THRESHOLD}s):')
print(f'{Color.Bold} {"Check":32s} {"Duration (in s)":>12}
{"Fraction (in %)":>17} Checked files{Color.Reset}')
for check, duration in sorted(self.check_duration.items(),
key=operator.itemgetter(1), reverse=True):
fraction = 100.0 * duration / total
- if fraction < THRESHOLD:
+ if fraction < PERCENT_THRESHOLD or duration < TIME_THRESHOLD:
continue
- checked_files = self.checks[check].checked_files
- if not checked_files:
- checked_files = ''
- print(f' {check:32s} {duration:15.2f}
{self._get_color_time_report_value(fraction)} {checked_files:>14}')
- print(f' {"TOTAL":32s} {total:15.2f} {100:17.2f}
{total_checked_files:>14}')
+
+ checked_files = ''
+ if check in self.checks:
+ checked = self.checks[check].checked_files
+ if checked:
+ checked_files = checked
+ print(f' {check:32s} {duration:15.1f}
{self._get_color_time_report_value(fraction)} {checked_files:>14}')
+ print(f' {"TOTAL":32s} {total:15.1f} {100:17.1f}
{total_checked_files:>14}\n')
def _print_cprofile(self):
N = 30
- print(f'\n{Color.Bold}cProfile report:{Color.Reset}')
+ print(f'{Color.Bold}cProfile report:{Color.Reset}')
self.profile.disable()
stats = Stats(self.profile)
stats.sort_stats('cumulative').print_stats(N)
@@ -152,7 +158,7 @@
def _load_installed_rpms(self, packages):
existing_packages = []
for name in packages:
- pkg = getInstalledPkgs(name)
+ pkg = get_installed_pkgs(name)
if pkg:
existing_packages.extend(pkg)
else:
@@ -230,30 +236,21 @@
for pkg in packages:
self.validate_file(pkg, pkg == packages[-1])
- # run post check function
- for checker in self.checks.values():
- checker.after_checks()
-
def _expand_filelist(self, files):
packages = []
for pkg in files:
- if pkg.is_file() and self._check_valid_suffix(pkg):
+ if pkg.is_file() and pkg.suffix in ('.rpm', '.spm', '.spec'):
packages.append(pkg)
elif pkg.is_dir():
packages.extend(self._expand_filelist(pkg.iterdir()))
return packages
- @staticmethod
- def _check_valid_suffix(filename):
- if any(ext == filename.suffix for ext in ['.rpm', '.spm',
'.spec']):
- return True
- return False
-
def validate_file(self, pname, is_last):
try:
if pname.suffix == '.rpm' or pname.suffix == '.spm':
with Pkg(pname, self.config.configuration['ExtractDir'],
verbose=self.config.info) as pkg:
+ self.check_duration['rpm2cpio'] += pkg.extraction_time
self.run_checks(pkg, is_last)
elif pname.suffix == '.spec':
with FakePkg(pname) as pkg:
@@ -268,16 +265,18 @@
def run_checks(self, pkg, is_last):
spec_checks = isinstance(pkg, FakePkg)
for checker in self.checks:
- if checker not in self.check_duration:
- self.check_duration[checker] = 0
start = time.monotonic()
fn = self.checks[checker].check_spec if spec_checks else
self.checks[checker].check
fn(pkg)
self.check_duration[checker] += time.monotonic() - start
- # validate used filters in rpmlintrc
+ # run post check function and validate used filters in rpmlintrc
if is_last:
- self.output.validate_filters(pkg)
+ for checker in self.checks.values():
+ checker.after_checks()
+
+ if not self.options['ignore_unused_rpmlintrc']:
+ self.output.validate_filters(pkg)
if spec_checks:
self.specfiles_checked += 1
@@ -306,10 +305,15 @@
SingletonTM
"""
+ selected_checks = self.options['checks']
+ if selected_checks:
+ selected_checks = selected_checks.split(',')
+
for check in self.config.configuration['Checks']:
if check in self.checks:
continue
- self.checks[check] = self.load_check(check)
+ if not selected_checks or check in selected_checks:
+ self.checks[check] = self.load_check(check)
def load_check(self, name):
"""Load a (check) module by its name, unless it is already
loaded."""
diff -Nru rpmlint-2.2.0+ds1/rpmlint/pkgfile.py rpmlint-
2.3.0+ds1/rpmlint/pkgfile.py
--- rpmlint-2.2.0+ds1/rpmlint/pkgfile.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/pkgfile.py 2022-05-29
12:46:55.000000000 -0400
@@ -2,7 +2,6 @@
class PkgFile(object):
-
__slots__ = ['name', 'path', 'flags', 'mode', 'user', 'group',
'linkto',
'size', 'md5', 'mtime', 'rdev', 'inode', 'requires',
'provides',
'lang', 'magic', 'filecaps']
diff -Nru rpmlint-2.2.0+ds1/rpmlint/pkg.py rpmlint-2.3.0+ds1/rpmlint/pkg.py
--- rpmlint-2.2.0+ds1/rpmlint/pkg.py 2021-12-12 09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/pkg.py 2022-05-29 12:46:55.000000000 -0400
@@ -2,6 +2,7 @@
from collections import namedtuple
import gzip
import lzma
+import mmap
import os
from pathlib import Path
import re
@@ -9,6 +10,7 @@
import stat
import subprocess
import tempfile
+import time
from urllib.parse import urljoin
try:
@@ -22,7 +24,7 @@
import rpm
from rpmlint.helpers import byte_to_string, ENGLISH_ENVIROMENT,
print_warning
from rpmlint.pkgfile import PkgFile
-import zstd
+import zstandard as zstd
DepInfo = namedtuple('DepInfo', ('name', 'flags', 'version'))
@@ -380,7 +382,6 @@
# classes representing package
class AbstractPkg(object):
-
def cleanup(self):
pass
@@ -392,13 +393,16 @@
class Pkg(AbstractPkg):
-
_magic_from_compressed_re =
re.compile(r'\([^)]+\s+compressed\s+data\b')
def __init__(self, filename, dirname, header=None, is_source=False,
extracted=False, verbose=False):
self.filename = filename
self.extracted = extracted
- self.dirname = self.dir_name(dirname, verbose)
+
+ # record decompression and extraction time
+ start = time.monotonic()
+ self.dirname = self._extract_rpm(dirname, verbose)
+ self.extraction_time = time.monotonic() - start
self.current_linenum = None
self._req_names = -1
@@ -422,11 +426,11 @@
(self.requires, self.prereq, self.provides, self.conflicts,
self.obsoletes, self.recommends, self.suggests, self.enhances,
- self.supplements) = self._gatherDepInfo()
+ self.supplements) = self._gather_dep_info()
self.req_names = [x[0] for x in self.requires + self.prereq]
- self.files = self._gatherFilesInfo()
+ self.files = self._gather_files_info()
self.config_files = [x.name for x in self.files.values() if
x.is_config]
self.doc_files = [x.name for x in self.files.values() if x.is_doc]
self.ghost_files = [x.name for x in self.files.values() if
x.is_ghost]
@@ -470,14 +474,10 @@
return val
# return the name of the directory where the package is extracted
- def dirName(self):
+ def dir_name(self):
return self.dirname
- def dir_name(self, dirname, verbose):
- return self._extract(dirname, verbose)
-
- # extract rpm contents
- def _extract(self, dirname, verbose):
+ def _extract_rpm(self, dirname, verbose):
if not Path(dirname).is_dir():
print_warning('Unable to access dir %s' % dirname)
elif dirname == '/':
@@ -504,7 +504,7 @@
self.extracted = True
return dirname
- def checkSignature(self):
+ def check_signature(self):
ret = subprocess.run(('rpm', '-Kv', self.filename),
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
env=ENGLISH_ENVIROMENT)
@@ -519,19 +519,21 @@
self.__tmpdir.cleanup()
def grep(self, regex, filename):
- """Grep regex from a file, return matching line numbers."""
- ret = []
- lineno = 0
+ """Grep regex from a file, return first matching line number
(starting with 1)."""
+ data = self.read_with_mmap(filename)
+ match = regex.search(data)
+ if match:
+ return data.count('\n', 0, match.start()) + 1
+ else:
+ return None
+
+ def read_with_mmap(self, filename):
+ """Mmap a file, return it's content decoded."""
try:
- with open(Path(self.dirName() or '/', filename.lstrip('/'))) as
in_file:
- for line in in_file:
- lineno += 1
- if regex.search(line):
- ret.append(str(lineno))
- break
+ with open(Path(self.dir_name() or '/', filename.lstrip('/')))
as in_file:
+ return mmap.mmap(in_file.fileno(), 0, mmap.MAP_SHARED,
mmap.PROT_READ).read().decode()
except Exception:
- pass
- return ret
+ return ''
def langtag(self, tag, lang):
"""Get value of tag in the given language."""
@@ -544,8 +546,7 @@
return ret
# extract information about the files
- def _gatherFilesInfo(self):
-
+ def _gather_files_info(self):
ret = {}
flags = self.header[rpm.RPMTAG_FILEFLAGS]
modes = self.header[rpm.RPMTAG_FILEMODES]
@@ -576,7 +577,7 @@
for idx, file in enumerate(files):
pkgfile = PkgFile(file)
pkgfile.path = os.path.normpath(os.path.join(
- self.dirName() or '/', pkgfile.name.lstrip('/')))
+ self.dir_name() or '/', pkgfile.name.lstrip('/')))
pkgfile.flags = flags[idx]
pkgfile.mode = modes[idx]
pkgfile.user = byte_to_string(users[idx])
@@ -655,7 +656,7 @@
xs.append(DepInfo(name, flags[loop], evr))
return xs, prereq
- def _gatherDepInfo(self):
+ def _gather_dep_info(self):
_requires = []
_prereq = []
_provides = []
@@ -720,7 +721,7 @@
return prog
-def getInstalledPkgs(name):
+def get_installed_pkgs(name):
"""Get list of installed package objects by name."""
pkgs = []
@@ -759,7 +760,7 @@
def cleanup(self):
pass
- def checkSignature(self):
+ def check_signature(self):
return (0, 'fake: pgp md5 OK')
@@ -776,13 +777,19 @@
self.files = {}
self.ghost_files = {}
+ def add_file(self, path, name):
+ pkgfile = PkgFile(name)
+ pkgfile.path = path
+ self.files[name] = pkgfile
+ return pkgfile
+
def add_file_with_content(self, name, content, **flags):
"""
Add file to the FakePkg and fill the file with provided
string content.
"""
basename = name.replace(os.path.sep, '_')
- path = os.path.join(self.dirName(), basename)
+ path = os.path.join(self.dir_name(), basename)
with open(path, 'w') as out:
out.write(content)
pkg_file = PkgFile(name)
@@ -806,7 +813,7 @@
# HACK: reuse the real Pkg's logic
return Pkg.readlink(self, pkgfile)
- def dirName(self):
+ def dir_name(self):
if not self.dirname:
self.__tmpdir =
tempfile.TemporaryDirectory(prefix='rpmlint.%s.' % Path(self.name).name)
self.dirname = self.__tmpdir.name
diff -Nru rpmlint-2.2.0+ds1/rpmlint/readelfparser.py rpmlint-
2.3.0+ds1/rpmlint/readelfparser.py
--- rpmlint-2.2.0+ds1/rpmlint/readelfparser.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/readelfparser.py 2022-05-29
12:46:55.000000000 -0400
@@ -76,15 +76,16 @@
section_regex = re.compile(r'.*\]
(?P<section>\S*)\s*\S+\s*\S*\s*\S*\s*(?P<size>\w*)')
pic_regex = re.compile(r'\.rela?\.(data|text)')
- def __init__(self, path):
+ def __init__(self, path, extra_flags):
self.path = path
self.elf_files = []
self.parsing_failed_reason = None
self.pic = False
+ self.extra_flags = extra_flags
self.parse()
def parse(self):
- r = subprocess.run(['readelf', '-W', '-S', self.path],
encoding='utf8',
+ r = subprocess.run(['readelf', '-W', '-S', self.path] +
self.extra_flags, encoding='utf8',
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
env=ENGLISH_ENVIROMENT)
if r.returncode != 0:
self.parsing_failed_reason = r.stderr
@@ -145,14 +146,15 @@
header_regex =
re.compile('\\s+(?P<header>\\w+)(\\s+\\w+){5}\\s+(?P<flags>[RWE ]{3}).*')
- def __init__(self, path):
+ def __init__(self, path, extra_flags):
self.path = path
self.headers = []
self.parsing_failed_reason = None
+ self.extra_flags = extra_flags
self.parse()
def parse(self):
- r = subprocess.run(['readelf', '-W', '-l', self.path],
encoding='utf8',
+ r = subprocess.run(['readelf', '-W', '-l', self.path] +
self.extra_flags, encoding='utf8',
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
env=ENGLISH_ENVIROMENT)
if r.returncode != 0:
self.parsing_failed_reason = r.stderr
@@ -215,17 +217,19 @@
section_regex =
re.compile('\\s+\\w*\\s+\\((?P<key>[^\\)]+)\\)\\s+(?P<value>.*)')
soname_regex = re.compile('Library soname: \\[(?P<soname>[^\\]]+)\\]')
needed_regex = re.compile('Shared library: \\[(?P<library>[^\\]]+)\\]')
- rpath_regex = re.compile('Library runpath: \\[(?P<path>[^\\]]+)\\]')
+ runpath_regex = re.compile('Library runpath: \\[(?P<path>[^\\]]+)\\]')
+ rpath_regex = re.compile('Library rpath: \\[(?P<path>[^\\]]+)\\]')
- def __init__(self, path):
+ def __init__(self, path, extra_flags):
self.path = path
self.sections = []
self.parsing_failed_reason = None
+ self.extra_flags = extra_flags
self.parse()
self.parse_meta()
def parse(self):
- r = subprocess.run(['readelf', '-W', '-d', self.path],
encoding='utf8',
+ r = subprocess.run(['readelf', '-W', '-d', self.path] +
self.extra_flags, encoding='utf8',
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
env=ENGLISH_ENVIROMENT)
if r.returncode != 0:
self.parsing_failed_reason = r.stderr
@@ -255,11 +259,17 @@
if r:
self.needed.append(r.group('library'))
- self.runpath = []
+ self.runpaths = []
+
+ # Parse both RUNPATH and RPATH
for line in self['RUNPATH']:
+ r = self.runpath_regex.search(line)
+ if r:
+ self.runpaths.append(r.group('path'))
+ for line in self['RPATH']:
r = self.rpath_regex.search(line)
if r:
- self.runpath.append(r.group('path'))
+ self.runpaths.append(r.group('path'))
def __getitem__(self, key):
return [x.value for x in self.sections if x.key == key]
@@ -279,15 +289,16 @@
section_regex = re.compile('\\s+[0-
9]+:\\s\\w+\\s+(\\w+)\\s+(?P<type>\\w+)\\s+(?P<bind>\\w+)\\s+(?P<visibility>
\\w+)\\s+\\w+\\s+(?P<name>\\S+)')
- def __init__(self, path):
+ def __init__(self, path, extra_flags):
self.path = path
self.symbols = []
self.parsing_failed_reason = None
+ self.extra_flags = extra_flags
self.parse()
def parse(self):
try:
- r = subprocess.run(['readelf', '-W', '-s', self.path],
encoding='utf8',
+ r = subprocess.run(['readelf', '-W', '-s', self.path] +
self.extra_flags, encoding='utf8',
stdout=subprocess.PIPE,
stderr=subprocess.PIPE, env=ENGLISH_ENVIROMENT)
if r.returncode != 0:
self.parsing_failed_reason = r.stderr
@@ -316,14 +327,15 @@
comment_regex = re.compile('\\s+\\[[\\s[0-9]+\\]\\s+(?P<comment>.*)')
- def __init__(self, path):
+ def __init__(self, path, extra_flags):
self.path = path
self.comments = []
self.parsing_failed_reason = None
+ self.extra_flags = extra_flags
self.parse()
def parse(self):
- r = subprocess.run(['readelf', '-p', '.comment', self.path],
encoding='utf8',
+ r = subprocess.run(['readelf', '-p', '.comment', self.path] +
self.extra_flags, encoding='utf8',
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
env=ENGLISH_ENVIROMENT)
if r.returncode != 0:
self.parsing_failed_reason = r.stderr
@@ -350,11 +362,16 @@
self.is_shlib = self.so_regex.search(path)
self.is_debug = path.endswith('.debug')
- self.section_info = ElfSectionInfo(pkgfile_path)
- self.program_header_info = ElfProgramHeaderInfo(pkgfile_path)
- self.dynamic_section_info = ElfDynamicSectionInfo(pkgfile_path)
- self.symbol_table_info = ElfSymbolTableInfo(pkgfile_path)
- self.comment_section_info = ElfCommentInfo(pkgfile_path)
+ # Do not follow debug info links
+ output = subprocess.check_output('readelf --help', shell=True,
encoding='utf8')
+ flag = '--debug-dump=no-follow-links'
+ extra_flags = [flag] if flag in output else []
+
+ self.section_info = ElfSectionInfo(pkgfile_path, extra_flags)
+ self.program_header_info = ElfProgramHeaderInfo(pkgfile_path,
extra_flags)
+ self.dynamic_section_info = ElfDynamicSectionInfo(pkgfile_path,
extra_flags)
+ self.symbol_table_info = ElfSymbolTableInfo(pkgfile_path,
extra_flags)
+ self.comment_section_info = ElfCommentInfo(pkgfile_path,
extra_flags)
def parsing_failed_reason(self):
reasons = [self.section_info.parsing_failed_reason,
diff -Nru rpmlint-2.2.0+ds1/rpmlint/rpmdiff.py rpmlint-
2.3.0+ds1/rpmlint/rpmdiff.py
--- rpmlint-2.2.0+ds1/rpmlint/rpmdiff.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/rpmlint/rpmdiff.py 2022-05-29
12:46:55.000000000 -0400
@@ -5,7 +5,7 @@
import rpm
from rpmlint.helpers import byte_to_string, print_warning
-from rpmlint.pkg import getInstalledPkgs, Pkg
+from rpmlint.pkg import get_installed_pkgs, Pkg
class Rpmdiff(object):
@@ -141,7 +141,7 @@
return Pkg(name, tmpdir)
except TypeError:
pass
- inst = getInstalledPkgs(str(name))
+ inst = get_installed_pkgs(str(name))
if not inst:
raise KeyError(f'No installed packages by name {name}')
if len(inst) > 1:
diff -Nru rpmlint-2.2.0+ds1/setup.py rpmlint-2.3.0+ds1/setup.py
--- rpmlint-2.2.0+ds1/setup.py 2021-12-12 09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/setup.py 2022-05-29 12:46:55.000000000 -0400
@@ -9,7 +9,7 @@
url='https://github.com/rpm-software-management/rpmlint',
download_url='https://github.com/rpm-software-management/rpmlint',
- version='2.2.0',
+ version='2.3.0',
author='Frédéric Lepied',
author_email='[email protected]',
@@ -28,8 +28,6 @@
'Operating System :: POSIX',
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
- 'Programming Language :: Python :: 3.6',
- 'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
@@ -45,8 +43,10 @@
'pyxdg',
'rpm',
'toml',
- 'zstd',
+ 'zstandard',
'importlib-metadata;python_version<"3.8"',
+ 'pyenchant',
+ 'python-magic'
],
tests_require=[
'pytest',
diff -Nru rpmlint-2.2.0+ds1/test/test_build_date.py rpmlint-
2.3.0+ds1/test/test_build_date.py
--- rpmlint-2.2.0+ds1/test/test_build_date.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/test/test_build_date.py 2022-05-29
12:46:55.000000000 -0400
@@ -1,7 +1,7 @@
import re
import pytest
-from rpmlint.checks.BuildDateCheck import BuildDateCheck
+from rpmlint.checks.BuildRootAndDateCheck import BuildRootAndDateCheck
from rpmlint.filter import Filter
from Testing import CONFIG, get_tested_package
@@ -11,7 +11,7 @@
def builddatecheck():
CONFIG.info = True
output = Filter(CONFIG)
- test = BuildDateCheck(CONFIG, output)
+ test = BuildRootAndDateCheck(CONFIG, output)
return output, test
diff -Nru rpmlint-2.2.0+ds1/test/test_build_root.py rpmlint-
2.3.0+ds1/test/test_build_root.py
--- rpmlint-2.2.0+ds1/test/test_build_root.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/test/test_build_root.py 2022-05-29
12:46:55.000000000 -0400
@@ -1,5 +1,5 @@
import pytest
-from rpmlint.checks.BuildRootCheck import BuildRootCheck
+from rpmlint.checks.BuildRootAndDateCheck import BuildRootAndDateCheck
from rpmlint.filter import Filter
from Testing import CONFIG, get_tested_package
@@ -9,7 +9,7 @@
def buildrootcheck():
CONFIG.info = True
output = Filter(CONFIG)
- test = BuildRootCheck(CONFIG, output)
+ test = BuildRootAndDateCheck(CONFIG, output)
return output, test
diff -Nru rpmlint-2.2.0+ds1/test/test_ldd_parser.py rpmlint-
2.3.0+ds1/test/test_ldd_parser.py
--- rpmlint-2.2.0+ds1/test/test_ldd_parser.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/test/test_ldd_parser.py 2022-05-29
12:46:55.000000000 -0400
@@ -27,9 +27,9 @@
return LddParser(get_full_path(path), system_path, True)
-def run_elf_checks(test, pkg, fullpath, path):
- test._detect_attributes(get_magic(fullpath))
- test.run_elf_checks(pkg, fullpath, path)
+def run_elf_checks(test, pkg, pkgfile):
+ test._detect_attributes(get_magic(pkgfile.path))
+ test.run_elf_checks(pkg, pkgfile)
@pytest.mark.skipif(not IS_X86_64, reason='x86-64 only')
@@ -64,38 +64,46 @@
@pytest.mark.skipif(not IS_X86_64, reason='x86-64 only')
def test_unused_dependency_in_package(binariescheck):
output, test = binariescheck
- run_elf_checks(test, FakePkg('fake'),
get_full_path('libtirpc.so.3.0.0'), '/lib64/x.so')
- assert not test.readelf_parser.parsing_failed_reason()
- assert not test.ldd_parser.parsing_failed_reason
- out = output.print_results(output.results)
- assert 'E: unused-direct-shlib-dependency ' in out
+ with FakePkg('fake') as pkg:
+ pkgfile = pkg.add_file(get_full_path('libtirpc.so.3.0.0'),
'/lib64/x.so')
+ run_elf_checks(test, pkg, pkgfile)
+ assert not test.readelf_parser.parsing_failed_reason()
+ assert not test.ldd_parser.parsing_failed_reason
+ out = output.print_results(output.results)
+ assert 'E: unused-direct-shlib-dependency ' in out
@pytest.mark.skipif(not IS_X86_64, reason='x86-64 only')
def test_unused_dependency_in_package_for_executable(binariescheck):
output, test = binariescheck
- run_elf_checks(test, FakePkg('fake'), get_full_path('appletviewer'),
'/usr/bin/appletviewer')
- assert not test.readelf_parser.parsing_failed_reason()
- assert not test.ldd_parser.parsing_failed_reason
- out = output.print_results(output.results)
- assert 'W: unused-direct-shlib-dependency ' in out
+ with FakePkg('fake') as pkg:
+ pkgfile = pkg.add_file(get_full_path('appletviewer'),
'/usr/bin/appletviewer')
+ run_elf_checks(test, pkg, pkgfile)
+ assert not test.readelf_parser.parsing_failed_reason()
+ assert not test.ldd_parser.parsing_failed_reason
+ out = output.print_results(output.results)
+ assert 'W: unused-direct-shlib-dependency ' in out
@pytest.mark.skipif(not IS_X86_64, reason='x86-64 only')
def test_opt_dependency(binariescheck):
output, test = binariescheck
- run_elf_checks(test, FakePkg('fake'), get_full_path('opt-dependency'),
'/bin/opt-dependency')
- assert not test.readelf_parser.parsing_failed_reason()
- assert not test.ldd_parser.parsing_failed_reason
- out = output.print_results(output.results)
- assert 'E: linked-against-opt-library /bin/opt-dependency
/opt/libfoo.so' in out
+ with FakePkg('fake') as pkg:
+ pkgfile = pkg.add_file(get_full_path('opt-dependency'), '/bin/opt-
dependency')
+ run_elf_checks(test, pkg, pkgfile)
+ assert not test.readelf_parser.parsing_failed_reason()
+ assert not test.ldd_parser.parsing_failed_reason
+ out = output.print_results(output.results)
+ assert 'E: linked-against-opt-library /bin/opt-dependency
/opt/libfoo.so' in out
@pytest.mark.skipif(not IS_X86_64, reason='x86-64 only')
def test_usr_dependency(binariescheck):
output, test = binariescheck
- run_elf_checks(test, FakePkg('fake'), get_full_path('usr-dependency'),
'/bin/usr-dependency')
- assert not test.readelf_parser.parsing_failed_reason()
- assert not test.ldd_parser.parsing_failed_reason
- out = output.print_results(output.results)
- assert 'W: linked-against-usr-library /bin/usr-dependency
/usr/libfoo.so' in out
+ with FakePkg('fake') as pkg:
+ pkgfile = pkg.add_file(get_full_path('usr-dependency'), '/bin/usr-
dependency')
+ run_elf_checks(test, pkg, pkgfile)
+ assert not test.readelf_parser.parsing_failed_reason()
+ assert not test.ldd_parser.parsing_failed_reason
+ out = output.print_results(output.results)
+ assert 'W: linked-against-usr-library /bin/usr-dependency
/usr/libfoo.so' in out
diff -Nru rpmlint-2.2.0+ds1/test/test_lint.py rpmlint-
2.3.0+ds1/test/test_lint.py
--- rpmlint-2.2.0+ds1/test/test_lint.py 2021-12-12 09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/test/test_lint.py 2022-05-29 12:46:55.000000000 -0400
@@ -22,15 +22,16 @@
'rpmlintrc': False,
'installed': '',
'time_report': False,
- 'profile': False
+ 'profile': False,
+ 'ignore_unused_rpmlintrc': False,
+ 'checks': None
}
basic_tests = [
'AlternativesCheck',
'AppDataCheck',
'BinariesCheck',
- 'BuildDateCheck',
- 'BuildRootCheck',
+ 'BuildRootAndDateCheck',
'ConfigFilesCheck',
'DBusPolicyCheck',
'DuplicatesCheck',
diff -Nru rpmlint-2.2.0+ds1/test/test_objdump_parser.py rpmlint-
2.3.0+ds1/test/test_objdump_parser.py
--- rpmlint-2.2.0+ds1/test/test_objdump_parser.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/test/test_objdump_parser.py 2022-05-29
12:46:55.000000000 -0400
@@ -28,9 +28,9 @@
return ObjdumpParser(get_full_path(path), system_path)
-def run_elf_checks(test, pkg, fullpath, path):
- test._detect_attributes(get_magic(fullpath))
- test.run_elf_checks(pkg, fullpath, path)
+def run_elf_checks(test, pkg, pkgfile):
+ test._detect_attributes(get_magic(pkgfile.path))
+ test.run_elf_checks(pkg, pkgfile)
def test_basic():
@@ -47,8 +47,11 @@
@pytest.mark.skipif(not IS_X86_64, reason='x86-64 only')
def test_executable_stack_package(binariescheck):
output, test = binariescheck
- run_elf_checks(test, FakePkg('fake'), get_full_path('executable-
stack'), 'a.out')
- out = output.print_results(output.results)
- assert 'W: missing-mandatory-optflags a.out -fno-PIE -g -Ofast' in out
- assert 'E: forbidden-optflags a.out -frounding-math' in out
+ with FakePkg('fake') as pkg:
+ pkgfile = pkg.add_file(get_full_path('executable-stack'), 'a.out')
+ run_elf_checks(test, FakePkg('fake'), pkgfile)
+ out = output.print_results(output.results)
+
+ assert 'W: missing-mandatory-optflags a.out -fno-PIE -g -Ofast' in
out
+ assert 'E: forbidden-optflags a.out -frounding-math' in out
diff -Nru rpmlint-2.2.0+ds1/test/test_readelf_parser.py rpmlint-
2.3.0+ds1/test/test_readelf_parser.py
--- rpmlint-2.2.0+ds1/test/test_readelf_parser.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/test/test_readelf_parser.py 2022-05-29
12:46:55.000000000 -0400
@@ -5,6 +5,7 @@
from rpmlint.checks.BinariesCheck import BinariesCheck
from rpmlint.filter import Filter
from rpmlint.pkg import FakePkg, get_magic
+from rpmlint.pkgfile import PkgFile
from rpmlint.readelfparser import ReadelfParser
from Testing import CONFIG, get_tested_path, HAS_32BIT_GLIBC, IS_I686,
IS_X86_64
@@ -28,9 +29,9 @@
return ReadelfParser(get_full_path(path), system_path)
-def run_elf_checks(test, pkg, fullpath, path):
- test._detect_attributes(get_magic(fullpath))
- test.run_elf_checks(pkg, fullpath, path)
+def run_elf_checks(test, pkg, pkgfile):
+ test._detect_attributes(get_magic(pkgfile.path))
+ test.run_elf_checks(pkg, pkgfile)
def test_empty_archive():
@@ -91,68 +92,86 @@
readelf = readelfparser('rpath-lib.so', '/lib64/rpath-lib.so')
assert readelf.is_shlib
assert not readelf.is_archive
- assert len(readelf.dynamic_section_info.runpath) == 1
- assert '/tmp/termcap.so.4' in readelf.dynamic_section_info.runpath
+ assert len(readelf.dynamic_section_info.runpaths) == 1
+ assert '/tmp/termcap.so.4' in readelf.dynamic_section_info.runpaths
def test_lto_bytecode(binariescheck):
output, test = binariescheck
- run_elf_checks(test, FakePkg('fake'), get_full_path('lto-object.o'),
'x.a')
- assert not test.readelf_parser.parsing_failed_reason()
- out = output.print_results(output.results)
- assert 'lto-bytecode' in out
+ with FakePkg('fake') as pkg:
+ pkgfile = pkg.add_file(get_full_path('lto-object.o'), 'x.a')
+ run_elf_checks(test, pkg, pkgfile)
+ assert not test.readelf_parser.parsing_failed_reason()
+ out = output.print_results(output.results)
+ assert 'lto-bytecode' in out
def test_lto_archive_text(binariescheck):
output, test = binariescheck
- run_elf_checks(test, FakePkg('fake'), get_full_path('stripped-lto.a'),
'x.a')
- out = output.print_results(output.results)
- assert 'E: lto-no-text-in-archive' in out
- assert 'E: static-library-without-debuginfo' in out
+ with FakePkg('fake') as pkg:
+ pkgfile = pkg.add_file(get_full_path('stripped-lto.a'), 'x.a')
+ run_elf_checks(test, pkg, pkgfile)
+ out = output.print_results(output.results)
+ assert 'E: lto-no-text-in-archive' in out
+ assert 'E: static-library-without-debuginfo' in out
def test_stripped_archive(binariescheck):
output, test = binariescheck
- run_elf_checks(test, FakePkg('fake'), get_full_path('stripped-
archive.a'), 'x.a')
- out = output.print_results(output.results)
- assert 'E: static-library-without-symtab' in out
+ with FakePkg('fake') as pkg:
+ pkgfile = pkg.add_file(get_full_path('stripped-archive.a'), 'x.a')
+ run_elf_checks(test, pkg, pkgfile)
+ out = output.print_results(output.results)
+ assert 'E: static-library-without-symtab' in out
def test_lto_archive_text_function_sections(binariescheck):
output, test = binariescheck
- run_elf_checks(test, FakePkg('fake'), get_full_path('function-
sections.a'), 'x.a')
- assert 'E: lto-no-text-in-archive' not in
output.print_results(output.results)
+ with FakePkg('fake') as pkg:
+ pkgfile = pkg.add_file(get_full_path('function-sections.a'), 'x.a')
+ run_elf_checks(test, pkg, pkgfile)
+ assert 'E: lto-no-text-in-archive' not in
output.print_results(output.results)
def test_lto_archive_init_array(binariescheck):
output, test = binariescheck
- run_elf_checks(test, FakePkg('fake'), get_full_path('libbsd-ctor.a'),
'x.a')
- assert 'E: lto-no-text-in-archive' not in
output.print_results(output.results)
+ with FakePkg('fake') as pkg:
+ pkgfile = pkg.add_file(get_full_path('libbsd-ctor.a'), 'x.a')
+ run_elf_checks(test, pkg, pkgfile)
+ assert 'E: lto-no-text-in-archive' not in
output.print_results(output.results)
def test_lto_archive_preinit_array(binariescheck):
output, test = binariescheck
- run_elf_checks(test, FakePkg('fake'), get_full_path('libclang_rt.asan-
preinit-x86_64.a'), 'x.a')
- assert 'E: lto-no-text-in-archive' not in
output.print_results(output.results)
+ with FakePkg('fake') as pkg:
+ pkgfile = pkg.add_file(get_full_path('libclang_rt.asan-preinit-
x86_64.a'), 'x.a')
+ run_elf_checks(test, pkg, pkgfile)
+ assert 'E: lto-no-text-in-archive' not in
output.print_results(output.results)
def test_lto_archive_with_only_data(binariescheck):
output, test = binariescheck
- run_elf_checks(test, FakePkg('fake'), get_full_path('only-data.a'),
'x.a')
- assert 'E: lto-no-text-in-archive' not in
output.print_results(output.results)
+ with FakePkg('fake') as pkg:
+ pkgfile = pkg.add_file(get_full_path('only-data.a'), 'x.a')
+ run_elf_checks(test, pkg, pkgfile)
+ assert 'E: lto-no-text-in-archive' not in
output.print_results(output.results)
def test_archive_with_debuginfo(binariescheck):
output, test = binariescheck
- run_elf_checks(test, FakePkg('fake'), get_full_path('archive-with-
debuginfo.a'), 'x.a')
- assert 'E: static-library-without-debuginfo' not in
output.print_results(output.results)
+ with FakePkg('fake') as pkg:
+ pkgfile = pkg.add_file(get_full_path('archive-with-debuginfo.a'),
'x.a')
+ run_elf_checks(test, pkg, pkgfile)
+ assert 'E: static-library-without-debuginfo' not in
output.print_results(output.results)
@pytest.mark.skipif(not IS_X86_64, reason='x86-64 only')
def test_executable_stack(binariescheck):
output, test = binariescheck
- run_elf_checks(test, FakePkg('fake'), get_full_path('executable-
stack'), 'a.out')
- assert 'E: executable-stack' in output.results[0]
+ with FakePkg('fake') as pkg:
+ pkgfile = pkg.add_file(get_full_path('executable-stack'),
'/lib64/my/a.out')
+ run_elf_checks(test, pkg, pkgfile)
+ assert 'E: executable-stack /lib64/my/a.out' in output.results[0]
def test_readelf_failure():
@@ -162,102 +181,129 @@
def test_readelf_failure_in_package(binariescheck):
output, test = binariescheck
- run_elf_checks(test, FakePkg('fake'), get_full_path('not-existing.so'),
'/lib64/not-existing.so')
- out = output.print_results(output.results)
- assert 'readelf-failed /lib64/not-existing.so' in out
+ with FakePkg('fake') as pkg:
+ pkgfile = pkg.add_file(get_full_path('not-existing.so'),
'/lib64/not-existing.so')
+ run_elf_checks(test, pkg, pkgfile)
+ out = output.print_results(output.results)
+ assert 'readelf-failed /lib64/not-existing.so' in out
def test_readelf_single_error_message(binariescheck):
output, test = binariescheck
- run_elf_checks(test, FakePkg('fake'), get_full_path('small_archive.a'),
'/lib64/small_archive.a')
- out = output.print_results(output.results)
- filtered = [line for line in out.splitlines() if 'Not an ELF file' in
line]
- assert len(filtered) == 1
+ with FakePkg('fake') as pkg:
+ pkgfile = pkg.add_file(get_full_path('small_archive.a'),
'/lib64/small_archive.a')
+ run_elf_checks(test, pkg, pkgfile)
+ out = output.print_results(output.results)
+ filtered = [line for line in out.splitlines() if 'Not an ELF file'
in line]
+ assert len(filtered) == 1
@pytest.mark.skipif(not IS_X86_64, reason='x86-64 only')
def test_no_soname(binariescheck):
output, test = binariescheck
- run_elf_checks(test, FakePkg('fake'), get_full_path('no-soname.so'),
'/lib64/no-soname.so')
- out = output.print_results(output.results)
- assert 'no-soname /lib64/no-soname.so' in out
+ with FakePkg('fake') as pkg:
+ pkgfile = pkg.add_file(get_full_path('no-soname.so'), '/lib64/no-
soname.so')
+ run_elf_checks(test, pkg, pkgfile)
+ out = output.print_results(output.results)
+ assert 'no-soname /lib64/no-soname.so' in out
@pytest.mark.skipif(not IS_X86_64, reason='x86-64 only')
def test_invalid_soname(binariescheck):
output, test = binariescheck
- run_elf_checks(test, FakePkg('fake'), get_full_path('invalid-
soname.so'), '/lib64/invalid-soname.so')
- out = output.print_results(output.results)
- assert 'invalid-soname /lib64/invalid-soname.so' in out
- assert 'E: shlib-with-non-pic-code /lib64/invalid-soname.so' not in out
+ with FakePkg('fake') as pkg:
+ pkgfile = pkg.add_file(get_full_path('invalid-soname.so'),
'/lib64/invalid-soname.so')
+ run_elf_checks(test, pkg, pkgfile)
+ out = output.print_results(output.results)
+ assert 'invalid-soname /lib64/invalid-soname.so' in out
+ assert 'E: shlib-with-non-pic-code /lib64/invalid-soname.so' not in
out
@pytest.mark.skipif(not IS_I686 and (not IS_X86_64 or not HAS_32BIT_GLIBC),
reason='i686 glibc only')
def test_non_pic_code_library(binariescheck):
output, test = binariescheck
- run_elf_checks(test, FakePkg('fake'), get_full_path('non-pic-shared-
m32.so'), '/usr/lib/non-pic-shared-m32.so')
- out = output.print_results(output.results)
- assert 'E: shlib-with-non-pic-code' in out
+ with FakePkg('fake') as pkg:
+ pkgfile = pkg.add_file(get_full_path('non-pic-shared-m32.so'),
'/usr/lib/non-pic-shared-m32.so')
+ run_elf_checks(test, pkg, pkgfile)
+ out = output.print_results(output.results)
+ assert 'E: shlib-with-non-pic-code' in out
@pytest.mark.skipif(not IS_X86_64, reason='x86-64 only')
def test_no_ldconfig_symlink(binariescheck):
output, test = binariescheck
- run_elf_checks(test, FakePkg('libfake'), get_full_path('libutil-
2.29.so'), '/lib64/libutil-2.29.so')
- out = output.print_results(output.results)
- assert 'no-ldconfig-symlink /lib64/libutil-2.29.so' in out
- assert 'E: shlib-policy-name-error SONAME: libutil.so.1, expected
package suffix: 1' in out
+ with FakePkg('libfake') as pkg:
+ pkgfile = pkg.add_file(get_full_path('libutil-2.29.so'),
'/lib64/libutil-2.29.so')
+ run_elf_checks(test, pkg, pkgfile)
+ out = output.print_results(output.results)
+ assert 'no-ldconfig-symlink /lib64/libutil-2.29.so' in out
+ assert 'E: shlib-policy-name-error SONAME: libutil.so.1, expected
package suffix: 1' in out
@pytest.mark.skipif(not IS_X86_64, reason='x86-64 only')
def test_call_mktemp(binariescheck):
output, test = binariescheck
- run_elf_checks(test, FakePkg('fake'), get_full_path('call-mktemp'),
'/bin/call-mktemp')
- out = output.print_results(output.results)
- assert 'E: call-to-mktemp /bin/call-mktemp' in out
+ with FakePkg('fake') as pkg:
+ pkgfile = pkg.add_file(get_full_path('call-mktemp'), '/bin/call-
mktemp')
+ run_elf_checks(test, pkg, pkgfile)
+ out = output.print_results(output.results)
+ assert 'E: call-to-mktemp /bin/call-mktemp' in out
@pytest.mark.skipif(not IS_X86_64, reason='x86-64 only')
def test_call_setgroups(binariescheck):
output, test = binariescheck
- run_elf_checks(test, FakePkg('fake'), get_full_path('call-setgroups'),
'/bin/call-setgroups')
- out = output.print_results(output.results)
- assert 'E: missing-call-to-setgroups-before-setuid /bin/call-setgroups'
in out
+ with FakePkg('fake') as pkg:
+ pkgfile = PkgFile('/bin/call-setgroups')
+ pkgfile.path = get_full_path('call-setgroups')
+ pkg.files[pkgfile.name] = pkgfile
+ run_elf_checks(test, pkg, pkgfile)
+ out = output.print_results(output.results)
+ assert 'E: missing-call-to-setgroups-before-setuid /bin/call-
setgroups' in out
@pytest.mark.skipif(not IS_X86_64, reason='x86-64 only')
def test_call_gethostbyname(binariescheck):
output, test = binariescheck
- run_elf_checks(test, FakePkg('fake'), get_full_path('hostname'),
'/usr/bin/hostname')
- out = output.print_results(output.results)
- assert 'W: binary-or-shlib-calls-gethostbyname' in out
+ with FakePkg('fake') as pkg:
+ pkgfile = pkg.add_file(get_full_path('hostname'),
'/usr/bin/hostname')
+ run_elf_checks(test, pkg, pkgfile)
+ out = output.print_results(output.results)
+ assert 'W: binary-or-shlib-calls-gethostbyname' in out
@pytest.mark.skipif(not IS_X86_64, reason='x86-64 only')
def test_missing_dependency(binariescheck):
output, test = binariescheck
- run_elf_checks(test, FakePkg('fake'), get_full_path('no-
dependency.so'), '/lib64/no-dependency.so')
- out = output.print_results(output.results)
- assert 'E: shared-library-without-dependency-information' in out
+ with FakePkg('fake') as pkg:
+ pkgfile = pkg.add_file(get_full_path('no-dependency.so'),
'/lib64/no-dependency.so')
+ run_elf_checks(test, pkg, pkgfile)
+ out = output.print_results(output.results)
+ assert 'E: shared-library-without-dependency-information' in out
def test_bca_files(binariescheck):
output, test = binariescheck
- run_elf_checks(test, FakePkg('fake'),
get_full_path('libkleeRuntimeFreeStanding.bca'),
'/usr/lib64/klee/runtime/libkleeRuntimeFreeStanding.bca')
- out = output.print_results(output.results)
- assert 'E: ' not in out
+ with FakePkg('fake') as pkg:
+ pkgfile =
pkg.add_file(get_full_path('libkleeRuntimeFreeStanding.bca'),
'/usr/lib64/klee/runtime/libkleeRuntimeFreeStanding.bca')
+ run_elf_checks(test, pkg, pkgfile)
+ out = output.print_results(output.results)
+ assert 'E: ' not in out
@pytest.mark.skipif(not IS_X86_64, reason='x86-64 only')
def test_shlib_policy_name_error(binariescheck):
output, test = binariescheck
- run_elf_checks(test, FakePkg('libgame'), get_full_path('libgame.so'),
'/lib64/libgame.so')
- out = output.print_results(output.results)
- assert 'libgame: E: shlib-policy-name-error SONAME: libgame2-
1.9.so.10.0.0, expected package suffix: 1_9-10_0_0' in out
+ with FakePkg('libgame') as pkg:
+ pkgfile = pkg.add_file(get_full_path('libgame.so'),
'/lib64/libgame.so')
+ run_elf_checks(test, pkg, pkgfile)
+ out = output.print_results(output.results)
+ print(out)
+ assert 'libgame: E: shlib-policy-name-error SONAME: libgame2-
1.9.so.10.0.0, expected package suffix: 1_9-10_0_0' in out
diff -Nru rpmlint-2.2.0+ds1/test/test_spellchecking.py rpmlint-
2.3.0+ds1/test/test_spellchecking.py
--- rpmlint-2.2.0+ds1/test/test_spellchecking.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/test/test_spellchecking.py 2022-05-29
12:46:55.000000000 -0400
@@ -4,6 +4,11 @@
from Testing import HAS_CZECH_DICTIONARY, HAS_ENGLISH_DICTIONARY
+def get_suggestions(suggestion):
+ suggestion = suggestion.split(' -> ')[-1]
+ return sorted(suggestion.split(', '))
+
+
@pytest.mark.skipif(not rpmlint.spellcheck.ENCHANT, reason='Missing enchant
bindings')
@pytest.mark.skipif(not HAS_ENGLISH_DICTIONARY, reason='Missing English
dictionary')
def test_spelldict(capsys):
@@ -45,13 +50,15 @@
text = "I don't think tihs tetx is correct English"
result = spell.spell_check(text, 'Description({}):')
assert len(result) == 2
- assert result['tihs'] == 'Description(en_US): tihs -> this, hits, ties'
+ assert result['tihs'].startswith('Description(en_US): tihs -> ')
+ assert get_suggestions(result['tihs']) == ['hits', 'this', 'ties']
# different language, one typo
text = 'Příčerně žluťoučký kůň'
result = spell.spell_check(text, 'Summary({}):', 'cs_CZ')
assert len(result) == 1
- assert result['Příčerně'] == 'Summary(cs_CZ): Příčerně -> Příčetně,
Příčeně, Příšerně'
+ assert result['Příčerně'].startswith('Summary(cs_CZ): Příčerně -> ')
+ assert get_suggestions(result['Příčerně']) == ['Příčeně', 'Příčetně',
'Příšerně']
# non-existing language, should return nothing:
text = 'Weird english text'
diff -Nru rpmlint-2.2.0+ds1/test/test_tags.py rpmlint-
2.3.0+ds1/test/test_tags.py
--- rpmlint-2.2.0+ds1/test/test_tags.py 2021-12-12 09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/test/test_tags.py 2022-05-29 12:46:55.000000000 -0400
@@ -141,9 +141,10 @@
test.check(get_tested_package(package, tmpdir))
out = output.print_results(output.results)
# Test if package has a summary longer than 80 characters
- assert 'E: summary-too-long lorem Ipsum is simply dummy text of the
printing and typesetting industry' in out
+ assert 'E: summary-too-long' in out
# Test if package has leading space at the beginning of the summary
- assert 'E: summary-has-leading-spaces lorem Ipsum is simply dummy
text of the printing and typesetting industry' in out
+ # where non-breaking space is used (U+00A0).
+ assert b'E: summary-has-leading-spaces \xc2\xa0\xc2\xa0lorem'.decode()
in out
# Test if package has a shorter description than Summary
assert 'W: description-shorter-than-summary' in out
# Test if a package has a Version: tag
diff -Nru rpmlint-2.2.0+ds1/tools/generate-fedora-users-groups.py rpmlint-
2.3.0+ds1/tools/generate-fedora-users-groups.py
--- rpmlint-2.2.0+ds1/tools/generate-fedora-users-groups.py 2021-12-12
09:54:14.000000000 -0500
+++ rpmlint-2.3.0+ds1/tools/generate-fedora-users-groups.py 2022-05-29
12:46:55.000000000 -0400
@@ -1,5 +1,5 @@
#!/usr/bin/python3
-""" This sciptt is used to generate values for
+""" This script is used to generate values for
config/Fedora/configs/Fedora/users-groups.toml
"""
import os
signature.asc
Description: This is a digitally signed message part

