Fixed #23601 -- Ensured view exists in URLconf before importing it in admindocs.

This commit is contained in:
Markus Holtermann 2014-10-04 19:04:21 +02:00 committed by Tim Graham
parent a24cf21722
commit 2f16ff5a6c
4 changed files with 29 additions and 4 deletions

View File

@ -143,10 +143,11 @@ class ViewDetailView(BaseAdminDocsView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
view = self.kwargs['view'] view = self.kwargs['view']
mod, func = urlresolvers.get_mod_func(view) urlconf = urlresolvers.get_urlconf()
try: if urlresolvers.get_resolver(urlconf)._is_callback(view):
mod, func = urlresolvers.get_mod_func(view)
view_func = getattr(import_module(mod), func) view_func = getattr(import_module(mod), func)
except (ImportError, AttributeError): else:
raise Http404 raise Http404
title, body, metadata = utils.parse_docstring(view_func.__doc__) title, body, metadata = utils.parse_docstring(view_func.__doc__)
if title: if title:

View File

@ -353,6 +353,11 @@ class RegexURLResolver(LocaleRegexProvider):
self._populate() self._populate()
return self._app_dict[language_code] return self._app_dict[language_code]
def _is_callback(self, name):
if not self._populated:
self._populate()
return name in self._callback_strs
def resolve(self, path): def resolve(self, path):
path = force_text(path) # path may be a reverse_lazy object path = force_text(path) # path may be a reverse_lazy object
tried = [] tried = []
@ -430,7 +435,7 @@ class RegexURLResolver(LocaleRegexProvider):
original_lookup = lookup_view original_lookup = lookup_view
try: try:
if lookup_view in self._callback_strs: if self._is_callback(lookup_view):
lookup_view = get_callable(lookup_view, True) lookup_view = get_callable(lookup_view, True)
except (ImportError, AttributeError) as e: except (ImportError, AttributeError) as e:
raise NoReverseMatch("Error importing '%s': %s." % (lookup_view, e)) raise NoReverseMatch("Error importing '%s': %s." % (lookup_view, e))

View File

@ -76,6 +76,14 @@ Minor features
<django.contrib.admin.ModelAdmin.show_full_result_count>` to control whether <django.contrib.admin.ModelAdmin.show_full_result_count>` to control whether
or not the full count of objects should be displayed on a filtered admin page. or not the full count of objects should be displayed on a filtered admin page.
:mod:`django.contrib.admindocs`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* The view to browse view details now checks if the view specified in the URL
exists in the URLconf. Previously it was possible to import arbitrary
packages from the Python path. This was not considered a security issue
because ``admindocs`` is only accessible to staff users.
:mod:`django.contrib.auth` :mod:`django.contrib.auth`
^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -1,3 +1,4 @@
import sys
import unittest import unittest
from django.conf import settings from django.conf import settings
@ -84,6 +85,16 @@ class AdminDocViewTests(AdminDocsTestCase):
# View docstring # View docstring
self.assertContains(response, 'Base view for admindocs views.') self.assertContains(response, 'Base view for admindocs views.')
def test_view_detail_illegal_import(self):
"""
#23601 - Ensure the view exists in the URLconf.
"""
response = self.client.get(
reverse('django-admindocs-views-detail',
args=['urlpatterns_reverse.nonimported_module.view']))
self.assertEqual(response.status_code, 404)
self.assertNotIn("urlpatterns_reverse.nonimported_module", sys.modules)
def test_model_index(self): def test_model_index(self):
response = self.client.get(reverse('django-admindocs-models-index')) response = self.client.get(reverse('django-admindocs-models-index'))
self.assertContains( self.assertContains(