diff options
| author | Simon Charette <charette.s@gmail.com> | 2014-08-07 00:18:10 -0400 |
|---|---|---|
| committer | Tim Graham <timograham@gmail.com> | 2014-08-20 14:39:40 -0400 |
| commit | 53ff0969822ac2248a89ccb6fef1088212dc800d (patch) | |
| tree | 57564f5a5293329b156592b82e964f7c4929598b /django | |
| parent | 5307ce565fbedb9cc27cbe7c757b41a00438d37c (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.py | 5 | ||||
| -rw-r--r-- | django/contrib/admin/options.py | 26 | ||||
| -rw-r--r-- | django/contrib/admin/views/main.py | 9 |
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] |
