Skip to content

Commit cce2488

Browse files
committed
fix: ignore conflicts while bulk creating v2 packages
- This is a TOCTOU problem when multiple workers try to create the same PURL Signed-off-by: Keshav Priyadarshi <git@keshav.space>
1 parent 6ea0ced commit cce2488

2 files changed

Lines changed: 41 additions & 11 deletions

File tree

vulnerabilities/models.py

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3402,16 +3402,10 @@ def bulk_get_or_create_from_purls(self, purls: List[Union[PackageURL, str]]):
34023402
"""
34033403
Return new or existing Packages given ``purls`` list of PackageURL object or PURL string.
34043404
"""
3405-
purl_strings = [str(p) for p in purls]
3406-
existing_packages = PackageV2.objects.filter(package_url__in=purl_strings)
3407-
existing_purls = set(existing_packages.values_list("package_url", flat=True))
3408-
3409-
all_packages = list(existing_packages)
34103405
packages_to_create = []
3411-
for purl in purls:
3412-
if str(purl) in existing_purls:
3413-
continue
3406+
normalize_purls = []
34143407

3408+
for purl in purls:
34153409
purl_dict = purl_to_dict(purl)
34163410
purl = PackageURL(**purl_dict)
34173411

@@ -3422,16 +3416,16 @@ def bulk_get_or_create_from_purls(self, purls: List[Union[PackageURL, str]]):
34223416
purl_dict["package_url"] = str(normalized)
34233417
purl_dict["plain_package_url"] = str(utils.plain_purl(normalized))
34243418

3419+
normalize_purls.append(str(normalized))
34253420
packages_to_create.append(PackageV2(**purl_dict))
34263421

34273422
try:
3428-
new_packages = PackageV2.objects.bulk_create(packages_to_create)
3423+
PackageV2.objects.bulk_create(packages_to_create, ignore_conflicts=True)
34293424
except Exception as e:
34303425
logging.error(f"Error creating PackageV2: {e} \n {traceback_format_exc()}")
34313426
return []
34323427

3433-
all_packages.extend(new_packages)
3434-
return all_packages
3428+
return PackageV2.objects.filter(package_url__in=normalize_purls)
34353429

34363430
def only_vulnerable(self):
34373431
return self._vulnerable(True)

vulnerabilities/tests/test_models.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from vulnerabilities.models import AdvisorySeverity
3535
from vulnerabilities.models import Alias
3636
from vulnerabilities.models import Package
37+
from vulnerabilities.models import PackageV2
3738
from vulnerabilities.models import Patch
3839
from vulnerabilities.models import Vulnerability
3940
from vulnerabilities.severity_systems import CVSSV3
@@ -827,3 +828,38 @@ def test_advisoryv2_duplication_data(self):
827828
result = models.AdvisoryV2.objects.count()
828829

829830
self.assertEqual(result, 2)
831+
832+
833+
class TestPackageV2BulkCreate(DjangoTestCase):
834+
def setUp(self):
835+
PackageV2.objects.get_or_create_from_purl(
836+
"pkg:deb/ubuntu/linux@6.17.0-19.19?arch=source&distro=questing"
837+
)
838+
PackageV2.objects.get_or_create_from_purl("pkg:pypi/foo@1.2.3")
839+
PackageV2.objects.get_or_create_from_purl("pkg:npm/foobar@3.2.3")
840+
PackageV2.objects.get_or_create_from_purl("pkg:maven/foo@1.2.3")
841+
PackageV2.objects.get_or_create_from_purl(
842+
"pkg:deb/ubuntu/linux@6.17.0-4.4?arch=source&distro=questing"
843+
)
844+
845+
def test_package_bulk_get_or_create_from_purls(self):
846+
purls = [
847+
"pkg:npm/foo@1.2.3",
848+
"pkg:pypi/foo@1.2.3",
849+
"pkg:npm/foobar@3.2.3",
850+
"pkg:maven/foo@1.2.3",
851+
"pkg:nuget/foo@1.2.3",
852+
"pkg:deb/ubuntu/linux@6.17.0-22.22?arch=source&distro=questing",
853+
"pkg:deb/ubuntu/linux@6.17.0-20.20?arch=source&distro=questing",
854+
"pkg:deb/ubuntu/linux@6.17.0-14.14?arch=source&distro=questing",
855+
"pkg:deb/ubuntu/linux@6.17.0-12.12?arch=source&distro=questing",
856+
"pkg:deb/ubuntu/linux@6.17.0-8.8?arch=source&distro=questing",
857+
"pkg:deb/ubuntu/linux@6.17.0-7.7?arch=source&distro=questing",
858+
"pkg:deb/ubuntu/linux@6.17.0-6.6?arch=source&distro=questing",
859+
"pkg:deb/ubuntu/linux@6.17.0-5.5?arch=source&distro=questing",
860+
"pkg:deb/ubuntu/linux@6.17.0-4.4?arch=source&distro=questing",
861+
]
862+
result_qs = PackageV2.objects.bulk_get_or_create_from_purls(purls)
863+
result = [p.package_url for p in result_qs]
864+
865+
self.assertCountEqual(result, purls)

0 commit comments

Comments
 (0)