Anonymous committed 344a7b6

[1.1.X] Fix a security issue in the admin. Disclosure and new release forthcoming.

  • Participants
  • Parent commits 2ccf9bd
  • Branches releases/1.1.X

Comments (0)

Files changed (4)


 from django.contrib.admin.util import unquote, flatten_fieldsets, get_deleted_objects, model_ngettext, model_format_dict
 from django.core.exceptions import PermissionDenied, ValidationError
 from django.db import models, transaction
-from django.db.models.fields import BLANK_CHOICE_DASH
+from django.db.models.related import RelatedObject
+from django.db.models.fields import BLANK_CHOICE_DASH, FieldDoesNotExist
+from django.db.models.sql.constants import LOOKUP_SEP, QUERY_TERMS
 from django.http import Http404, HttpResponse, HttpResponseRedirect
 from django.shortcuts import get_object_or_404, render_to_response
 from django.utils.datastructures import SortedDict
         return None
     declared_fieldsets = property(_declared_fieldsets)
+    def lookup_allowed(self, lookup):
+        parts = lookup.split(LOOKUP_SEP)
+        # Last term in lookup is a query term (__exact, __startswith etc)
+        # This term can be ignored.
+        if len(parts) > 1 and parts[-1] in QUERY_TERMS:
+            parts.pop()
+        # Special case -- foo__id__exact and foo__id queries are implied
+        # if foo has been specificially included in the lookup list; so
+        # drop __id if it is the last part.
+        if len(parts) > 1 and parts[-1] ==
+            parts.pop()
+        try:
+            self.model._meta.get_field_by_name(parts[0])
+        except FieldDoesNotExist:
+            # Lookups on non-existants fields are ok, since they're ignored
+            # later.
+            return True
+        else:
+            clean_lookup = LOOKUP_SEP.join(parts)
+            return clean_lookup in self.list_filter or clean_lookup == self.date_hierarchy
 class ModelAdmin(BaseModelAdmin):
     "Encapsulates all admin options and functionality for a given model."


 from django.contrib.admin.filterspecs import FilterSpec
 from django.contrib.admin.options import IncorrectLookupParameters
 from django.contrib.admin.util import quote
+from django.core.exceptions import SuspiciousOperation
 from django.core.paginator import Paginator, InvalidPage
 from django.db import models
 from django.db.models.query import QuerySet
                     lookup_params[key] = True
+            if not self.model_admin.lookup_allowed(key):
+                raise SuspiciousOperation(
+                    "Filtering by %s not allowed" % key
+                )
         # Apply lookup parameters from the query string.
             qs = qs.filter(**lookup_params)
         # Naked except! Because we don't have any other way of validating "params".
         # They might be invalid if the keyword arguments are incorrect, or if the
         # values are not in the correct type, so we might get FieldError, ValueError,
-        # ValicationError, or ? from a custom field that raises yet something else 
+        # ValicationError, or ? from a custom field that raises yet something else
         # when handed impossible data.
             raise IncorrectLookupParameters


 from django.core.mail import EmailMessage
 from django import forms
 from django.forms.models import BaseModelFormSet
+from django.contrib.auth.models import User
 from django.contrib.contenttypes import generic
 from django.contrib.contenttypes.models import ContentType
 class ArticleAdmin(admin.ModelAdmin):
     list_display = ('content', 'date', callable_year, 'model_year', 'modeladmin_year')
-    list_filter = ('date',)
+    list_filter = ('date', 'section')
     def changelist_view(self, request):
         "Test that extra_context works"
     def __unicode__(self):
+class Album(models.Model):
+    owner = models.ForeignKey(User)
+    title = models.CharField(max_length=30)
+class AlbumAdmin(admin.ModelAdmin):
+    list_filter = ['title']
+, ArticleAdmin), CustomArticleAdmin), save_as=True, inlines=[ArticleInline]), inlines=[ChapterInline]), AlbumAdmin)


 import re
 import datetime
 from django.conf import settings
+from django.core.exceptions import SuspiciousOperation
 from django.core.files import temp as tempfile
 from django.contrib.auth.models import User, Permission
 from django.contrib.contenttypes.models import ContentType
         self.assertContains(response, 'Choisir une heure')
+    def test_disallowed_filtering(self):
+        self.assertRaises(SuspiciousOperation,
+            self.client.get, "/test_admin/admin/admin_views/album/?owner__email__startswith=fuzzy"
+        )
 class SaveAsTests(TestCase):
     fixtures = ['admin-views-users.xml','admin-views-person.xml']