Repository: fedora-infra/bodhi

Issue:
Bodhi should reject builds done from .src.rpms
I found a Rawhide update - https://bodhi.fedoraproject.org/updates/FEDORA-2023-7c0c58a874 , but it's fixed now - which was stuck in gating because greenwave was 502ing and tripping up Bodhi (see https://github.com/release-engineering/greenwave/issues/218 for the greenwave side here). Kevin says it's because he mistakenly did one of the builds from a .src.rpm - regular packagers and even proven packagers can't do that, but Koji admins like Kevin can.

I'm going to try and make greenwave/Bodhi handle this situation a bit more elegantly, but also, we think Bodhi should probably just reject such builds out of hand. They shouldn't be allowed into updates. Kevin can probably provide more info on how to distinguish such a build from a 'normal' one.


Discussion:
You can tell from the 'Source' field of the build... for example: 

Source: git+https://src.fedoraproject.org/rpms/collectd.git#fcee6f7d11dd3cea89dedb9fb9dcd648354610dc

vs

Source: collectd-5.12.0-34.fc40.src.rpm


Resolving patch:
diff --git a/bodhi-server/bodhi/server/buildsys.py b/bodhi-server/bodhi/server/buildsys.py
--- a/bodhi-server/bodhi/server/buildsys.py
+++ b/bodhi-server/bodhi/server/buildsys.py
@@ -95,6 +95,7 @@ class DevBuildsys:
                    'owner_id': 388,
                    'owner_name': 'lmacken',
                    'package_id': 8,
+                   'source': 'git+https://src.fedoraproject.org/rpms/foo.git#abc',
                    'state': 1,
                    'tag_id': 19,
                    'task_id': 127621}
@@ -210,7 +211,8 @@ def getBuild(self, build='TurboGears-1.0.2.2-2.fc17', other=False, testing=False
                     'tag_name': 'f17-build-side-7777',
                     'version': '3.0',
                     'id': 16061,
-                    'task_id': 15051}
+                    'task_id': 15051,
+                    'source': 'git+https://src.fedoraproject.org/rpms/gnome-backgrounds.git#abc'}
 
         theid = 16058
         if other and not testing:
