diff options
Diffstat (limited to 'www/py-social-auth-app-django/files/patch-cve-2025-61783')
| -rw-r--r-- | www/py-social-auth-app-django/files/patch-cve-2025-61783 | 101 | 
1 files changed, 101 insertions, 0 deletions
| diff --git a/www/py-social-auth-app-django/files/patch-cve-2025-61783 b/www/py-social-auth-app-django/files/patch-cve-2025-61783 new file mode 100644 index 000000000000..941b9e2cd4b4 --- /dev/null +++ b/www/py-social-auth-app-django/files/patch-cve-2025-61783 @@ -0,0 +1,101 @@ +Backported and adapted patch for py-social-auth-core 5.4.3 to fix +CVE-2025-61783. + +Obtained from: + +From 10c80e2ebabeccd4e9c84ad0e16e1db74148ed4c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= <michal@cihar.com> +Date: Tue, 30 Sep 2025 13:38:21 +0200 +Subject: [PATCH] fix: avoid associating with existing user when creating fails + +This behavior was introduced in 9f86059e9d8070bc5ecd7ba069fadab1c9bf502a +to workaround concurrency issues, but the only safe way to deal with +this is to restart the pipeline to make sure that all possible policies +apply. This is currently not possible, so let's fail with +AuthAlreadyAssociated and let user restart the authentication pipeline +manually. + +--- social_django/storage.py.orig	2025-02-13 13:06:56 UTC ++++ social_django/storage.py +@@ -5,6 +5,7 @@ from django.db.utils import IntegrityError + from django.core.exceptions import FieldDoesNotExist + from django.db import router, transaction + from django.db.utils import IntegrityError ++from social_core.exceptions import AuthAlreadyAssociated + from social_core.storage import ( +     AssociationMixin, +     BaseStorage, +@@ -75,26 +76,24 @@ class DjangoUserMixin(UserMixin): +                     cls.user_model()._meta.get_field("username") +                 except FieldDoesNotExist: +                     kwargs.pop("username") ++ ++        if hasattr(transaction, "atomic"): ++            # In Django versions that have an "atomic" transaction decorator / context ++            # manager, there's a transaction wrapped around this call. ++            # If the create fails below due to an IntegrityError, ensure that the transaction ++            # stays undamaged by wrapping the create in an atomic. ++            using = router.db_for_write(cls.user_model()) ++ +         try: +             if hasattr(transaction, "atomic"): +-                # In Django versions that have an "atomic" transaction decorator / context +-                # manager, there's a transaction wrapped around this call. +-                # If the create fails below due to an IntegrityError, ensure that the transaction +-                # stays undamaged by wrapping the create in an atomic. +-                using = router.db_for_write(cls.user_model()) +                 with transaction.atomic(using=using): +                     user = cls.user_model()._default_manager.create_user(*args, **kwargs) +             else: +                 user = cls.user_model()._default_manager.create_user(*args, **kwargs) ++ ++            return user +         except IntegrityError as exc: +-            # If email comes in as None it won't get found in the get +-            if kwargs.get("email", True) is None: +-                kwargs["email"] = "" +-            try: +-                user = cls.user_model()._default_manager.get(*args, **kwargs) +-            except cls.user_model().DoesNotExist: +-                raise exc +-        return user ++            raise AuthAlreadyAssociated(None) from exc +  +     @classmethod +     def get_user(cls, pk=None, **kwargs): +--- tests/test_models.py.orig	2025-02-13 13:06:56 UTC ++++ tests/test_models.py +@@ -5,6 +5,7 @@ from django.test import TestCase + from django.core.management import call_command + from django.db import IntegrityError + from django.test import TestCase ++from social_core.exceptions import AuthAlreadyAssociated +  + from social_django.models import ( +     AbstractUserSocialAuth, +@@ -101,17 +102,21 @@ class TestUserSocialAuth(TestCase): +         self.assertEqual(UserSocialAuth.get_username(self.user), self.user.username) +  +     def test_create_user(self): +-        # Catch integrity error and find existing user +-        UserSocialAuth.create_user(username=self.user.username) ++        UserSocialAuth.create_user(username="testuser") +  +     def test_create_user_reraise(self): +-        with self.assertRaises(IntegrityError): ++        with self.assertRaises(AuthAlreadyAssociated): +             UserSocialAuth.create_user(username=self.user.username, email=None) +  +     @mock.patch("social_django.models.UserSocialAuth.username_field", return_value="email") +-    @mock.patch("django.contrib.auth.models.UserManager.create_user", side_effect=IntegrityError) ++    @mock.patch("django.contrib.auth.models.UserManager.create_user", return_value="<User>") +     def test_create_user_custom_username(self, *args): +         UserSocialAuth.create_user(username=self.user.email) ++ ++    @mock.patch("django.contrib.auth.models.UserManager.create_user", side_effect=IntegrityError) ++    def test_create_user_existing(self, *args): ++        with self.assertRaises(AuthAlreadyAssociated): ++            UserSocialAuth.create_user(username=self.user.email) +  +     @mock.patch("social_django.storage.transaction", spec=[]) +     def test_create_user_without_transaction_atomic(self, *args): | 
