From 984e2f5146e87b4b62424eb43f42c8898e2c25c6 Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Aug 22 2025 15:04:39 +0000
Subject: [PATCH 1/4] extra validation for sigkey values


---

diff --git a/kojihub/kojihub.py b/kojihub/kojihub.py
index 69d6eaf..9b0222f 100644
--- a/kojihub/kojihub.py
+++ b/kojihub/kojihub.py
@@ -8273,7 +8273,7 @@ def add_rpm_sig(an_rpm, sighdr, sigkey=None):
     if not os.path.isdir(builddir):
         raise koji.GenericError("No such directory: %s" % builddir)
     if sigkey is not None:
-        verify_name_internal(sigkey)
+        validate_sigkey_value(sigkey)
 
     # verify sigmd5 matches rpm and pick sigkey if needed
     rawhdr = koji.RawHeader(sighdr)
@@ -8324,10 +8324,18 @@ def add_rpm_sig(an_rpm, sighdr, sigkey=None):
                               sigkey=sigkey, sighash=sighash, build=binfo, rpm=rinfo)
 
 
+def validate_sigkey_value(sigkey):
+    convert_value(sigkey, cast=str, check_only=True)
+    if '/' in sigkey or sigkey.startswith('.'):
+        # not allowed because the value is used in a path
+        raise koji.GenericError("Invalid sigkey value")
+    verify_name_internal(sigkey)
+
+
 def rename_rpm_sig(rpminfo, oldkey, newkey):
     """Change the sigkey for an rpm signature"""
 
-    verify_name_internal(newkey)
+    validate_sigkey_value(newkey)
     rinfo = get_rpm(rpminfo, strict=True)
     nvra = "%(name)s-%(version)s-%(release)s.%(arch)s" % rinfo
     if rinfo['external_repo_id']:

From 8ba268eecc473b0e6744e16c9f297db89a7a92ea Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Aug 22 2025 15:04:39 +0000
Subject: [PATCH 2/4] tweak unit test


---

diff --git a/tests/test_hub/test_add_rpm_sig.py b/tests/test_hub/test_add_rpm_sig.py
index 3505d3d..fcf62b0 100644
--- a/tests/test_hub/test_add_rpm_sig.py
+++ b/tests/test_hub/test_add_rpm_sig.py
@@ -146,15 +146,22 @@ class TestAddRPMSig(unittest.TestCase):
     def test_add_rpm_sig_bad_sigkey(self):
         """bad sigkey failure case"""
         sighdr = 'SIG HEADER 99'
-        self.isdir.side_effect = [True]
-        self.get_rpm.side_effect = [{'build_id': 100, 'external_repo_id': None}]
-
-        with self.assertRaises(koji.GenericError):
-            kojihub.add_rpm_sig(1, sighdr, sigkey='foo/bar !')
+        self.isdir.return_value = True
+        self.get_rpm.return_value = {'build_id': 100, 'external_repo_id': None}
+
+        badkeys = [
+            'white space',
+            'badchar!',
+            '.hidden',
+            'sub/dir',
+        ]
+        for sigkey in badkeys:
+            with self.assertRaises(koji.GenericError):
+                kojihub.add_rpm_sig(1, sighdr, sigkey=sigkey)
 
         self.assertEqual(len(self.inserts), 0)
         self.open.assert_not_called()
-        self.isdir.assert_called_once()
+        self.isdir.assert_called()
 
 
 class TestScanHeaderOnly(unittest.TestCase):

From 6ea68825c7270d47c833b0ca7c24673b40c16c04 Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Aug 22 2025 15:04:39 +0000
Subject: [PATCH 3/4] allow sigkey=""


---

diff --git a/kojihub/kojihub.py b/kojihub/kojihub.py
index 9b0222f..af6e398 100644
--- a/kojihub/kojihub.py
+++ b/kojihub/kojihub.py
@@ -8326,6 +8326,9 @@ def add_rpm_sig(an_rpm, sighdr, sigkey=None):
 
 def validate_sigkey_value(sigkey):
     convert_value(sigkey, cast=str, check_only=True)
+    if sigkey == '':
+        # special case for the unsigned sig header
+        return
     if '/' in sigkey or sigkey.startswith('.'):
         # not allowed because the value is used in a path
         raise koji.GenericError("Invalid sigkey value")

From 2734d5276fd92aa6388961dc75d545f008b870a7 Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Aug 22 2025 15:04:39 +0000
Subject: [PATCH 4/4] require lowercase sigkey


---

diff --git a/kojihub/kojihub.py b/kojihub/kojihub.py
index af6e398..c848bc9 100644
--- a/kojihub/kojihub.py
+++ b/kojihub/kojihub.py
@@ -8332,6 +8332,10 @@ def validate_sigkey_value(sigkey):
     if '/' in sigkey or sigkey.startswith('.'):
         # not allowed because the value is used in a path
         raise koji.GenericError("Invalid sigkey value")
+    if sigkey != sigkey.lower():
+        # we require lowercase because koji has historically forced lowercase for this value
+        # e.g. in query_rpm_sigs
+        raise koji.GenericError("Invalid sigkey value. Must be lowercase")
     verify_name_internal(sigkey)
 
 
diff --git a/tests/test_hub/test_add_rpm_sig.py b/tests/test_hub/test_add_rpm_sig.py
index fcf62b0..c40067a 100644
--- a/tests/test_hub/test_add_rpm_sig.py
+++ b/tests/test_hub/test_add_rpm_sig.py
@@ -154,6 +154,7 @@ class TestAddRPMSig(unittest.TestCase):
             'badchar!',
             '.hidden',
             'sub/dir',
+            'hasUPPERcase',
         ]
         for sigkey in badkeys:
             with self.assertRaises(koji.GenericError):

