Al...@bcc190cf-cafb-0310-a4f2-bffc1f526a37  committed 58ea91a

Fix a security issue in the admin. Disclosure and new release forthcoming.

  • Participants
  • Parent commits 816ba38
  • Branches releases/1.2.X

Comments (0)

Files changed (4)

File django/contrib/admin/

 from django.views.decorators.csrf import csrf_protect
 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.decorators import method_decorator
     def get_readonly_fields(self, request, obj=None):
         return self.readonly_fields
+    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."

File django/contrib/admin/views/

 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

File tests/regressiontests/admin_views/

 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"
     owner = models.ForeignKey(User)
     title = models.CharField(max_length=30)
+class AlbumAdmin(admin.ModelAdmin):
+    list_filter = ['title']
+, ArticleAdmin), CustomArticleAdmin), save_as=True, inlines=[ArticleInline]), PizzaAdmin), AlbumAdmin)

File tests/regressiontests/admin_views/

 import datetime
 from django.conf import settings
+from django.core.exceptions import SuspiciousOperation
 from django.core.files import temp as tempfile
 # Register auth models with the admin.
 from django.contrib.auth import REDIRECT_FIELD_NAME, admin
         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']