From d0feef9e641e42b21f378a396048c402b0a12ae5 Mon Sep 17 00:00:00 2001 From: tdruez Date: Wed, 7 Jan 2026 10:20:26 +0400 Subject: [PATCH] Fix PURL comparison in get_purldb_entries Signed-off-by: tdruez --- component_catalog/models.py | 8 ++++---- component_catalog/tests/test_models.py | 15 +++++++++++++++ dje/tests/test_utils.py | 19 +++++++++++++++++++ dje/utils.py | 7 +++++++ 4 files changed, 45 insertions(+), 4 deletions(-) diff --git a/component_catalog/models.py b/component_catalog/models.py index 074858ac..1c8719cc 100644 --- a/component_catalog/models.py +++ b/component_catalog/models.py @@ -75,9 +75,9 @@ from dje.models import ParentChildRelationshipModel from dje.models import ReferenceNotesMixin from dje.tasks import logger as tasks_logger -from dje.utils import get_plain_purl from dje.utils import is_purl_str from dje.utils import merge_common_non_empty_values +from dje.utils import plain_purls_equal from dje.utils import set_fields_from_object from dje.validators import generic_uri_validator from dje.validators import validate_url_segment @@ -2618,13 +2618,13 @@ def get_purldb_entries(self, user, max_request_call=0, timeout=10): return [] # Cleanup the PurlDB entries: - # Packages with different "plain" PURL are excluded. The qualifiers and - # subpaths are not involved in this comparison. + # Packages with different "plain" PURL are excluded. + # The qualifiers and subpaths are not involved in this comparison. if package_url: purldb_entries = [ entry for entry in purldb_entries - if get_plain_purl(entry.get("purl", "")) == package_url + if plain_purls_equal(entry.get("purl"), package_url) ] return purldb_entries diff --git a/component_catalog/tests/test_models.py b/component_catalog/tests/test_models.py index edb2ec2b..6e348beb 100644 --- a/component_catalog/tests/test_models.py +++ b/component_catalog/tests/test_models.py @@ -2622,12 +2622,27 @@ def test_package_model_get_purldb_entries(self, mock_find_packages): mock_find_packages.return_value = None purldb_entries = package1.get_purldb_entries(user=self.user) + self.assertEqual([], purldb_entries) mock_find_packages.return_value = [purldb_entry1, purldb_entry2, purldb_entry3] purldb_entries = package1.get_purldb_entries(user=self.user) # 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_plain_purls_equal(self, mock_find_packages): + purl1 = "pkg:maven/core/jackson-core@2.18.3?type=jar" + purl2 = "pkg:maven/core/jackson-core@2.18.3?classifier=sources&type=jar" + + package1_purl = "pkg:maven/core/jackson-core@2.18.3?some=qualifier" + package1 = make_package(self.dataspace, package_url=package1_purl) + purldb_entry1 = {"purl": purl1} + purldb_entry2 = {"purl": purl2} + + mock_find_packages.return_value = [purldb_entry1, purldb_entry2] + purldb_entries = package1.get_purldb_entries(user=self.user) + self.assertEqual([purldb_entry1, purldb_entry2], purldb_entries) + @mock.patch("component_catalog.models.Package.get_purldb_entries") def test_package_model_update_from_purldb(self, mock_get_purldb_entries): purldb_entry = { diff --git a/dje/tests/test_utils.py b/dje/tests/test_utils.py index e97557fb..317c4208 100644 --- a/dje/tests/test_utils.py +++ b/dje/tests/test_utils.py @@ -42,6 +42,7 @@ from dje.utils import merge_common_non_empty_values from dje.utils import merge_relations from dje.utils import normalize_newlines_as_CR_plus_LF +from dje.utils import plain_purls_equal from dje.utils import remove_field_from_query_dict from dje.utils import str_to_id_list from dje.views import get_previous_next @@ -528,6 +529,7 @@ def test_utils_is_purl_fragment(self): self.assertFalse(is_purl_fragment(fragment), msg=fragment) def test_utils_get_plain_purl(self): + self.assertEqual("", get_plain_purl(None)) self.assertEqual("", get_plain_purl("")) self.assertEqual("not:a/purl", get_plain_purl("not:a/purl")) self.assertEqual("not:a/purl", get_plain_purl("not:a/purl")) @@ -536,6 +538,23 @@ def test_utils_get_plain_purl(self): "pkg:npm/is-npm@1.0.0", get_plain_purl("pkg:npm/is-npm@1.0.0?qualifier=1#frament") ) + def test_utils_plain_purls_equal(self): + purl1 = "pkg:npm/is-npm@1.0.0" + purl2 = "pkg:npm/is-npm@1.0.0" + self.assertTrue(plain_purls_equal(purl1, purl2)) + + purl1 = "pkg:npm/is-npm@1.0.0?qual=1#subpath" + purl2 = "pkg:npm/is-npm@1.0.0" + self.assertTrue(plain_purls_equal(purl1, purl2)) + + purl1 = "pkg:npm/is-npm@1.0.0?qual=1" + purl2 = "pkg:npm/is-npm@1.0.0?qual=2" + self.assertTrue(plain_purls_equal(purl1, purl2)) + + purl1 = "pkg:npm/is-npm@1.0.0" + purl2 = "pkg:npm/is-npm@2.0.0" + self.assertFalse(plain_purls_equal(purl1, purl2)) + def test_utils_localized_datetime(self): self.assertIsNone(localized_datetime(None)) diff --git a/dje/utils.py b/dje/utils.py index c628fdd9..08bbfdd9 100644 --- a/dje/utils.py +++ b/dje/utils.py @@ -658,9 +658,16 @@ def is_purl_fragment(string): def get_plain_purl(purl_str): """Remove the qualifiers and subpath from the `purl_str```.""" + if not purl_str: + return "" return purl_str.split("?")[0] +def plain_purls_equal(purl1, purl2): + """Check if two PURLs are equal, ignoring qualifiers and subpath.""" + return get_plain_purl(purl1) == get_plain_purl(purl2) + + def remove_empty_values(input_dict): """ Return a new dict not including empty value entries from `input_dict`.