summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorSimon Charette <charette.s@gmail.com>2014-08-07 00:18:10 -0400
committerTim Graham <timograham@gmail.com>2014-08-20 14:39:40 -0400
commit53ff0969822ac2248a89ccb6fef1088212dc800d (patch)
tree57564f5a5293329b156592b82e964f7c4929598b /django
parent5307ce565fbedb9cc27cbe7c757b41a00438d37c (diff)
Prevented data leakage in contrib.admin via query string manipulation.
This is a security fix. Disclosure following shortly.
Diffstat (limited to 'django')
-rw-r--r--django/contrib/admin/exceptions.py5
-rw-r--r--django/contrib/admin/options.py26
-rw-r--r--django/contrib/admin/views/main.py9
3 files changed, 36 insertions, 4 deletions
diff --git a/django/contrib/admin/exceptions.py b/django/contrib/admin/exceptions.py
index 2e094c6da1..f619bc2252 100644
--- a/django/contrib/admin/exceptions.py
+++ b/django/contrib/admin/exceptions.py
@@ -4,3 +4,8 @@ from django.core.exceptions import SuspiciousOperation
class DisallowedModelAdminLookup(SuspiciousOperation):
"""Invalid filter was passed to admin view via URL querystring"""
pass
+
+
+class DisallowedModelAdminToField(SuspiciousOperation):
+ """Invalid to_field was passed to admin view via URL query string"""
+ pass
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index 3b429fbaed..ce89cf4dbd 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -11,6 +11,7 @@ from django.contrib.admin import widgets, helpers
from django.contrib.admin import validation
from django.contrib.admin.checks import (BaseModelAdminChecks, ModelAdminChecks,
InlineModelAdminChecks)
+from django.contrib.admin.exceptions import DisallowedModelAdminToField
from django.contrib.admin.utils import (quote, unquote, flatten_fieldsets,
get_deleted_objects, model_format_dict, NestedObjects,
lookup_needs_distinct)
@@ -434,6 +435,24 @@ class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
valid_lookups.append(filter_item)
return clean_lookup in valid_lookups
+ 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 in related_object.field.foreign_related_fields):
+ return True
+
+ return False
+
def has_add_permission(self, request):
"""
Returns True if the given request has permission to add an object.
@@ -1337,6 +1356,10 @@ class ModelAdmin(BaseModelAdmin):
@transaction.atomic
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
+ to_field = request.POST.get(TO_FIELD_VAR, request.GET.get(TO_FIELD_VAR))
+ if to_field and not self.to_field_allowed(request, to_field):
+ raise DisallowedModelAdminToField("The field %s cannot be referenced." % to_field)
+
model = self.model
opts = model._meta
add = object_id is None
@@ -1409,8 +1432,7 @@ class ModelAdmin(BaseModelAdmin):
original=obj,
is_popup=(IS_POPUP_VAR in request.POST or
IS_POPUP_VAR in request.GET),
- to_field=request.POST.get(TO_FIELD_VAR,
- request.GET.get(TO_FIELD_VAR)),
+ to_field=to_field,
media=media,
inline_admin_formsets=inline_formsets,
errors=helpers.AdminErrorList(form, formsets),
diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py
index d1b6d82a3e..1f42310f8e 100644
--- a/django/contrib/admin/views/main.py
+++ b/django/contrib/admin/views/main.py
@@ -12,7 +12,9 @@ from django.utils.translation import ugettext, ugettext_lazy
from django.utils.http import urlencode
from django.contrib.admin import FieldListFilter
-from django.contrib.admin.exceptions import DisallowedModelAdminLookup
+from django.contrib.admin.exceptions import (
+ DisallowedModelAdminLookup, DisallowedModelAdminToField,
+)
from django.contrib.admin.options import IncorrectLookupParameters, IS_POPUP_VAR, TO_FIELD_VAR
from django.contrib.admin.utils import (quote, get_fields_from_path,
lookup_needs_distinct, prepare_lookup_value)
@@ -58,7 +60,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]