Rewrite SignatureCheck and add tests

Separate SignatureCheck to functions and add tests. Also, rewrite the
logic as SignatureCheck was obsolete and didn't work properly
(e.g. it searched for "MISSING KEYS" and "pgp|gpg" strings that are no
longer used in 'rpm -K' output).

Now it checks the output of 'rpm -Kv' which provides more detailed output.
Thanks to that we could add another check ('E: invalid-signature' that
checks if the signature is corrupted or not).

The test_signature.py file was added so now all 3 signature checks are
tested properly.
This commit is contained in:
Kristyna Streitova 2020-04-08 23:01:49 +02:00 committed by Tomáš Chvátal
parent aa433df1ce
commit 297f09e745
7 changed files with 108 additions and 25 deletions

View File

@ -1,11 +1,3 @@
#############################################################################
# File : SignatureCheck.py
# Package : rpmlint
# Author : Frederic Lepied
# Created on : Thu Oct 7 17:06:14 1999
# Purpose : check the presence of a PGP signature.
#############################################################################
import re
from rpmlint.checks.AbstractCheck import AbstractCheck
@ -13,21 +5,56 @@ from rpmlint.helpers import print_warning
class SignatureCheck(AbstractCheck):
pgp_regex = re.compile(r'pgp|gpg', re.IGNORECASE)
unknown_key_regex = re.compile(r'\(MISSING KEYS:(?:\([^)]+\))?\s+([^\)]+)\)')
"""
Checks for PGP signature in the package.
It checks if the signature is present, known (imported in RPM DB) and
valid. It uses 'rpm -Kv' command than returns detailed information about
the package digests and signature.
"""
any_sig_regex = re.compile(r'[Ss]ignature, key ID')
nokey_sig_regex = re.compile(r'[Ss]ignature, key ID ([\w\d]*): NOKEY')
invalid_sig_regex = re.compile(r'invalid OpenPGP signature')
def check(self, pkg):
res = pkg.checkSignature()
if not res or res[0] != 0:
if res and res[1]:
kres = SignatureCheck.unknown_key_regex.search(res[1])
else:
kres = None
if kres:
self.output.add_info('E', pkg, 'unknown-key', kres.group(1))
else:
print_warning('Error checking signature of %s: %s' %
(pkg.filename, res[1]))
else:
if not SignatureCheck.pgp_regex.search(res[1]):
self.output.add_info('E', pkg, 'no-signature')
retcode, output = pkg.checkSignature()
# Skip all signature checks if checkSignature output is empty
if output is None:
print_warning(f'No output from checkSignature() for '
f'{pkg.filename}. Skipping signature checks.')
return
self._check_no_signature(pkg, retcode, output)
self._check_unknown_key(pkg, retcode, output)
self._check_invalid_signature(pkg, retcode, output)
def _check_no_signature(self, pkg, retcode, output):
"""
Check if the package contains a signature.
Print an error if there is no signature present. That means that
there is no mention about any signature in the 'rpm -Kv' output.
"""
if retcode == 0 and not SignatureCheck.any_sig_regex.search(output):
self.output.add_info('E', pkg, 'no-signature')
def _check_unknown_key(self, pkg, retcode, output):
"""
Check if the public key is imported in the RPM database.
Print an error if it's not imported and signature is therefore unknown.
"""
if retcode == 1:
nokey = SignatureCheck.nokey_sig_regex.search(output)
if nokey and not SignatureCheck.invalid_sig_regex.search(output):
self.output.add_info('E', pkg, 'unknown-key', nokey.group(1))
def _check_invalid_signature(self, pkg, retcode, output):
"""
Check if the signature is valid.
Print an error if the signature is corrupted.
"""
if retcode == 1 and SignatureCheck.invalid_sig_regex.search(output):
self.output.add_info('E', pkg, 'invalid-signature')

View File

@ -5,3 +5,6 @@ unknown-key="""
The package was signed, but with an unknown key. See the rpm --import option
for more information.
"""
invalid-signature="""
The package was signed, but the signature is corrupted.
"""

View File

@ -476,7 +476,9 @@ class Pkg(AbstractPkg):
return dirname
def checkSignature(self):
ret = subprocess.run(('rpm', '-K', self.filename), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=ENGLISH_ENVIROMENT)
ret = subprocess.run(('rpm', '-Kv', self.filename),
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
env=ENGLISH_ENVIROMENT)
text = ret.stdout.decode()
if text.endswith('\n'):
text = text[:-1]

Binary file not shown.

Binary file not shown.

Binary file not shown.

51
test/test_signature.py Normal file
View File

@ -0,0 +1,51 @@
import pytest
from rpmlint.checks.SignatureCheck import SignatureCheck
from rpmlint.filter import Filter
from Testing import CONFIG, get_tested_package
@pytest.fixture(scope='function', autouse=True)
def signaturecheck():
CONFIG.info = True
output = Filter(CONFIG)
test = SignatureCheck(CONFIG, output)
return output, test
# The signature was stripped via "rpmsign --delsign <package>"
@pytest.mark.parametrize('package', ['binary/no-signature'])
def test_no_signature(tmpdir, package, signaturecheck):
output, test = signaturecheck
test.check(get_tested_package(package, tmpdir))
out = output.print_results(output.results)
assert 'E: no-signature' in out
assert 'E: unknown-key' not in out
assert 'E: invalid-signature' not in out
# The test rpm was signed with gpg key created for this purpose that is not
# imported in rpm db and therefore unknown-key error should be thrown
@pytest.mark.parametrize('package', ['binary/unknown-key'])
def test_unknown_key(tmpdir, package, signaturecheck):
output, test = signaturecheck
test.check(get_tested_package(package, tmpdir))
out = output.print_results(output.results)
assert 'E: unknown-key 31fdc502' in out
assert 'E: no-signature' not in out
assert 'E: invalid-signature' not in out
# The test rpm hello-2.0-1.x86_64-signed.rpm was taken from
# https://github.com/rpm-software-management/rpm/blob/master/tests/data/RPMS/
# and then the signature was corrupted by running "dd if=/dev/zero
# of=hello-2.0-1.x86_64-signed.rpm conv=notrunc bs=1 seek=264 count=6
# 2> /dev/null"
@pytest.mark.parametrize('package', ['binary/hello'])
def test_invalid_signature(tmpdir, package, signaturecheck):
output, test = signaturecheck
test.check(get_tested_package(package, tmpdir))
out = output.print_results(output.results)
assert 'E: invalid-signature' in out
assert 'E: no-signature' not in out
assert 'E: unknown-key' not in out