@@ -225,6 +227,7 @@ def getBuild(self, build='TurboGears-1.0.2.2-2.fc17', other=False, testing=False
 
         name, version, release = build.rsplit("-", 2)
         release_tokens = release.split(".")
+        data['source'] = f'git+https://src.fedoraproject.org/rpms/{name}.git#abc'
 
         for token in release_tokens:
             # Starting to hardcode some dev buildsys bits for docker.
@@ -247,9 +250,11 @@ def getBuild(self, build='TurboGears-1.0.2.2-2.fc17', other=False, testing=False
 
                 if token.endswith("flatpak"):
                     format_data['repository'] = name
+                    data['source'] = f'git+https://src.fedoraproject.org/flatpaks/{name}.git#abc'
                 else:
                     tag = "f%s-updates-testing" % token.replace("fc", "").replace("container", "")
                     format_data['repository'] = "{}/{}".format(fedora_release, name)
+                    data['source'] = f'https://src.fedoraproject.org/container/{name}.git#abc'
 
                 data['extra'] = {
                     'typeinfo': {
@@ -275,6 +280,7 @@ def getBuild(self, build='TurboGears-1.0.2.2-2.fc17', other=False, testing=False
                 data['extra'] = {
                     'typeinfo': {'module': {'more': 'mbs stuff goes here'}}
                 }
+                data['source'] = f'https://src.fedoraproject.org/modules/{name}.git?#abc'
                 break
 
             if token.startswith("fc"):
diff --git a/bodhi-server/bodhi/server/config.py b/bodhi-server/bodhi/server/config.py
--- a/bodhi-server/bodhi/server/config.py
+++ b/bodhi-server/bodhi/server/config.py
@@ -604,6 +604,9 @@ class BodhiConfig(dict):
         'test_gating.url': {
             'value': '',
             'validator': str},
+        'trusted_build_sources': {
+            'value': [],
+            'validator': _generate_list_validator()},
         'update_notes_maxlength': {
             'value': 10000,
             'validator': int},
diff --git a/bodhi-server/bodhi/server/validators.py b/bodhi-server/bodhi/server/validators.py
--- a/bodhi-server/bodhi/server/validators.py
+++ b/bodhi-server/bodhi/server/validators.py
@@ -199,6 +199,8 @@ def validate_build_nvrs(request, **kwargs):
         request (pyramid.request.Request): The current request.
         kwargs (dict): The kwargs of the related service definition. Unused.
     """
+    trusted_sources = config.get('trusted_build_sources', [])
+
     for build in request.validated.get('builds') or []:  # cope with builds being None
         try:
             cache_nvrs(request, build)
@@ -212,6 +214,13 @@ def validate_build_nvrs(request, **kwargs):
                         'body', 'builds',
                         f"Can't create update from tag for release"
                         f" '{release.name}' composed by Bodhi.")
+
+            if trusted_sources:
+                build_source = request.buildinfo[build]['info']['source']
+                if not any(build_source.startswith(source) for source in trusted_sources):
+                    request.validated['builds'] = []
+                    request.errors.add('body', 'builds',
+                                       f'{build} was not built from an allowed source')
         except ValueError:
             request.validated['builds'] = []
             request.errors.add('body', 'builds', 'Build does not exist: %s' % build)


Test patch:
diff --git a/bodhi-server/tests/test_validators.py b/bodhi-server/tests/test_validators.py
--- a/bodhi-server/tests/test_validators.py
+++ b/bodhi-server/tests/test_validators.py
@@ -975,6 +975,7 @@ def test_build_from_release_composed_by_bodhi(self, mock_cache_nvrs):
                                   'builds': ['foo-1-1.f17']}
         self.request.buildinfo = {'foo-1-1.f17': {
             'nvr': ('foo', '1-1', 'f17'),
+            'info': {'source': 'git+https://src.fedoraproject.org/rpms/foo.git#aabbccdd'}
         }}
         self.release.composed_by_bodhi = True
         validators.validate_build_nvrs(self.request)
@@ -986,6 +987,40 @@ def test_build_from_release_composed_by_bodhi(self, mock_cache_nvrs):
                  f" '{self.release.name}' composed by Bodhi."}
         ]
 
+    @mock.patch.dict(
+        'bodhi.server.validators.config',
+        {'trusted_build_sources': ['git+https://src.fedoraproject.org/']})
+    @mock.patch('bodhi.server.validators.cache_nvrs')
+    def test_build_from_distgit(self, mock_cache_nvrs):
+        """Assert that a build from distgit is allowed."""
+        self.request.validated = {'builds': ['foo-1-1.f17']}
+        self.request.buildinfo = {'foo-1-1.f17': {
+            'nvr': ('foo', '1-1', 'f17'),
+            'info': {'source': 'git+https://src.fedoraproject.org/rpms/foo.git#aabbccdd'}
+        }}
+        validators.validate_build_nvrs(self.request)
+
+        assert self.request.errors == []
+
+    @mock.patch.dict(
+        'bodhi.server.validators.config',
+        {'trusted_build_sources': ['git+https://src.fedoraproject.org/']})
+    @mock.patch('bodhi.server.validators.cache_nvrs')
+    def test_build_from_srpm(self, mock_cache_nvrs):
+        """Assert that a build from srpm is not allowed."""
+        self.request.validated = {'builds': ['foo-1-1.f17']}
+        self.request.buildinfo = {'foo-1-1.f17': {
+            'nvr': ('foo', '1-1', 'f17'),
+            'info': {'source': 'foo-1-1.f17.src.rpm'}
+        }}
+        validators.validate_build_nvrs(self.request)
+
+        assert self.request.errors == [
+            {'location': 'body', 'name': 'builds',
+             'description':
+                 "foo-1-1.f17 was not built from an allowed source"}
+        ]
+
 
 class TestValidateBuildTags(BasePyTestCase):
     """Test the validate_build_tags() function."""