CVE-2024-32882
Description
Wagtail is an open source content management system built on Django. In affected versions if a model has been made available for editing through the wagtail.contrib.settings module or ModelViewSet, and the permission argument on FieldPanel has been used to further restrict access to one or more fields of the model, a user with edit permission over the model but not the specific field can craft an HTTP POST request that bypasses the permission check on the individual field, allowing them to update its value. This vulnerability is not exploitable by an ordinary site visitor without access to the Wagtail admin, or by a user who has not been granted edit access to the model in question. The editing interfaces for pages and snippets are also unaffected. Patched versions have been released as Wagtail 6.0.3 and 6.1. Wagtail releases prior to 6.0 are unaffected. Users are advised to upgrade. Site owners who are unable to upgrade to a patched version can avoid the vulnerability as follows: 1.For models registered through ModelViewSet, register the model as a snippet instead; 2. For settings models, place the restricted fields in a separate settings model, and configure permission at the model level.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
wagtailPyPI | >= 6.0.0, < 6.0.3 | 6.0.3 |
Patches
2ab2a5d82b4eeRevert "Merge pull request from GHSA-w2v8-php4-p8hc"
8 files changed · +24 −324
wagtail/admin/tests/viewsets/test_model_viewset.py+0 −29 modified@@ -1474,35 +1474,6 @@ def test_edit_form_rendered_with_panels(self): modal_workflow_script = soup.select_one(f'script[src="{modal_workflow_js}"]') self.assertIsNotNone(modal_workflow_script) - def test_field_permissions(self): - self.user.is_superuser = False - self.user.save() - self.user.user_permissions.add( - Permission.objects.get( - content_type__app_label="wagtailadmin", codename="access_admin" - ), - Permission.objects.get( - content_type__app_label=self.object._meta.app_label, - codename=get_permission_codename("change", self.object._meta), - ), - ) - - response = self.client.get(self.url) - self.assertEqual(response.status_code, 200) - self.assertEqual(list(response.context["form"].fields), ["name"]) - - self.user.user_permissions.add( - Permission.objects.get( - codename="can_set_release_date", - ) - ) - - response = self.client.get(self.url) - self.assertEqual(response.status_code, 200) - self.assertEqual( - list(response.context["form"].fields), ["name", "release_date"] - ) - class TestDefaultMessages(WagtailTestUtils, TestCase): def setUp(self):
wagtail/admin/views/generic/models.py+0 −26 modified@@ -27,7 +27,6 @@ from wagtail.actions.unpublish import UnpublishAction from wagtail.admin import messages from wagtail.admin.filters import WagtailFilterSet -from wagtail.admin.forms.models import WagtailAdminModelForm from wagtail.admin.forms.search import SearchForm from wagtail.admin.panels import get_edit_handler from wagtail.admin.ui.components import Component, MediaContainer @@ -617,24 +616,6 @@ def get_translations(self): for locale in Locale.objects.all().exclude(id=self.locale.id) ] - def get_initial_form_instance(self): - if self.locale: - instance = self.model() - instance.locale = self.locale - return instance - - def get_form_kwargs(self): - if instance := self.get_initial_form_instance(): - # super().get_form_kwargs() will use self.object as the instance kwarg - self.object = instance - kwargs = super().get_form_kwargs() - - form_class = self.get_form_class() - # Add for_user support for PermissionedForm - if issubclass(form_class, WagtailAdminModelForm): - kwargs["for_user"] = self.request.user - return kwargs - def save_instance(self): """ Called after the form is successfully validated - saves the object to the db @@ -805,13 +786,6 @@ def get_translations(self): for translation in self.object.get_translations().select_related("locale") ] - def get_form_kwargs(self): - kwargs = super().get_form_kwargs() - form_class = self.get_form_class() - if issubclass(form_class, WagtailAdminModelForm): - kwargs["for_user"] = self.request.user - return kwargs - def save_instance(self): """ Called after the form is successfully validated - saves the object to the db.
wagtail/contrib/settings/tests/generic/test_admin.py+1 −88 modified@@ -14,7 +14,6 @@ PanelGenericSettings, TabbedGenericSettings, TestGenericSetting, - TestPermissionedGenericSetting, ) from wagtail.test.utils import WagtailTestUtils @@ -77,11 +76,6 @@ def edit_url(self, setting): class TestGenericSettingCreateView(BaseTestGenericSettingView): def setUp(self): self.user = self.login() - self.user.user_permissions.add( - Permission.objects.get( - content_type__app_label="wagtailadmin", codename="access_admin" - ) - ) def test_get_edit(self): response = self.get() @@ -113,51 +107,14 @@ def test_file_upload_multipart(self): # Ensure the form supports file uploads self.assertContains(response, 'enctype="multipart/form-data"') - def test_create_restricted_field_without_permission(self): - self.user.is_superuser = False - self.user.save() - - self.assertFalse(TestPermissionedGenericSetting.objects.exists()) - response = self.post( - post_data={"sensitive_email": "test@example.com", "title": "test"}, - setting=TestPermissionedGenericSetting, - ) - self.assertEqual(response.status_code, 302) - - settings = TestPermissionedGenericSetting.objects.get() - self.assertEqual(settings.title, "test") - self.assertEqual(settings.sensitive_email, "") - - def test_create_restricted_field(self): - self.user.is_superuser = False - self.user.save() - self.user.user_permissions.add( - Permission.objects.get(codename="can_edit_sensitive_email_generic_setting") - ) - self.assertFalse(TestPermissionedGenericSetting.objects.exists()) - response = self.post( - post_data={"sensitive_email": "test@example.com", "title": "test"}, - setting=TestPermissionedGenericSetting, - ) - self.assertEqual(response.status_code, 302) - - settings = TestPermissionedGenericSetting.objects.get() - self.assertEqual(settings.title, "test") - self.assertEqual(settings.sensitive_email, "test@example.com") - class TestGenericSettingEditView(BaseTestGenericSettingView): def setUp(self): self.test_setting = TestGenericSetting() self.test_setting.title = "Setting title" self.test_setting.save() - self.user = self.login() - self.user.user_permissions.add( - Permission.objects.get( - content_type__app_label="wagtailadmin", codename="access_admin" - ) - ) + self.login() def test_get_edit(self): response = self.get() @@ -196,50 +153,6 @@ def test_for_request(self): expected_url=f"{url}{TestGenericSetting.objects.first().pk}/", ) - def test_edit_restricted_field(self): - test_setting = TestPermissionedGenericSetting() - test_setting.sensitive_email = "test@example.com" - test_setting.save() - self.user.is_superuser = False - self.user.save() - - self.user.user_permissions.add( - Permission.objects.get(codename="can_edit_sensitive_email_generic_setting") - ) - - response = self.get(setting=TestPermissionedGenericSetting) - self.assertEqual(response.status_code, 200) - self.assertIn("sensitive_email", list(response.context["form"].fields)) - - response = self.post( - setting=TestPermissionedGenericSetting, - post_data={"sensitive_email": "test-updated@example.com", "title": "title"}, - ) - self.assertEqual(response.status_code, 302) - - test_setting.refresh_from_db() - self.assertEqual(test_setting.sensitive_email, "test-updated@example.com") - - def test_edit_restricted_field_without_permission(self): - test_setting = TestPermissionedGenericSetting() - test_setting.sensitive_email = "test@example.com" - test_setting.save() - self.user.is_superuser = False - self.user.save() - - response = self.get(setting=TestPermissionedGenericSetting) - self.assertEqual(response.status_code, 200) - self.assertNotIn("sensitive_email", list(response.context["form"].fields)) - - response = self.post( - setting=TestPermissionedGenericSetting, - post_data={"sensitive_email": "test-updated@example.com", "title": "title"}, - ) - self.assertEqual(response.status_code, 302) - - test_setting.refresh_from_db() - self.assertEqual(test_setting.sensitive_email, "test@example.com") - class TestAdminPermission(WagtailTestUtils, TestCase): def test_registered_permission(self):
wagtail/contrib/settings/tests/site_specific/test_admin.py+3 −92 modified@@ -14,7 +14,6 @@ IconSiteSetting, PanelSiteSettings, TabbedSiteSettings, - TestPermissionedSiteSetting, TestSiteSetting, ) from wagtail.test.utils import WagtailTestUtils @@ -73,11 +72,6 @@ def edit_url(self, setting, site_pk=1): class TestSiteSettingCreateView(BaseTestSiteSettingView): def setUp(self): self.user = self.login() - self.user.user_permissions.add( - Permission.objects.get( - content_type__app_label="wagtailadmin", codename="access_admin" - ) - ) def test_get_edit(self): response = self.get() @@ -109,55 +103,18 @@ def test_file_upload_multipart(self): # Ensure the form supports file uploads self.assertContains(response, 'enctype="multipart/form-data"') - def test_create_restricted_field_without_permission(self): - self.user.is_superuser = False - self.user.save() - - self.assertFalse(TestPermissionedSiteSetting.objects.exists()) - response = self.post( - post_data={"sensitive_email": "test@example.com", "title": "test"}, - setting=TestPermissionedSiteSetting, - ) - self.assertEqual(response.status_code, 302) - - settings = TestPermissionedSiteSetting.objects.get() - self.assertEqual(settings.title, "test") - self.assertEqual(settings.sensitive_email, "") - - def test_create_restricted_field(self): - self.user.is_superuser = False - self.user.save() - self.user.user_permissions.add( - Permission.objects.get(codename="can_edit_sensitive_email_site_setting") - ) - self.assertFalse(TestPermissionedSiteSetting.objects.exists()) - response = self.post( - post_data={"sensitive_email": "test@example.com", "title": "test"}, - setting=TestPermissionedSiteSetting, - ) - self.assertEqual(response.status_code, 302) - - settings = TestPermissionedSiteSetting.objects.get() - self.assertEqual(settings.title, "test") - self.assertEqual(settings.sensitive_email, "test@example.com") - class TestSiteSettingEditView(BaseTestSiteSettingView): def setUp(self): - self.default_site = Site.objects.get(is_default_site=True) + default_site = Site.objects.get(is_default_site=True) self.test_setting = TestSiteSetting() self.test_setting.title = "Site title" self.test_setting.email = "initial@example.com" - self.test_setting.site = self.default_site + self.test_setting.site = default_site self.test_setting.save() - self.user = self.login() - self.user.user_permissions.add( - Permission.objects.get( - content_type__app_label="wagtailadmin", codename="access_admin" - ) - ) + self.login() def test_get_edit(self): response = self.get() @@ -201,52 +158,6 @@ def test_get_redirect_to_relevant_instance_invalid(self): response = self.client.get(url) self.assertRedirects(response, status_code=302, expected_url="/admin/") - def test_edit_restricted_field(self): - test_setting = TestPermissionedSiteSetting() - test_setting.sensitive_email = "test@example.com" - test_setting.site = self.default_site - test_setting.save() - self.user.is_superuser = False - self.user.save() - - self.user.user_permissions.add( - Permission.objects.get(codename="can_edit_sensitive_email_site_setting") - ) - - response = self.get(setting=TestPermissionedSiteSetting) - self.assertEqual(response.status_code, 200) - self.assertIn("sensitive_email", list(response.context["form"].fields)) - - response = self.post( - setting=TestPermissionedSiteSetting, - post_data={"sensitive_email": "test-updated@example.com", "title": "title"}, - ) - self.assertEqual(response.status_code, 302) - - test_setting.refresh_from_db() - self.assertEqual(test_setting.sensitive_email, "test-updated@example.com") - - def test_edit_restricted_field_without_permission(self): - test_setting = TestPermissionedSiteSetting() - test_setting.sensitive_email = "test@example.com" - test_setting.site = self.default_site - test_setting.save() - self.user.is_superuser = False - self.user.save() - - response = self.get(setting=TestPermissionedSiteSetting) - self.assertEqual(response.status_code, 200) - self.assertNotIn("sensitive_email", list(response.context["form"].fields)) - - response = self.post( - setting=TestPermissionedSiteSetting, - post_data={"sensitive_email": "test-updated@example.com", "title": "title"}, - ) - self.assertEqual(response.status_code, 302) - - test_setting.refresh_from_db() - self.assertEqual(test_setting.sensitive_email, "test@example.com") - @override_settings( ALLOWED_HOSTS=["testserver", "example.com", "noneoftheabove.example.com"]
wagtail/snippets/views/snippets.py+19 −0 modified@@ -235,6 +235,22 @@ def run_after_hook(self): def _get_action_menu(self): return SnippetActionMenu(self.request, view=self.view_name, model=self.model) + def _get_initial_form_instance(self): + instance = self.model() + + # Set locale of the new instance + if self.locale: + instance.locale = self.locale + + return instance + + def get_form_kwargs(self): + return { + **super().get_form_kwargs(), + "instance": self._get_initial_form_instance(), + "for_user": self.request.user, + } + def get_side_panels(self): side_panels = [ SnippetStatusSidePanel( @@ -294,6 +310,9 @@ def _get_action_menu(self): locked_for_user=self.locked_for_user, ) + def get_form_kwargs(self): + return {**super().get_form_kwargs(), "for_user": self.request.user} + def get_side_panels(self): side_panels = [ SnippetStatusSidePanel(
wagtail/test/testapp/migrations/0034_testpermissionedgenericsetting_and_more.py+0 −42 removed@@ -1,42 +0,0 @@ -# Generated by Django 4.2.11 on 2024-04-25 15:51 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('wagtailcore', '0091_remove_revision_submitted_for_moderation'), - ('tests', '0033_customcopyformpage'), - ] - - operations = [ - migrations.CreateModel( - name='TestPermissionedGenericSetting', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('title', models.CharField(max_length=100)), - ('sensitive_email', models.EmailField(max_length=50)), - ], - options={ - 'permissions': [('can_edit_sensitive_email_generic_setting', 'Can edit sensitive email generic setting.')], - }, - ), - migrations.AlterModelOptions( - name='featurecompletetoy', - options={'permissions': [('can_set_release_date', 'Can set release date')]}, - ), - migrations.CreateModel( - name='TestPermissionedSiteSetting', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('title', models.CharField(max_length=100)), - ('sensitive_email', models.EmailField(max_length=50)), - ('site', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, to='wagtailcore.site')), - ], - options={ - 'permissions': [('can_edit_sensitive_email_site_setting', 'Can edit sensitive email site setting.')], - }, - ), - ]
wagtail/test/testapp/models.py+0 −46 modified@@ -1640,49 +1640,6 @@ class TestGenericSetting(BaseGenericSetting): email = models.EmailField(max_length=50) -@register_setting -class TestPermissionedGenericSetting(BaseGenericSetting): - title = models.CharField(max_length=100) - sensitive_email = models.EmailField(max_length=50) - - panels = [ - FieldPanel("title"), - FieldPanel( - "sensitive_email", - permission="tests.can_edit_sensitive_email_generic_setting", - ), - ] - - class Meta: - permissions = [ - ( - "can_edit_sensitive_email_generic_setting", - "Can edit sensitive email generic setting.", - ), - ] - - -@register_setting -class TestPermissionedSiteSetting(BaseSiteSetting): - title = models.CharField(max_length=100) - sensitive_email = models.EmailField(max_length=50) - - panels = [ - FieldPanel("title"), - FieldPanel( - "sensitive_email", permission="tests.can_edit_sensitive_email_site_setting" - ), - ] - - class Meta: - permissions = [ - ( - "can_edit_sensitive_email_site_setting", - "Can edit sensitive email site setting.", - ), - ] - - @register_setting class ImportantPagesSiteSetting(BaseSiteSetting): sign_up_page = models.ForeignKey( @@ -2272,9 +2229,6 @@ def is_cool(self): def __str__(self): return f"{self.name} ({self.release_date})" - class Meta: - permissions = [("can_set_release_date", "Can set release date")] - class PurgeRevisionsProtectedTestModel(models.Model): revision = models.OneToOneField(
wagtail/test/testapp/views.py+1 −1 modified@@ -227,7 +227,7 @@ class FeatureCompleteToyViewSet(ModelViewSet): panels = [ FieldPanel("name"), - FieldPanel("release_date", permission="tests.can_set_release_date"), + FieldPanel("release_date"), ]
fa0d4829f9c8Merge pull request from GHSA-w2v8-php4-p8hc
8 files changed · +324 −24
wagtail/admin/tests/viewsets/test_model_viewset.py+29 −0 modified@@ -1474,6 +1474,35 @@ def test_edit_form_rendered_with_panels(self): modal_workflow_script = soup.select_one(f'script[src="{modal_workflow_js}"]') self.assertIsNotNone(modal_workflow_script) + def test_field_permissions(self): + self.user.is_superuser = False + self.user.save() + self.user.user_permissions.add( + Permission.objects.get( + content_type__app_label="wagtailadmin", codename="access_admin" + ), + Permission.objects.get( + content_type__app_label=self.object._meta.app_label, + codename=get_permission_codename("change", self.object._meta), + ), + ) + + response = self.client.get(self.url) + self.assertEqual(response.status_code, 200) + self.assertEqual(list(response.context["form"].fields), ["name"]) + + self.user.user_permissions.add( + Permission.objects.get( + codename="can_set_release_date", + ) + ) + + response = self.client.get(self.url) + self.assertEqual(response.status_code, 200) + self.assertEqual( + list(response.context["form"].fields), ["name", "release_date"] + ) + class TestDefaultMessages(WagtailTestUtils, TestCase): def setUp(self):
wagtail/admin/views/generic/models.py+26 −0 modified@@ -27,6 +27,7 @@ from wagtail.actions.unpublish import UnpublishAction from wagtail.admin import messages from wagtail.admin.filters import WagtailFilterSet +from wagtail.admin.forms.models import WagtailAdminModelForm from wagtail.admin.forms.search import SearchForm from wagtail.admin.panels import get_edit_handler from wagtail.admin.ui.components import Component, MediaContainer @@ -616,6 +617,24 @@ def get_translations(self): for locale in Locale.objects.all().exclude(id=self.locale.id) ] + def get_initial_form_instance(self): + if self.locale: + instance = self.model() + instance.locale = self.locale + return instance + + def get_form_kwargs(self): + if instance := self.get_initial_form_instance(): + # super().get_form_kwargs() will use self.object as the instance kwarg + self.object = instance + kwargs = super().get_form_kwargs() + + form_class = self.get_form_class() + # Add for_user support for PermissionedForm + if issubclass(form_class, WagtailAdminModelForm): + kwargs["for_user"] = self.request.user + return kwargs + def save_instance(self): """ Called after the form is successfully validated - saves the object to the db @@ -786,6 +805,13 @@ def get_translations(self): for translation in self.object.get_translations().select_related("locale") ] + def get_form_kwargs(self): + kwargs = super().get_form_kwargs() + form_class = self.get_form_class() + if issubclass(form_class, WagtailAdminModelForm): + kwargs["for_user"] = self.request.user + return kwargs + def save_instance(self): """ Called after the form is successfully validated - saves the object to the db.
wagtail/contrib/settings/tests/generic/test_admin.py+88 −1 modified@@ -14,6 +14,7 @@ PanelGenericSettings, TabbedGenericSettings, TestGenericSetting, + TestPermissionedGenericSetting, ) from wagtail.test.utils import WagtailTestUtils @@ -76,6 +77,11 @@ def edit_url(self, setting): class TestGenericSettingCreateView(BaseTestGenericSettingView): def setUp(self): self.user = self.login() + self.user.user_permissions.add( + Permission.objects.get( + content_type__app_label="wagtailadmin", codename="access_admin" + ) + ) def test_get_edit(self): response = self.get() @@ -107,14 +113,51 @@ def test_file_upload_multipart(self): # Ensure the form supports file uploads self.assertContains(response, 'enctype="multipart/form-data"') + def test_create_restricted_field_without_permission(self): + self.user.is_superuser = False + self.user.save() + + self.assertFalse(TestPermissionedGenericSetting.objects.exists()) + response = self.post( + post_data={"sensitive_email": "test@example.com", "title": "test"}, + setting=TestPermissionedGenericSetting, + ) + self.assertEqual(response.status_code, 302) + + settings = TestPermissionedGenericSetting.objects.get() + self.assertEqual(settings.title, "test") + self.assertEqual(settings.sensitive_email, "") + + def test_create_restricted_field(self): + self.user.is_superuser = False + self.user.save() + self.user.user_permissions.add( + Permission.objects.get(codename="can_edit_sensitive_email_generic_setting") + ) + self.assertFalse(TestPermissionedGenericSetting.objects.exists()) + response = self.post( + post_data={"sensitive_email": "test@example.com", "title": "test"}, + setting=TestPermissionedGenericSetting, + ) + self.assertEqual(response.status_code, 302) + + settings = TestPermissionedGenericSetting.objects.get() + self.assertEqual(settings.title, "test") + self.assertEqual(settings.sensitive_email, "test@example.com") + class TestGenericSettingEditView(BaseTestGenericSettingView): def setUp(self): self.test_setting = TestGenericSetting() self.test_setting.title = "Setting title" self.test_setting.save() - self.login() + self.user = self.login() + self.user.user_permissions.add( + Permission.objects.get( + content_type__app_label="wagtailadmin", codename="access_admin" + ) + ) def test_get_edit(self): response = self.get() @@ -153,6 +196,50 @@ def test_for_request(self): expected_url=f"{url}{TestGenericSetting.objects.first().pk}/", ) + def test_edit_restricted_field(self): + test_setting = TestPermissionedGenericSetting() + test_setting.sensitive_email = "test@example.com" + test_setting.save() + self.user.is_superuser = False + self.user.save() + + self.user.user_permissions.add( + Permission.objects.get(codename="can_edit_sensitive_email_generic_setting") + ) + + response = self.get(setting=TestPermissionedGenericSetting) + self.assertEqual(response.status_code, 200) + self.assertIn("sensitive_email", list(response.context["form"].fields)) + + response = self.post( + setting=TestPermissionedGenericSetting, + post_data={"sensitive_email": "test-updated@example.com", "title": "title"}, + ) + self.assertEqual(response.status_code, 302) + + test_setting.refresh_from_db() + self.assertEqual(test_setting.sensitive_email, "test-updated@example.com") + + def test_edit_restricted_field_without_permission(self): + test_setting = TestPermissionedGenericSetting() + test_setting.sensitive_email = "test@example.com" + test_setting.save() + self.user.is_superuser = False + self.user.save() + + response = self.get(setting=TestPermissionedGenericSetting) + self.assertEqual(response.status_code, 200) + self.assertNotIn("sensitive_email", list(response.context["form"].fields)) + + response = self.post( + setting=TestPermissionedGenericSetting, + post_data={"sensitive_email": "test-updated@example.com", "title": "title"}, + ) + self.assertEqual(response.status_code, 302) + + test_setting.refresh_from_db() + self.assertEqual(test_setting.sensitive_email, "test@example.com") + class TestAdminPermission(WagtailTestUtils, TestCase): def test_registered_permission(self):
wagtail/contrib/settings/tests/site_specific/test_admin.py+92 −3 modified@@ -14,6 +14,7 @@ IconSiteSetting, PanelSiteSettings, TabbedSiteSettings, + TestPermissionedSiteSetting, TestSiteSetting, ) from wagtail.test.utils import WagtailTestUtils @@ -72,6 +73,11 @@ def edit_url(self, setting, site_pk=1): class TestSiteSettingCreateView(BaseTestSiteSettingView): def setUp(self): self.user = self.login() + self.user.user_permissions.add( + Permission.objects.get( + content_type__app_label="wagtailadmin", codename="access_admin" + ) + ) def test_get_edit(self): response = self.get() @@ -103,18 +109,55 @@ def test_file_upload_multipart(self): # Ensure the form supports file uploads self.assertContains(response, 'enctype="multipart/form-data"') + def test_create_restricted_field_without_permission(self): + self.user.is_superuser = False + self.user.save() + + self.assertFalse(TestPermissionedSiteSetting.objects.exists()) + response = self.post( + post_data={"sensitive_email": "test@example.com", "title": "test"}, + setting=TestPermissionedSiteSetting, + ) + self.assertEqual(response.status_code, 302) + + settings = TestPermissionedSiteSetting.objects.get() + self.assertEqual(settings.title, "test") + self.assertEqual(settings.sensitive_email, "") + + def test_create_restricted_field(self): + self.user.is_superuser = False + self.user.save() + self.user.user_permissions.add( + Permission.objects.get(codename="can_edit_sensitive_email_site_setting") + ) + self.assertFalse(TestPermissionedSiteSetting.objects.exists()) + response = self.post( + post_data={"sensitive_email": "test@example.com", "title": "test"}, + setting=TestPermissionedSiteSetting, + ) + self.assertEqual(response.status_code, 302) + + settings = TestPermissionedSiteSetting.objects.get() + self.assertEqual(settings.title, "test") + self.assertEqual(settings.sensitive_email, "test@example.com") + class TestSiteSettingEditView(BaseTestSiteSettingView): def setUp(self): - default_site = Site.objects.get(is_default_site=True) + self.default_site = Site.objects.get(is_default_site=True) self.test_setting = TestSiteSetting() self.test_setting.title = "Site title" self.test_setting.email = "initial@example.com" - self.test_setting.site = default_site + self.test_setting.site = self.default_site self.test_setting.save() - self.login() + self.user = self.login() + self.user.user_permissions.add( + Permission.objects.get( + content_type__app_label="wagtailadmin", codename="access_admin" + ) + ) def test_get_edit(self): response = self.get() @@ -158,6 +201,52 @@ def test_get_redirect_to_relevant_instance_invalid(self): response = self.client.get(url) self.assertRedirects(response, status_code=302, expected_url="/admin/") + def test_edit_restricted_field(self): + test_setting = TestPermissionedSiteSetting() + test_setting.sensitive_email = "test@example.com" + test_setting.site = self.default_site + test_setting.save() + self.user.is_superuser = False + self.user.save() + + self.user.user_permissions.add( + Permission.objects.get(codename="can_edit_sensitive_email_site_setting") + ) + + response = self.get(setting=TestPermissionedSiteSetting) + self.assertEqual(response.status_code, 200) + self.assertIn("sensitive_email", list(response.context["form"].fields)) + + response = self.post( + setting=TestPermissionedSiteSetting, + post_data={"sensitive_email": "test-updated@example.com", "title": "title"}, + ) + self.assertEqual(response.status_code, 302) + + test_setting.refresh_from_db() + self.assertEqual(test_setting.sensitive_email, "test-updated@example.com") + + def test_edit_restricted_field_without_permission(self): + test_setting = TestPermissionedSiteSetting() + test_setting.sensitive_email = "test@example.com" + test_setting.site = self.default_site + test_setting.save() + self.user.is_superuser = False + self.user.save() + + response = self.get(setting=TestPermissionedSiteSetting) + self.assertEqual(response.status_code, 200) + self.assertNotIn("sensitive_email", list(response.context["form"].fields)) + + response = self.post( + setting=TestPermissionedSiteSetting, + post_data={"sensitive_email": "test-updated@example.com", "title": "title"}, + ) + self.assertEqual(response.status_code, 302) + + test_setting.refresh_from_db() + self.assertEqual(test_setting.sensitive_email, "test@example.com") + @override_settings( ALLOWED_HOSTS=["testserver", "example.com", "noneoftheabove.example.com"]
wagtail/snippets/views/snippets.py+0 −19 modified@@ -235,22 +235,6 @@ def run_after_hook(self): def _get_action_menu(self): return SnippetActionMenu(self.request, view=self.view_name, model=self.model) - def _get_initial_form_instance(self): - instance = self.model() - - # Set locale of the new instance - if self.locale: - instance.locale = self.locale - - return instance - - def get_form_kwargs(self): - return { - **super().get_form_kwargs(), - "instance": self._get_initial_form_instance(), - "for_user": self.request.user, - } - def get_side_panels(self): side_panels = [ SnippetStatusSidePanel( @@ -310,9 +294,6 @@ def _get_action_menu(self): locked_for_user=self.locked_for_user, ) - def get_form_kwargs(self): - return {**super().get_form_kwargs(), "for_user": self.request.user} - def get_side_panels(self): side_panels = [ SnippetStatusSidePanel(
wagtail/test/testapp/migrations/0034_testpermissionedgenericsetting_and_more.py+42 −0 added@@ -0,0 +1,42 @@ +# Generated by Django 4.2.11 on 2024-04-25 15:51 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('wagtailcore', '0091_remove_revision_submitted_for_moderation'), + ('tests', '0033_customcopyformpage'), + ] + + operations = [ + migrations.CreateModel( + name='TestPermissionedGenericSetting', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=100)), + ('sensitive_email', models.EmailField(max_length=50)), + ], + options={ + 'permissions': [('can_edit_sensitive_email_generic_setting', 'Can edit sensitive email generic setting.')], + }, + ), + migrations.AlterModelOptions( + name='featurecompletetoy', + options={'permissions': [('can_set_release_date', 'Can set release date')]}, + ), + migrations.CreateModel( + name='TestPermissionedSiteSetting', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=100)), + ('sensitive_email', models.EmailField(max_length=50)), + ('site', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, to='wagtailcore.site')), + ], + options={ + 'permissions': [('can_edit_sensitive_email_site_setting', 'Can edit sensitive email site setting.')], + }, + ), + ]
wagtail/test/testapp/models.py+46 −0 modified@@ -1640,6 +1640,49 @@ class TestGenericSetting(BaseGenericSetting): email = models.EmailField(max_length=50) +@register_setting +class TestPermissionedGenericSetting(BaseGenericSetting): + title = models.CharField(max_length=100) + sensitive_email = models.EmailField(max_length=50) + + panels = [ + FieldPanel("title"), + FieldPanel( + "sensitive_email", + permission="tests.can_edit_sensitive_email_generic_setting", + ), + ] + + class Meta: + permissions = [ + ( + "can_edit_sensitive_email_generic_setting", + "Can edit sensitive email generic setting.", + ), + ] + + +@register_setting +class TestPermissionedSiteSetting(BaseSiteSetting): + title = models.CharField(max_length=100) + sensitive_email = models.EmailField(max_length=50) + + panels = [ + FieldPanel("title"), + FieldPanel( + "sensitive_email", permission="tests.can_edit_sensitive_email_site_setting" + ), + ] + + class Meta: + permissions = [ + ( + "can_edit_sensitive_email_site_setting", + "Can edit sensitive email site setting.", + ), + ] + + @register_setting class ImportantPagesSiteSetting(BaseSiteSetting): sign_up_page = models.ForeignKey( @@ -2229,6 +2272,9 @@ def is_cool(self): def __str__(self): return f"{self.name} ({self.release_date})" + class Meta: + permissions = [("can_set_release_date", "Can set release date")] + class PurgeRevisionsProtectedTestModel(models.Model): revision = models.OneToOneField(
wagtail/test/testapp/views.py+1 −1 modified@@ -227,7 +227,7 @@ class FeatureCompleteToyViewSet(ModelViewSet): panels = [ FieldPanel("name"), - FieldPanel("release_date"), + FieldPanel("release_date", permission="tests.can_set_release_date"), ]
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
9- github.com/advisories/GHSA-w2v8-php4-p8hcghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-32882ghsaADVISORY
- docs.wagtail.org/en/stable/extending/generic_views.htmlnvdWEB
- docs.wagtail.org/en/stable/reference/contrib/settings.htmlnvdWEB
- docs.wagtail.org/en/stable/reference/pages/panels.htmlnvdWEB
- github.com/wagtail/wagtail/commit/ab2a5d82b4ee3c909d2456704388ccf90e367c9bnvdWEB
- github.com/wagtail/wagtail/commit/fa0d4829f9c81eefb37cc058e2fa1b6a918741daghsaWEB
- github.com/wagtail/wagtail/releases/tag/v6.0.3ghsaWEB
- github.com/wagtail/wagtail/security/advisories/GHSA-w2v8-php4-p8hcnvdWEB
News mentions
0No linked articles in our index yet.