diff --git a/docs/source/conf.py b/docs/source/conf.py index 34deb1dab..a6247eafa 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -41,6 +41,7 @@ "https://ftp.suse.com/pub/projects/security/yaml/suse-cvss-scores.yaml", "http://ftp.suse.com/pub/projects/security/yaml/", r"https://nixos\.wiki/", # NixOS wiki blocks CI bots with 403 + "https://usn.ubuntu.com/usn-db/database-all.json.bz2", ] # Add any Sphinx extension module names here, as strings. They can be diff --git a/requirements-dev.txt b/requirements-dev.txt index 9600ada49..8eb058b95 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -153,7 +153,7 @@ typecode==30.2.0 typecode-libmagic==5.39.210531 typing_extensions==4.13.0 tzlocal==5.3.1 -univers==30.12.0 +univers==32.0.1 uritemplate==4.2.0 urllib3==2.6.3 uvicorn==0.42.0 diff --git a/requirements.txt b/requirements.txt index 0a2c1ac1e..5940ea835 100644 --- a/requirements.txt +++ b/requirements.txt @@ -80,7 +80,7 @@ typecode==30.2.0 typecode-libmagic==5.39.210531 typing_extensions==4.13.0 tzlocal==5.3.1 -univers==30.12.0 +univers==32.0.1 uritemplate==4.2.0 urllib3==2.6.3 webencodings==0.5.1 diff --git a/setup.cfg b/setup.cfg index 6e2d40a81..6f3543f7f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -72,7 +72,7 @@ install_requires = #essentials packageurl-python==0.17.6 - univers==30.12.0 + univers==32.0.1 license-expression==30.3.1 # file and data formats diff --git a/vulnerabilities/migrations/0125_clean_vers_range_without_constraints.py b/vulnerabilities/migrations/0125_clean_vers_range_without_constraints.py new file mode 100644 index 000000000..1e8c05aa6 --- /dev/null +++ b/vulnerabilities/migrations/0125_clean_vers_range_without_constraints.py @@ -0,0 +1,29 @@ +# Generated by Django 5.2.11 on 2026-05-01 10:38 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("vulnerabilities", "0124_advisoryv2_remove_malformed_aliases_and_dvisory_id"), + ] + + def clean_empty_vers_range(apps, _): + ImpactedPackage = apps.get_model("vulnerabilities", "ImpactedPackage") + + ImpactedPackage.objects.filter(affecting_vers__regex=r"vers:[^/]+/$").update( + affecting_vers=None + ) + ImpactedPackage.objects.filter(fixed_vers__regex=r"vers:[^/]+/$").update(fixed_vers=None) + + ImpactedPackage.objects.filter( + affecting_vers__isnull=True, + fixed_vers__isnull=True, + introduced_by_package_commit_patches__isnull=True, + fixed_by_package_commit_patches__isnull=True, + ).distinct().delete() + + operations = [ + migrations.RunPython(clean_empty_vers_range, reverse_code=migrations.RunPython.noop), + ] diff --git a/vulnerabilities/tests/test_data_migrations.py b/vulnerabilities/tests/test_data_migrations.py index 242b57ca9..4837041a9 100644 --- a/vulnerabilities/tests/test_data_migrations.py +++ b/vulnerabilities/tests/test_data_migrations.py @@ -1184,3 +1184,90 @@ def test_migration_processes_malformed_aliases(self): else: assert adv_exists == False assert alias_exists == False + + +class TestCleanVersRangeMigration(TestMigrations): + app_name = "vulnerabilities" + migrate_from = "0124_advisoryv2_remove_malformed_aliases_and_dvisory_id" + migrate_to = "0125_clean_vers_range_without_constraints" + + def setUpBeforeMigration(self, apps): + AdvisoryV2 = apps.get_model("vulnerabilities", "AdvisoryV2") + ImpactedPackage = apps.get_model("vulnerabilities", "ImpactedPackage") + PackageCommitPatch = apps.get_model("vulnerabilities", "PackageCommitPatch") + + self.advisory1 = AdvisoryV2.objects.create( + unique_content_id="content_id_old", + url="https://old.example.com", + summary="Old advisory", + advisory_id="test_adv1", + avid="test_pipeline/test_adv", + datasource_id="test_pipeline", + ) + + self.advisory2 = AdvisoryV2.objects.create( + unique_content_id="content_id_old2", + url="https://old.example.com", + summary="Old 2 advisory", + advisory_id="test_adv2", + avid="test_pipeline/test_adv", + datasource_id="test_pipeline", + ) + + ImpactedPackage.objects.create( + advisory=self.advisory1, + base_purl="pkg:npm/foobar0", + affecting_vers="vers:npm/", + fixed_vers="vers:npm/", + ) + + self.impact1 = ImpactedPackage.objects.create( + advisory=self.advisory1, + base_purl="pkg:npm/foobar1", + affecting_vers="vers:npm/>=5.3.1|<6.0.0", + fixed_vers="vers:npm/", + ) + + self.impact2 = ImpactedPackage.objects.create( + advisory=self.advisory1, + base_purl="pkg:npm/foobar2", + affecting_vers="vers:npm/", + fixed_vers="vers:npm/", + ) + + self.pkg_commit_patch1 = PackageCommitPatch.objects.create( + commit_hash="8c001a11dbcb3eb6d851e18f4cefa080af5fb398", + vcs_url="https://github.com/aboutcode-org/test1/", + patch_text="test1", + ) + + self.impact3 = ImpactedPackage.objects.create( + advisory=self.advisory2, + base_purl="pkg:npm/foobar3", + affecting_vers="vers:npm/>5.6.7", + fixed_vers="vers:npm/5.6.8", + ) + + self.impact2.fixed_by_package_commit_patches.add(self.pkg_commit_patch1) + + self.assertEqual(ImpactedPackage.objects.count(), 4) + + def test_empty_impactepackages_removed(self): + ImpactedPackage = apps.get_model("vulnerabilities", "ImpactedPackage") + + self.assertEqual(ImpactedPackage.objects.count(), 3) + + def test_empty_fixed_vers_cleaned(self): + self.impact1.refresh_from_db() + self.assertEqual(self.impact1.fixed_vers, None) + + def test_empty_affecting_vers_cleaned(self): + self.impact2.refresh_from_db() + self.assertEqual(self.impact2.affecting_vers, None) + self.assertEqual(self.impact2.fixed_vers, None) + + def test_no_change_to_valid_vers(self): + self.impact3.refresh_from_db() + self.assertEqual(self.impact3.affecting_vers, "vers:npm/>5.6.7") + self.assertEqual(self.impact3.fixed_vers, "vers:npm/5.6.8") + self.assertEqual(self.impact3.advisory.advisory_id, "test_adv2")