diff -u python-django-1.0.2/debian/changelog python-django-1.0.2/debian/changelog --- python-django-1.0.2/debian/changelog +++ python-django-1.0.2/debian/changelog @@ -1,3 +1,12 @@ +python-django (1.0.2-1ubuntu0.1) jaunty-security; urgency=low + + * SECURITY UPDATE: crafted URL can cause the development server to serve + any file to which it has read access + http://www.djangoproject.com/weblog/2009/jul/28/security/ + - Apply upstream security patch changeset 11353 + + -- Alistair Marshall Fri, 07 Aug 2009 14:06:30 +0100 + python-django (1.0.2-1) unstable; urgency=low [ Chris Lamb ] diff -u python-django-1.0.2/debian/control python-django-1.0.2/debian/control --- python-django-1.0.2/debian/control +++ python-django-1.0.2/debian/control @@ -1,7 +1,8 @@ Source: python-django Section: python Priority: optional -Maintainer: Brett Parker +Maintainer: Ubuntu MOTU Developers +XSBC-Original-Maintainer: Brett Parker Uploaders: Raphael Hertzog , Gustavo Noronha Silva , David Spreen , Chris Lamb , Debian Python Modules Team Standards-Version: 3.8.0 Build-Depends: debhelper (>= 5.0.37.2), python-dev, cdbs (>= 0.4.42), python-setuptools (>= 0.6b3), python-support (>= 0.3) only in patch2: unchanged: --- python-django-1.0.2.orig/debian/patches/05_security_admin-media-handlerr.diff +++ python-django-1.0.2/debian/patches/05_security_admin-media-handlerr.diff @@ -0,0 +1,89 @@ +# +# Ubuntu: https://bugs.launchpad.net/bugs/408825 +# Upstream: http://www.djangoproject.com/weblog/2009/jul/28/security/ +# Patch: http://code.djangoproject.com/changeset/11353 +# Description: crafted URL can cause the development server to serve any file to +# which it has read access. +# + +diff -Nur -x '*.orig' -x '*~' python-django-1.0.2/django/core/management/commands/runserver.py python-django-1.0.2.new/django/core/management/commands/runserver.py +--- python-django-1.0.2/django/core/management/commands/runserver.py 2008-11-19 05:44:20.000000000 +0000 ++++ python-django-1.0.2.new/django/core/management/commands/runserver.py 2009-08-07 14:41:18.000000000 +0100 +@@ -56,8 +56,7 @@ + translation.activate(settings.LANGUAGE_CODE) + + try: +- path = admin_media_path or django.__path__[0] + '/contrib/admin/media' +- handler = AdminMediaHandler(WSGIHandler(), path) ++ handler = AdminMediaHandler(WSGIHandler(), admin_media_path) + run(addr, int(port), handler) + except WSGIServerException, e: + # Use helpful error messages instead of ugly tracebacks. +diff -Nur -x '*.orig' -x '*~' python-django-1.0.2/django/core/servers/basehttp.py python-django-1.0.2.new/django/core/servers/basehttp.py +--- python-django-1.0.2/django/core/servers/basehttp.py 2008-11-19 05:44:20.000000000 +0000 ++++ python-django-1.0.2.new/django/core/servers/basehttp.py 2009-08-07 14:41:18.000000000 +0100 +@@ -16,6 +16,7 @@ + import urllib + + from django.utils.http import http_date ++from django.utils._os import safe_join + + __version__ = "0.1" + __all__ = ['WSGIServer','WSGIRequestHandler'] +@@ -620,11 +621,25 @@ + self.application = application + if not media_dir: + import django +- self.media_dir = django.__path__[0] + '/contrib/admin/media' ++ self.media_dir = \ ++ os.path.join(django.__path__[0], 'contrib', 'admin', 'media') + else: + self.media_dir = media_dir + self.media_url = settings.ADMIN_MEDIA_PREFIX + ++ def file_path(self, url): ++ """ ++ Returns the path to the media file on disk for the given URL. ++ ++ The passed URL is assumed to begin with ADMIN_MEDIA_PREFIX. If the ++ resultant file path is outside the media directory, then a ValueError ++ is raised. ++ """ ++ # Remove ADMIN_MEDIA_PREFIX. ++ relative_url = url[len(self.media_url):] ++ relative_path = urllib.url2pathname(relative_url) ++ return safe_join(self.media_dir, relative_path) ++ + def __call__(self, environ, start_response): + import os.path + +@@ -635,19 +650,25 @@ + return self.application(environ, start_response) + + # Find the admin file and serve it up, if it exists and is readable. +- relative_url = environ['PATH_INFO'][len(self.media_url):] +- file_path = os.path.join(self.media_dir, relative_url) ++ try: ++ file_path = self.file_path(environ['PATH_INFO']) ++ except ValueError: # Resulting file path was not valid. ++ status = '404 NOT FOUND' ++ headers = {'Content-type': 'text/plain'} ++ output = ['Page not found: %s' % environ['PATH_INFO']] ++ start_response(status, headers.items()) ++ return output + if not os.path.exists(file_path): + status = '404 NOT FOUND' + headers = {'Content-type': 'text/plain'} +- output = ['Page not found: %s' % file_path] ++ output = ['Page not found: %s' % environ['PATH_INFO']] + else: + try: + fp = open(file_path, 'rb') + except IOError: + status = '401 UNAUTHORIZED' + headers = {'Content-type': 'text/plain'} +- output = ['Permission denied: %s' % file_path] ++ output = ['Permission denied: %s' % environ['PATH_INFO']] + else: + # This is a very simple implementation of conditional GET with + # the Last-Modified header. It makes media files a bit speedier