summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorJacob Walls <jacobtylerwalls@gmail.com>2026-03-12 11:00:05 -0400
committerJacob Walls <jacobtylerwalls@gmail.com>2026-04-07 07:12:16 -0400
commitef8b25dcc06d158683a5623ce406d561638f4073 (patch)
treecd2cdb9556e001c770247091a49b14b2850a3a59 /django
parentcaf90a971f09323775ed0cacf94eadaf39d040e0 (diff)
Fixed CVE-2026-4277 -- Checked add permissions in GenericInlineModelAdmin.
Edit permissions were still checked as part of ordinary form validation, but because GenericInlineModelAdmin overrides get_formset(), it lacked InlineModelAdmin's dynamic DeleteProtectedModelForm.has_changed() logic for checking permissions server-side, leaving the add case unaddressed. This change reimplements the relevant part of InlineModelAdmin.get_formset(). Thanks N05ec@LZU-DSLab for the report, and Natalia Bidart, Markus Holtermann, and Simon Charette for reviews.
Diffstat (limited to 'django')
-rw-r--r--django/contrib/contenttypes/admin.py15
1 files changed, 15 insertions, 0 deletions
diff --git a/django/contrib/contenttypes/admin.py b/django/contrib/contenttypes/admin.py
index f595ce5285..d324a4f4fe 100644
--- a/django/contrib/contenttypes/admin.py
+++ b/django/contrib/contenttypes/admin.py
@@ -127,6 +127,21 @@ class GenericInlineModelAdmin(InlineModelAdmin):
**kwargs,
}
+ base_model_form = defaults["form"]
+ can_change = self.has_change_permission(request, obj) if request else True
+ can_add = self.has_add_permission(request, obj) if request else True
+
+ class PermissionProtectedModelForm(base_model_form):
+ def has_changed(self):
+ # Protect against unauthorized edits.
+ if not can_change and not self.instance._state.adding:
+ return False
+ if not can_add and self.instance._state.adding:
+ return False
+ return super().has_changed()
+
+ defaults["form"] = PermissionProtectedModelForm
+
if defaults["fields"] is None and not modelform_defines_fields(
defaults["form"]
):