summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacob Walls <jacobtylerwalls@gmail.com>2026-03-16 18:05:22 -0400
committerJacob Walls <jacobtylerwalls@gmail.com>2026-04-07 07:12:20 -0400
commit6afe7ce93964f56e33a29d477c269436f9b60cbf (patch)
treec17401bca74e97de72def6c67a323ff0c27b94f3
parentef8b25dcc06d158683a5623ce406d561638f4073 (diff)
Fixed CVE-2026-4292 -- Disallowed instance creation via ModelAdmin.list_editable.
Thanks Natalia Bidart, Jake Howard, and Markus Holtermann for reviews.
-rw-r--r--django/contrib/admin/options.py3
-rw-r--r--docs/releases/4.2.30.txt10
-rw-r--r--docs/releases/5.2.13.txt10
-rw-r--r--docs/releases/6.0.4.txt10
-rw-r--r--tests/admin_views/admin.py10
-rw-r--r--tests/admin_views/tests.py17
6 files changed, 59 insertions, 1 deletions
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index c59cb2ab4c..4841661f16 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -34,6 +34,7 @@ from django.contrib.admin.utils import (
from django.contrib.admin.widgets import AutocompleteSelect, AutocompleteSelectMultiple
from django.contrib.auth import get_permission_codename
from django.core.exceptions import (
+ BadRequest,
FieldDoesNotExist,
FieldError,
PermissionDenied,
@@ -2119,6 +2120,8 @@ class ModelAdmin(BaseModelAdmin):
for form in cl.formset.forms:
if form.has_changed():
obj = self.save_form(request, form, change=True)
+ if obj._state.adding:
+ raise BadRequest("list_editable does not allow adding.")
self.save_model(request, obj, form, change=True)
self.save_related(request, form, formsets=[], change=True)
change_msg = self.construct_change_message(
diff --git a/docs/releases/4.2.30.txt b/docs/releases/4.2.30.txt
index a6d2deef3c..de19a6f08f 100644
--- a/docs/releases/4.2.30.txt
+++ b/docs/releases/4.2.30.txt
@@ -36,3 +36,13 @@ forged ``POST`` data in
This issue has severity "low" according to the :ref:`Django security policy
<security-disclosure>`.
+
+CVE-2026-4292: Privilege abuse in ``ModelAdmin.list_editable``
+==============================================================
+
+Admin changelist forms using
+:attr:`~django.contrib.admin.ModelAdmin.list_editable` incorrectly allowed new
+instances to be created via forged ``POST`` data.
+
+This issue has severity "low" according to the :ref:`Django security policy
+<security-disclosure>`.
diff --git a/docs/releases/5.2.13.txt b/docs/releases/5.2.13.txt
index 8b03103508..8b303f2700 100644
--- a/docs/releases/5.2.13.txt
+++ b/docs/releases/5.2.13.txt
@@ -36,3 +36,13 @@ forged ``POST`` data in
This issue has severity "low" according to the :ref:`Django security policy
<security-disclosure>`.
+
+CVE-2026-4292: Privilege abuse in ``ModelAdmin.list_editable``
+==============================================================
+
+Admin changelist forms using
+:attr:`~django.contrib.admin.ModelAdmin.list_editable` incorrectly allowed new
+instances to be created via forged ``POST`` data.
+
+This issue has severity "low" according to the :ref:`Django security policy
+<security-disclosure>`.
diff --git a/docs/releases/6.0.4.txt b/docs/releases/6.0.4.txt
index 73b08436c1..4287a3086a 100644
--- a/docs/releases/6.0.4.txt
+++ b/docs/releases/6.0.4.txt
@@ -37,6 +37,16 @@ forged ``POST`` data in
This issue has severity "low" according to the :ref:`Django security policy
<security-disclosure>`.
+CVE-2026-4292: Privilege abuse in ``ModelAdmin.list_editable``
+==============================================================
+
+Admin changelist forms using
+:attr:`~django.contrib.admin.ModelAdmin.list_editable` incorrectly allowed new
+instances to be created via forged ``POST`` data.
+
+This issue has severity "low" according to the :ref:`Django security policy
+<security-disclosure>`.
+
Bugfixes
========
diff --git a/tests/admin_views/admin.py b/tests/admin_views/admin.py
index 0f05a66746..26648a1e47 100644
--- a/tests/admin_views/admin.py
+++ b/tests/admin_views/admin.py
@@ -369,6 +369,14 @@ class PersonAdmin(admin.ModelAdmin):
return super().get_queryset(request).order_by("age")
+class ParentWithUUIDPKAdmin(admin.ModelAdmin):
+ list_display = ("id", "title")
+ list_editable = ("title",)
+
+ def has_add_permission(self, request):
+ return False
+
+
class FooAccountAdmin(admin.StackedInline):
model = FooAccount
extra = 1
@@ -1286,7 +1294,7 @@ site.register(ReferencedByInline)
site.register(InlineReferer, InlineRefererAdmin)
site.register(ReferencedByGenRel)
site.register(GenRelReference)
-site.register(ParentWithUUIDPK)
+site.register(ParentWithUUIDPK, ParentWithUUIDPKAdmin)
site.register(RelatedPrepopulated, search_fields=["name"])
site.register(RelatedWithUUIDPKModel)
site.register(ReadOnlyRelatedField, ReadOnlyRelatedFieldAdmin)
diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py
index 4359a31135..107267b342 100644
--- a/tests/admin_views/tests.py
+++ b/tests/admin_views/tests.py
@@ -4,6 +4,7 @@ import re
import sys
import unittest
import zoneinfo
+from http import HTTPStatus
from unittest import mock
from urllib.parse import parse_qsl, urljoin, urlsplit
@@ -4730,6 +4731,22 @@ class AdminViewListEditable(TestCase):
self.assertIs(Person.objects.get(name="John Mauchly").alive, False)
+ def test_forged_post_submission_when_no_add_permission(self):
+ before_count = ParentWithUUIDPK.objects.count()
+ data = {
+ "form-TOTAL_FORMS": "1",
+ "form-INITIAL_FORMS": "0",
+ "form-MAX_NUM_FORMS": "0",
+ "form-0-title": "The News",
+ "form-0-id": "",
+ "_save": "Save",
+ }
+ # This model admin allows no add permissions.
+ changelist_url = reverse("admin:admin_views_parentwithuuidpk_changelist")
+ response = self.client.post(changelist_url, data)
+ self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST)
+ self.assertEqual(ParentWithUUIDPK.objects.count(), before_count)
+
def test_non_field_errors(self):
"""
Non-field errors are displayed for each of the forms in the