1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
|
Description: Prevented data leakage in contrib.admin via query string manipulation
Patch backported by Thorsten Alteholz and Raphaƫl Hertzog.
Origin: backport, https://github.com/django/django/commit/027bd348642007617518379f8b02546abacaa6e0
Author: Simon Charette <charette.s@gmail.com>
Last-Update: 2014-09-26
--- /dev/null
+++ b/django/contrib/admin/exceptions.py
@@ -0,0 +1,6 @@
+from django.core.exceptions import SuspiciousOperation
+
+
+class DisallowedModelAdminToField(SuspiciousOperation):
+ """Invalid to_field was passed to admin view via URL query string"""
+ pass
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -306,6 +306,24 @@ class ModelAdmin(BaseModelAdmin):
return forms.Media(js=['%s%s' % (settings.ADMIN_MEDIA_PREFIX, url) for url in js])
media = property(_media)
+ def to_field_allowed(self, request, to_field):
+ opts = self.model._meta
+
+ try:
+ field = opts.get_field(to_field)
+ except FieldDoesNotExist:
+ return False
+
+ # Make sure at least one of the models registered for this site
+ # references this field.
+ registered_models = self.admin_site._registry
+ for related_object in opts.get_all_related_objects():
+ if (related_object.model in registered_models and
+ field == related_object.field.rel.get_related_field()):
+ return True
+
+ return False
+
def has_add_permission(self, request):
"Returns True if the given request has permission to add an object."
opts = self.opts
--- a/django/contrib/admin/views/main.py
+++ b/django/contrib/admin/views/main.py
@@ -1,4 +1,5 @@
from django.contrib.admin.filterspecs import FilterSpec
+from django.contrib.admin.exceptions import DisallowedModelAdminToField
from django.contrib.admin.options import IncorrectLookupParameters
from django.contrib.admin.util import quote
from django.core.exceptions import SuspiciousOperation
@@ -50,7 +51,10 @@ class ChangeList(object):
self.page_num = 0
self.show_all = ALL_VAR in request.GET
self.is_popup = IS_POPUP_VAR in request.GET
- self.to_field = request.GET.get(TO_FIELD_VAR)
+ to_field = request.GET.get(TO_FIELD_VAR)
+ if to_field and not model_admin.to_field_allowed(request, to_field):
+ raise DisallowedModelAdminToField("The field %s cannot be referenced." % to_field)
+ self.to_field = to_field
self.params = dict(request.GET.items())
if PAGE_VAR in self.params:
del self.params[PAGE_VAR]
--- a/tests/regressiontests/admin_views/tests.py
+++ b/tests/regressiontests/admin_views/tests.py
@@ -11,6 +11,8 @@ from django.contrib.contenttypes.models
from django.contrib.admin.models import LogEntry, DELETION
from django.contrib.admin.sites import LOGIN_FORM_KEY
from django.contrib.admin.util import quote
+from django.contrib.admin.views.main import TO_FIELD_VAR
+from django.contrib.admin.exceptions import DisallowedModelAdminToField
from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME
from django.forms.util import ErrorList
from django.test import TestCase
@@ -303,6 +305,22 @@ class AdminViewBasicTest(TestCase):
self.client.get, "/test_admin/admin/admin_views/album/?owner__email__startswith=fuzzy"
)
+ def test_disallowed_to_field(self):
+ self.assertRaises(DisallowedModelAdminToField,
+ self.client.get, "/test_admin/admin/admin_views/section/",
+ {TO_FIELD_VAR: 'missing_field'})
+
+ # Specifying a field that is not refered by any other model registered
+ # to this admin site should raise an exception.
+ self.assertRaises(DisallowedModelAdminToField,
+ self.client.get, "/test_admin/admin/admin_views/section/",
+ {TO_FIELD_VAR: 'name'})
+
+ # Specifying a field referenced by another model should be allowed.
+ response = self.client.get("/test_admin/admin/admin_views/section/",
+ {TO_FIELD_VAR: 'id'})
+ self.assertEqual(response.status_code, 200)
+
class SaveAsTests(TestCase):
fixtures = ['admin-views-users.xml','admin-views-person.xml']
|