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/0126_use_apk_scheme_for_alpine_vers.py b/vulnerabilities/migrations/0126_use_apk_scheme_for_alpine_vers.py new file mode 100644 index 000000000..56c910b57 --- /dev/null +++ b/vulnerabilities/migrations/0126_use_apk_scheme_for_alpine_vers.py @@ -0,0 +1,41 @@ +# Generated by Django 5.2.11 on 2026-05-04 09:51 + +from django.db import migrations +from django.db.models import F +from django.db.models import Q +from django.db.models import Value +from django.db.models.functions import Replace + + +class Migration(migrations.Migration): + + def update_alpine_scheme_to_apk(apps, schema_editor): + ImpactedPackage = apps.get_model("vulnerabilities", "ImpactedPackage") + + ImpactedPackage.objects.filter( + Q(affecting_vers__startswith="vers:alpine/") | Q(fixed_vers__startswith="vers:alpine/") + ).update( + affecting_vers=Replace(F("affecting_vers"), Value("vers:alpine/"), Value("vers:apk/")), + fixed_vers=Replace(F("fixed_vers"), Value("vers:alpine/"), Value("vers:apk/")), + ) + + def reverse_update_alpine_scheme_to_apk(apps, schema_editor): + ImpactedPackage = apps.get_model("vulnerabilities", "ImpactedPackage") + + ImpactedPackage.objects.filter( + Q(affecting_vers__startswith="vers:apk/") | Q(fixed_vers__startswith="vers:apk/") + ).update( + affecting_vers=Replace(F("affecting_vers"), Value("vers:apk/"), Value("vers:alpine/")), + fixed_vers=Replace(F("fixed_vers"), Value("vers:apk/"), Value("vers:alpine/")), + ) + + dependencies = [ + ("vulnerabilities", "0125_clean_vers_range_without_constraints"), + ] + + operations = [ + migrations.RunPython( + update_alpine_scheme_to_apk, + reverse_code=reverse_update_alpine_scheme_to_apk, + ), + ] diff --git a/vulnerabilities/tests/test_data/alpine/expected-advisories-v3.3.json b/vulnerabilities/tests/test_data/alpine/expected-advisories-v3.3.json index a118938eb..76be88519 100644 --- a/vulnerabilities/tests/test_data/alpine/expected-advisories-v3.3.json +++ b/vulnerabilities/tests/test_data/alpine/expected-advisories-v3.3.json @@ -16,7 +16,7 @@ "subpath": "" }, "affected_version_range": null, - "fixed_version_range": "vers:alpine/2.7.2-r0", + "fixed_version_range": "vers:apk/2.7.2-r0", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -30,7 +30,7 @@ "subpath": "" }, "affected_version_range": null, - "fixed_version_range": "vers:alpine/2.7.2-r0", + "fixed_version_range": "vers:apk/2.7.2-r0", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -44,7 +44,7 @@ "subpath": "" }, "affected_version_range": null, - "fixed_version_range": "vers:alpine/2.7.2-r0", + "fixed_version_range": "vers:apk/2.7.2-r0", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -58,7 +58,7 @@ "subpath": "" }, "affected_version_range": null, - "fixed_version_range": "vers:alpine/2.7.2-r0", + "fixed_version_range": "vers:apk/2.7.2-r0", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -72,7 +72,7 @@ "subpath": "" }, "affected_version_range": null, - "fixed_version_range": "vers:alpine/2.7.2-r0", + "fixed_version_range": "vers:apk/2.7.2-r0", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -86,7 +86,7 @@ "subpath": "" }, "affected_version_range": null, - "fixed_version_range": "vers:alpine/2.7.2-r0", + "fixed_version_range": "vers:apk/2.7.2-r0", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -100,7 +100,7 @@ "subpath": "" }, "affected_version_range": null, - "fixed_version_range": "vers:alpine/2.7.2-r0", + "fixed_version_range": "vers:apk/2.7.2-r0", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] } @@ -135,7 +135,7 @@ "subpath": "" }, "affected_version_range": null, - "fixed_version_range": "vers:alpine/2.7.2-r0", + "fixed_version_range": "vers:apk/2.7.2-r0", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -149,7 +149,7 @@ "subpath": "" }, "affected_version_range": null, - "fixed_version_range": "vers:alpine/2.7.2-r0", + "fixed_version_range": "vers:apk/2.7.2-r0", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -163,7 +163,7 @@ "subpath": "" }, "affected_version_range": null, - "fixed_version_range": "vers:alpine/2.7.2-r0", + "fixed_version_range": "vers:apk/2.7.2-r0", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -177,7 +177,7 @@ "subpath": "" }, "affected_version_range": null, - "fixed_version_range": "vers:alpine/2.7.2-r0", + "fixed_version_range": "vers:apk/2.7.2-r0", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -191,7 +191,7 @@ "subpath": "" }, "affected_version_range": null, - "fixed_version_range": "vers:alpine/2.7.2-r0", + "fixed_version_range": "vers:apk/2.7.2-r0", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -205,7 +205,7 @@ "subpath": "" }, "affected_version_range": null, - "fixed_version_range": "vers:alpine/2.7.2-r0", + "fixed_version_range": "vers:apk/2.7.2-r0", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -219,7 +219,7 @@ "subpath": "" }, "affected_version_range": null, - "fixed_version_range": "vers:alpine/2.7.2-r0", + "fixed_version_range": "vers:apk/2.7.2-r0", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] } @@ -274,7 +274,7 @@ "subpath": "" }, "affected_version_range": null, - "fixed_version_range": "vers:alpine/4.10.0-r1", + "fixed_version_range": "vers:apk/4.10.0-r1", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -288,7 +288,7 @@ "subpath": "" }, "affected_version_range": null, - "fixed_version_range": "vers:alpine/4.10.0-r1", + "fixed_version_range": "vers:apk/4.10.0-r1", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -302,7 +302,7 @@ "subpath": "" }, "affected_version_range": null, - "fixed_version_range": "vers:alpine/4.10.0-r1", + "fixed_version_range": "vers:apk/4.10.0-r1", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -316,7 +316,7 @@ "subpath": "" }, "affected_version_range": null, - "fixed_version_range": "vers:alpine/4.10.0-r1", + "fixed_version_range": "vers:apk/4.10.0-r1", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -330,7 +330,7 @@ "subpath": "" }, "affected_version_range": null, - "fixed_version_range": "vers:alpine/4.10.0-r1", + "fixed_version_range": "vers:apk/4.10.0-r1", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -344,7 +344,7 @@ "subpath": "" }, "affected_version_range": null, - "fixed_version_range": "vers:alpine/4.10.0-r1", + "fixed_version_range": "vers:apk/4.10.0-r1", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -358,7 +358,7 @@ "subpath": "" }, "affected_version_range": null, - "fixed_version_range": "vers:alpine/4.10.0-r1", + "fixed_version_range": "vers:apk/4.10.0-r1", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] } @@ -394,7 +394,7 @@ "subpath": "" }, "affected_version_range": null, - "fixed_version_range": "vers:alpine/4.10.0-r2", + "fixed_version_range": "vers:apk/4.10.0-r2", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -408,7 +408,7 @@ "subpath": "" }, "affected_version_range": null, - "fixed_version_range": "vers:alpine/4.10.0-r2", + "fixed_version_range": "vers:apk/4.10.0-r2", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -422,7 +422,7 @@ "subpath": "" }, "affected_version_range": null, - "fixed_version_range": "vers:alpine/4.10.0-r2", + "fixed_version_range": "vers:apk/4.10.0-r2", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -436,7 +436,7 @@ "subpath": "" }, "affected_version_range": null, - "fixed_version_range": "vers:alpine/4.10.0-r2", + "fixed_version_range": "vers:apk/4.10.0-r2", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -450,7 +450,7 @@ "subpath": "" }, "affected_version_range": null, - "fixed_version_range": "vers:alpine/4.10.0-r2", + "fixed_version_range": "vers:apk/4.10.0-r2", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -464,7 +464,7 @@ "subpath": "" }, "affected_version_range": null, - "fixed_version_range": "vers:alpine/4.10.0-r2", + "fixed_version_range": "vers:apk/4.10.0-r2", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -478,7 +478,7 @@ "subpath": "" }, "affected_version_range": null, - "fixed_version_range": "vers:alpine/4.10.0-r2", + "fixed_version_range": "vers:apk/4.10.0-r2", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] } diff --git a/vulnerabilities/tests/test_data_migrations.py b/vulnerabilities/tests/test_data_migrations.py index 4837041a9..17c916347 100644 --- a/vulnerabilities/tests/test_data_migrations.py +++ b/vulnerabilities/tests/test_data_migrations.py @@ -1271,3 +1271,103 @@ def test_no_change_to_valid_vers(self): 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") + + +class TestAlpineVersSchemeMigration(TestMigrations): + app_name = "vulnerabilities" + migrate_from = "0125_clean_vers_range_without_constraints" + migrate_to = "0126_use_apk_scheme_for_alpine_vers" + + def setUpBeforeMigration(self, apps): + ImpactedPackage = apps.get_model("vulnerabilities", "ImpactedPackage") + AdvisoryV2 = apps.get_model("vulnerabilities", "AdvisoryV2") + + 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.impact0 = ImpactedPackage.objects.create( + advisory=self.advisory1, + base_purl="pkg:apk/foobar0", + affecting_vers="vers:alpine/<2.3.4-0r", + fixed_vers="vers:alpine/2.3.4-0", + ) + + self.impact1 = ImpactedPackage.objects.create( + advisory=self.advisory1, + base_purl="pkg:apk/foobar1", + affecting_vers=None, + fixed_vers="vers:alpine/2.3.4", + ) + + self.impact2 = ImpactedPackage.objects.create( + advisory=self.advisory1, + base_purl="pkg:apk/foobar2", + affecting_vers="vers:alpine/3.4.5", + fixed_vers=None, + ) + + self.impact3 = ImpactedPackage.objects.create( + advisory=self.advisory1, + base_purl="pkg:alpm/foobar2", + affecting_vers="vers:alpm/0.2.1", + fixed_vers=None, + ) + + alpine_affecting = ImpactedPackage.objects.filter( + affecting_vers__startswith="vers:alpine/" + ).count() + alpine_fixing = ImpactedPackage.objects.filter( + affecting_vers__startswith="vers:alpine/" + ).count() + + self.assertEqual(alpine_affecting, 2) + self.assertEqual(alpine_fixing, 2) + + def test_empty_fixed_vers_cleaned(self): + ImpactedPackage = apps.get_model("vulnerabilities", "ImpactedPackage") + + result_apk_affecting = ImpactedPackage.objects.filter( + affecting_vers__startswith="vers:apk/" + ).count() + result_apk_fixing = ImpactedPackage.objects.filter( + affecting_vers__startswith="vers:apk/" + ).count() + + result_alpine_affecting = ImpactedPackage.objects.filter( + affecting_vers__startswith="vers:alpine/" + ).count() + result_alpine_fixing = ImpactedPackage.objects.filter( + affecting_vers__startswith="vers:alpine/" + ).count() + + self.assertEqual(result_apk_affecting, 2) + self.assertEqual(result_apk_fixing, 2) + + self.assertEqual(result_alpine_affecting, 0) + self.assertEqual(result_alpine_fixing, 0) + + def test_no_change_to_non_alpine_vers(self): + self.impact3.refresh_from_db() + + self.assertEqual(self.impact3.affecting_vers, "vers:alpm/0.2.1") + self.assertEqual(self.impact3.fixed_vers, None) + + def test_scheme_migration_correctness(self): + self.impact0.refresh_from_db() + self.impact1.refresh_from_db() + self.impact2.refresh_from_db() + + self.assertEqual(self.impact0.affecting_vers, "vers:apk/<2.3.4-0r") + self.assertEqual(self.impact0.fixed_vers, "vers:apk/2.3.4-0") + + self.assertEqual(self.impact1.affecting_vers, None) + self.assertEqual(self.impact1.fixed_vers, "vers:apk/2.3.4") + + self.assertEqual(self.impact2.affecting_vers, "vers:apk/3.4.5") + self.assertEqual(self.impact2.fixed_vers, None)