From 619a22844cd8a35f49a95243ec937286b4da286d Mon Sep 17 00:00:00 2001 From: Amitesh Gupta Date: Wed, 15 Jul 2026 11:30:45 +0530 Subject: [PATCH] fix: Package.get_purldb_entries() built a purl payload but placed it Addresses #462. --- component_catalog/models.py | 12 ++++++---- component_catalog/tests/test_models.py | 31 ++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/component_catalog/models.py b/component_catalog/models.py index 55902b65..a1b3e699 100644 --- a/component_catalog/models.py +++ b/component_catalog/models.py @@ -2585,10 +2585,13 @@ def get_purldb_entries(self, user, max_request_call=0, timeout=10): 3. Package URL - Broadest match, may return multiple versions/variants A `max_request_call` integer can be provided to limit the number of - HTTP requests made to the PackageURL server. + HTTP requests made to the PackageURL server for the hash and download + URL fields. By default, one request will be made per field until a match is found. Providing max_request_call=1 will stop after the first request, even is nothing was found. + The Package URL is always used as a final fallback, regardless of + `max_request_call`, as it is the broadest match available. """ payloads = [] purldb_entries = [] @@ -2602,17 +2605,18 @@ def get_purldb_entries(self, user, max_request_call=0, timeout=10): payloads.append({"md5": self.md5}) if self.download_url: payloads.append({"download_url": self.download_url}) - if package_url: - payloads.append({"purl": package_url}) purldb = PurlDB(user.dataspace) for index, payload in enumerate(payloads): if max_request_call and index >= max_request_call: - return + break if purldb_entries := purldb.find_packages(payload, timeout): break + if not purldb_entries and package_url: + purldb_entries = purldb.find_packages({"purl": package_url}, timeout) + if not purldb_entries: return [] diff --git a/component_catalog/tests/test_models.py b/component_catalog/tests/test_models.py index ecb9fdc5..bfdb3ad9 100644 --- a/component_catalog/tests/test_models.py +++ b/component_catalog/tests/test_models.py @@ -2631,6 +2631,37 @@ def test_package_model_get_purldb_entries(self, mock_find_packages): # The purldb_entry2 is excluded as the PURL differs self.assertEqual([purldb_entry1, purldb_entry2], purldb_entries) + @mock.patch("dejacode_toolkit.purldb.PurlDB.find_packages") + def test_package_model_get_purldb_entries_max_request_call_purl_fallback( + self, mock_find_packages + ): + """ + A Package without hashes, whose `download_url` does not match anything in + PurlDB, should still be matched on its `purl` even when `max_request_call` + limits the number of PurlDB requests. See #462. + """ + purl1 = "pkg:golang/github.com/pkg/errors@0.9.1" + package1 = make_package( + self.dataspace, + package_url=purl1, + download_url="https://example.com/inferred/download/url", + ) + purldb_entry1 = { + "purl": purl1, + "type": "golang", + "name": "errors", + "version": "0.9.1", + } + + def find_packages_side_effect(payload, timeout=None): + if payload.get("purl") == purl1: + return [purldb_entry1] + return None + + mock_find_packages.side_effect = find_packages_side_effect + purldb_entries = package1.get_purldb_entries(user=self.user, max_request_call=1) + self.assertEqual([purldb_entry1], purldb_entries) + @mock.patch("dejacode_toolkit.purldb.PurlDB.find_packages") def test_package_model_get_purldb_entries_plain_purls_equal(self, mock_find_packages): purl1 = "pkg:maven/core/jackson-core@2.18.3?type=jar"