Disclosure of user names via admin bulk action views in wagtail
Description
Wagtail is an open source content management system built on Django. A user with a limited-permission editor account for the Wagtail admin can make a direct URL request to the admin view that handles bulk actions on user accounts. While authentication rules prevent the user from making any changes, the error message discloses the display names of user accounts, and by modifying URL parameters, the user can retrieve the display name for any user. The vulnerability is not exploitable by an ordinary site visitor without access to the Wagtail admin. Patched versions have been released as Wagtail 4.1.8 (LTS), 5.0.5 and 5.1.3. The fix is also included in Release Candidate 1 of the forthcoming Wagtail 5.2 release. Users are advised to upgrade. There are no known workarounds for this vulnerability.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
wagtailPyPI | < 4.1.9 | 4.1.9 |
wagtailPyPI | >= 4.2.0, < 5.0.5 | 5.0.5 |
wagtailPyPI | >= 5.1.0, < 5.1.3 | 5.1.3 |
Affected products
1Patches
3bc96aed6ac53Redirect away from user bulk actions when user has no permissions on users
2 files changed · +21 −2
wagtail/users/tests/test_bulk_actions/test_bulk_delete.py+13 −0 modified@@ -1,4 +1,5 @@ from django.contrib.auth import get_user_model +from django.contrib.auth.models import Permission from django.http import HttpRequest, HttpResponse from django.test import TestCase from django.urls import reverse @@ -51,6 +52,18 @@ def test_simple(self): response, "wagtailusers/bulk_actions/confirm_bulk_delete.html" ) + def test_user_permissions_required(self): + # Log in with a user that doesn't have permission to delete users + user = self.create_user(username="editor", password="password") + admin_permission = Permission.objects.get( + content_type__app_label="wagtailadmin", codename="access_admin" + ) + user.user_permissions.add(admin_permission) + self.login(username="editor", password="password") + + response = self.client.get(self.url) + self.assertRedirects(response, "/admin/") + def test_bulk_delete(self): response = self.client.post(self.url)
wagtail/users/views/bulk_actions/user_bulk_action.py+8 −2 modified@@ -1,11 +1,17 @@ from django.contrib.auth import get_user_model from wagtail.admin.views.bulk_action import BulkAction +from wagtail.admin.views.generic.permissions import PermissionCheckedMixin +from wagtail.permission_policies import ModelPermissionPolicy from wagtail.users.views.users import get_users_filter_query +User = get_user_model() -class UserBulkAction(BulkAction): - models = [get_user_model()] + +class UserBulkAction(PermissionCheckedMixin, BulkAction): + models = [User] + permission_policy = ModelPermissionPolicy(User) + any_permission_required = ["add", "change", "delete"] def get_all_objects_in_listing_query(self, parent_id): listing_objects = self.model.objects.all().values_list("pk", flat=True)
0bacd2947310Redirect away from user bulk actions when user has no permissions on users
2 files changed · +21 −2
wagtail/users/tests/test_bulk_actions/test_bulk_delete.py+13 −0 modified@@ -1,4 +1,5 @@ from django.contrib.auth import get_user_model +from django.contrib.auth.models import Permission from django.http import HttpRequest, HttpResponse from django.test import TestCase from django.urls import reverse @@ -51,6 +52,18 @@ def test_simple(self): response, "wagtailusers/bulk_actions/confirm_bulk_delete.html" ) + def test_user_permissions_required(self): + # Log in with a user that doesn't have permission to delete users + user = self.create_user(username="editor", password="password") + admin_permission = Permission.objects.get( + content_type__app_label="wagtailadmin", codename="access_admin" + ) + user.user_permissions.add(admin_permission) + self.login(username="editor", password="password") + + response = self.client.get(self.url) + self.assertRedirects(response, "/admin/") + def test_bulk_delete(self): response = self.client.post(self.url)
wagtail/users/views/bulk_actions/user_bulk_action.py+8 −2 modified@@ -1,11 +1,17 @@ from django.contrib.auth import get_user_model from wagtail.admin.views.bulk_action import BulkAction +from wagtail.admin.views.generic.permissions import PermissionCheckedMixin +from wagtail.permission_policies import ModelPermissionPolicy from wagtail.users.views.users import get_users_filter_query +User = get_user_model() -class UserBulkAction(BulkAction): - models = [get_user_model()] + +class UserBulkAction(PermissionCheckedMixin, BulkAction): + models = [User] + permission_policy = ModelPermissionPolicy(User) + any_permission_required = ["add", "change", "delete"] def get_all_objects_in_listing_query(self, parent_id): listing_objects = self.model.objects.all().values_list("pk", flat=True)
2231f462c75dRedirect away from user bulk actions when user has no permissions on users
2 files changed · +21 −2
wagtail/users/tests/test_bulk_actions/test_bulk_delete.py+13 −0 modified@@ -1,4 +1,5 @@ from django.contrib.auth import get_user_model +from django.contrib.auth.models import Permission from django.http import HttpRequest, HttpResponse from django.test import TestCase from django.urls import reverse @@ -51,6 +52,18 @@ def test_simple(self): response, "wagtailusers/bulk_actions/confirm_bulk_delete.html" ) + def test_user_permissions_required(self): + # Log in with a user that doesn't have permission to delete users + user = self.create_user(username="editor", password="password") + admin_permission = Permission.objects.get( + content_type__app_label="wagtailadmin", codename="access_admin" + ) + user.user_permissions.add(admin_permission) + self.login(username="editor", password="password") + + response = self.client.get(self.url) + self.assertRedirects(response, "/admin/") + def test_bulk_delete(self): response = self.client.post(self.url)
wagtail/users/views/bulk_actions/user_bulk_action.py+8 −2 modified@@ -1,11 +1,17 @@ from django.contrib.auth import get_user_model from wagtail.admin.views.bulk_action import BulkAction +from wagtail.admin.views.generic.permissions import PermissionCheckedMixin +from wagtail.permission_policies import ModelPermissionPolicy from wagtail.users.views.users import get_users_filter_query +User = get_user_model() -class UserBulkAction(BulkAction): - models = [get_user_model()] + +class UserBulkAction(PermissionCheckedMixin, BulkAction): + models = [User] + permission_policy = ModelPermissionPolicy(User) + any_permission_required = ["add", "change", "delete"] def get_all_objects_in_listing_query(self, parent_id): listing_objects = self.model.objects.all().values_list("pk", flat=True)
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
10- github.com/advisories/GHSA-fc75-58r8-rm3hghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2023-45809ghsaADVISORY
- github.com/pypa/advisory-database/tree/main/vulns/wagtail/PYSEC-2023-219.yamlghsaWEB
- github.com/wagtail/wagtail/commit/0bacd29473107d9d7f5b723a15a683449679756dghsaWEB
- github.com/wagtail/wagtail/commit/2231f462c75dfe84307fb40577e8c2109a23b27eghsaWEB
- github.com/wagtail/wagtail/commit/bc96aed6ac53f998b2f4c4bf97e2d4f5fe337e5bghsax_refsource_MISCWEB
- github.com/wagtail/wagtail/releases/tag/v4.1.9ghsaWEB
- github.com/wagtail/wagtail/releases/tag/v5.0.5ghsaWEB
- github.com/wagtail/wagtail/releases/tag/v5.1.3ghsaWEB
- github.com/wagtail/wagtail/security/advisories/GHSA-fc75-58r8-rm3hghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.