The vulnerability, CVE-2025-48383, in django-select2 (versions prior to 8.4.1) concerns the leaking of secret cache key components when widget instances derived from HeavySelect2Mixin (such as ModelSelect2Widget and ModelSelect2MultipleWidget) are instantiated once (e.g., during application load) and then reused across multiple requests.
The core issue lies in how the field_id, a crucial part of the cache key for AJAX requests made by these widgets, was generated. The provided patch (commit e5f41e6edba004d35f94915ff5e2559f44853412) shows that the lines responsible for generating self.uuid and self.field_id were removed from an __init__ method and moved into the build_attrs method within django_select2/forms.py.
Previously, self.field_id = signing.dumps(self.uuid) was executed in __init__. If a widget instance was defined as a class attribute on a Django form (e.g., widgets = {"my_field": ModelSelect2Widget()}), this __init__ would run once when the form class is defined (typically at app startup). Consequently, the field_id would be fixed for that widget instance. When multiple users made requests that rendered this form, they would all use the same widget instance and thus the same field_id for AJAX lookups. This could lead to users accessing cached data or querysets not intended for them, effectively leaking information or bypassing access controls tied to the AJAX endpoint.
The vulnerable function is identified as django_select2.forms.HeavySelect2Mixin.__init__ (or the __init__ method of a class that HeavySelect2Mixin is part of or mixed into, which is directly modified by the patch). This function contained the flawed logic of prematurely initializing the cache key component (field_id), making it static for reused instances.
The fix, by moving the generation of uuid and field_id to build_attrs, ensures that these values are generated fresh each time the widget's HTML attributes are constructed (which typically happens per request/render), thus isolating cache keys correctly.
During exploitation, a profiler would show the __init__ method of the affected widget being called once at startup (for the shared instance scenario), and subsequently, multiple AJAX requests from different users would use URLs containing the same field_id (derived in get_url and used in build_attrs). The __init__ method is the vulnerable function because its original implementation established the shared, static state that led to the vulnerability.