Hello community,

here is the log from the commit of package python-django.4317 for 
openSUSE:13.1:Update checked in at 2015-12-04 11:41:27
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:13.1:Update/python-django.4317 (Old)
 and      /work/SRC/openSUSE:13.1:Update/.python-django.4317.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-django.4317"

Changes:
--------
New Changes file:

--- /dev/null   2015-11-02 12:10:47.524024255 +0100
+++ 
/work/SRC/openSUSE:13.1:Update/.python-django.4317.new/python-django.changes    
    2015-12-04 11:41:28.000000000 +0100
@@ -0,0 +1,367 @@
+-------------------------------------------------------------------
+Wed Nov 18 09:40:55 UTC 2015 - [email protected]
+
+- add 0010-1.5.x-Fixed-a-settings-leak-possibility-in-the-date-.patch
+  to prevent settings leak in date template filter (bnc#955412, CVE-2015-8213)
+
+-------------------------------------------------------------------
+Mon Oct 12 12:49:26 UTC 2015 - [email protected]
+
+- add 0009-1.5.x-Prevented-newlines-from-being-accepted-in-some.patch
+  to prevent Header injection possibility (bnc#937523, CVE-2015-5144)
+- add 0008-1.5.x-Fixed-19324-Avoided-creating-a-session-record-.patch
+  to prevent Denial-of-service possibility by filling session store
+  (bnc#937522, CVE-2015-5143)
+
+-------------------------------------------------------------------
+Wed Sep  9 11:12:40 UTC 2015 - [email protected]
+
+- Add 0007-1.6.x-Fixed-DoS-possiblity-in-contrib.auth.views.log.patch
+  (bnc#941587, CVE-2015-5963)
+
+-------------------------------------------------------------------
+Fri Mar 20 12:56:53 UTC 2015 - [email protected]
+
+- Made is_safe_url() reject URLs that start with control characters
+  to mitigate possible XSS attack via user-supplied redirect URLs
+  (bnc#923176, CVE-2015-2317)
+  + Add 0006-1.5.x-Made-is_safe_url-reject-URLs-that-start-with-c.patch
+
+-------------------------------------------------------------------
+Wed Jan 28 16:21:41 UTC 2015 - [email protected]
+
+- Method check_for_test_cookie is deprecated, bnc#914706
+  + Add 0005-1.6.x-Method-check_for_test_cookie-is-deprecated.patch
+
+-------------------------------------------------------------------
+Fri Jan 23 08:41:48 UTC 2015 - [email protected]
+
+- security fix backports
+  add 0001-1.5.x-Stripped-headers-containing-underscores-to-pre.patch 
(bnc#913053, CVE-2015-0219)
+  add 0002-1.5.x-Fixed-is_safe_url-to-handle-leading-whitespace.patch 
(bnc#913054, CVE-2015-0220)
+  add 0003-1.5.x-Prevented-views.static.serve-from-using-large-.patch 
(bnc#913056, CVE-2015-0221)
+  add 0004-1.5.x-Fixed-DoS-possibility-in-ModelMultipleChoiceFi.patch 
(bnc#913055, CVE-2015-0222)
+
+-------------------------------------------------------------------
+Wed Jan 21 09:57:12 UTC 2015 - [email protected]
+
+- Update to version 1.5.12:
+  + Fixed a regression with dynamically generated inlines and allowed field
+    references in the admin
+  + Allowed related many-to-many fields to be referenced in the admin
+  + Allowed inline and hidden references to admin fields
+
+-------------------------------------------------------------------
+Wed Sep  3 12:15:52 UTC 2014 - [email protected]
+
+- Update to version 1.5.10:
+  + Prevented reverse() from generating URLs pointing to other hosts
+    to prevent phishing attacks (bnc#893087, CVE-2014-0480)
+  + Removed O(n) algorithm when uploading duplicate file names
+    to fix file upload denial of service (bnc#893088, CVE-2014-0481)
+  + Modified RemoteUserMiddleware to logout on REMOTE_USE change
+    to prevent session hijacking (bnc#893089, CVE-2014-0482)
+  + Prevented data leakage in contrib.admin via query string manipulation
+    (bnc#893090, CVE-2014-0483)
+
+-------------------------------------------------------------------
+Mon May 26 07:22:53 UTC 2014 - [email protected]
+
+- Update to version 1.5.8:
+  + Fixed: Caches may incorrectly be allowed to store and serve private data
+    (bnc#877993, CVE-2014-1418)
+  + Fixed: Malformed redirect URLs from user input not correctly validated
+    (bnc#878641, CVE-2014-3730)
+  + Fixed queries that may return unexpected results on MySQL
+    due to typecasting (bnc#874956, CVE-2014-0474)
+  + Prevented leaking the CSRF token through caching
+    (bnc#874955, CVE-2014-0473)
+  + Fixed a remote code execution vulnerabilty in URL reversing
+    (bnc#874950, CVE-2014-0472)
+
+-------------------------------------------------------------------
+Thu Oct 31 14:14:58 UTC 2013 - [email protected]
+
+- Update to version 1.5.5:
+  + Readdressed denial-of-service via password hashers
+  + Properly rotate CSRF token on login
+
+-------------------------------------------------------------------
+Tue Sep 17 12:37:53 UTC 2013 - [email protected]
+
+- Update to version 1.5.4:
+  + Fixed denial-of-service via large passwords
+- Changes from version 1.5.3:
+  + Fixed directory traversal with ssi template tag
+
+-------------------------------------------------------------------
+Wed Aug 14 05:49:54 UTC 2013 - [email protected]
+
+- Update to 1.5.2:
+  - Security release, please check release notes for details:
+    https://www.djangoproject.com/weblog/2013/aug/13/security-releases-issued
+
+-------------------------------------------------------------------
+Thu Mar 28 23:27:01 UTC 2013 - [email protected]
+
+- Update to 1.5.1:
+   - Memory leak fix, please read release announcement at
+     https://www.djangoproject.com/weblog/2013/mar/28/django-151.
+
+-------------------------------------------------------------------
+Tue Feb 26 19:49:02 UTC 2013 - [email protected]
+
+- Update to 1.5:
+  - Please read the release notes
+    https://docs.djangoproject.com/en/1.5/releases/1.5
+
+-------------------------------------------------------------------
+Tue Dec 11 12:27:50 UTC 2012 - [email protected]
+
+- Update to 1.4.3:
+  - Security release:
+    - Host header poisoning
+    - Redirect poisoning
+  - Please check release notes for details:
+    https://www.djangoproject.com/weblog/2012/dec/10/security
+
+-------------------------------------------------------------------
+Sat Oct 20 13:41:10 UTC 2012 - [email protected]
+
+- Add a symlink from /usr/bin/django-admin.py to /usr/bin/django-admin
+
+-------------------------------------------------------------------
+Wed Oct 17 22:51:36 UTC 2012 - [email protected]
+
+- Update to 1.4.2:
+  - Security release:
+    - Host header poisoning
+  - Please check release notes for details:
+    https://www.djangoproject.com/weblog/2012/oct/17/security
+
+-------------------------------------------------------------------
+Mon Jul 30 21:38:31 UTC 2012 - [email protected]
+
+- Update to 1.4.1:
+  - Security release:
+    - Cross-site scripting in authentication views
+    - Denial-of-service in image validation
+    - Denial-of-service via get_image_dimensions()
+  - Please check release notes for details:
+    https://www.djangoproject.com/weblog/2012/jul/30/security-releases-issued
+
+-------------------------------------------------------------------
+Tue Jun 19 11:27:33 UTC 2012 - [email protected]
+
+- Add patch to support CSRF_COOKIE_HTTPONLY config
+
+-------------------------------------------------------------------
+Fri Mar 23 18:39:40 UTC 2012 - [email protected]
+
+- Update to 1.4:
+  - Please read the release notes
+    https://docs.djangoproject.com/en/dev/releases/1.4
+- Removed Patch2, it was merged on upstream,
+
+-------------------------------------------------------------------
+Thu Nov 24 12:30:40 UTC 2011 - [email protected]
+
+- Set license to SDPX style (BSD-3-Clause)
+- Package AUTHORS, LICENE and README files
+- No CFLAGS for noarch package
+- Drop runtime dependency on gettext-tools
+
+-------------------------------------------------------------------
+Sat Sep 10 12:05:07 UTC 2011 - [email protected]
+
+- Update to 1.3.1 to fix security issues, please read
+  https://www.djangoproject.com/weblog/2011/sep/09/security-releases-issued.
+
+-------------------------------------------------------------------
+Thu Mar 31 15:09:16 UTC 2011 - [email protected]
+
+- Fix build on SLES_9.
+
+-------------------------------------------------------------------
+Wed Mar 23 11:39:53 UTC 2011 - [email protected]
+
+- Update to 1.3 final;
+- Refresh patch empty-ip-2.diff.
+
+-------------------------------------------------------------------
+Fri Mar 18 03:45:45 UTC 2011 - [email protected]
+
+- Update to 1.3-rc1;
+- Regenerated spec file with py2pack;
+- No more need to fix wrong line endings;
+- Refresh patch empty-ip-2.diff with -p0.
++++ 170 more lines (skipped)
++++ between /dev/null
++++ and 
/work/SRC/openSUSE:13.1:Update/.python-django.4317.new/python-django.changes

New:
----
  0001-1.5.x-Stripped-headers-containing-underscores-to-pre.patch
  0002-1.5.x-Fixed-is_safe_url-to-handle-leading-whitespace.patch
  0003-1.5.x-Prevented-views.static.serve-from-using-large-.patch
  0004-1.5.x-Fixed-DoS-possibility-in-ModelMultipleChoiceFi.patch
  0005-1.6.x-Method-check_for_test_cookie-is-deprecated.patch
  0006-1.5.x-Made-is_safe_url-reject-URLs-that-start-with-c.patch
  0007-1.6.x-Fixed-DoS-possiblity-in-contrib.auth.views.log.patch
  0008-1.5.x-Fixed-19324-Avoided-creating-a-session-record-.patch
  0009-1.5.x-Prevented-newlines-from-being-accepted-in-some.patch
  0010-1.5.x-Fixed-a-settings-leak-possibility-in-the-date-.patch
  Django-1.2-completion-only-for-bash.patch
  Django-1.4-CSRF_COOKIE_HTTPONLY-support.patch
  Django-1.5.12.tar.gz
  python-django-rpmlintrc
  python-django.changes
  python-django.spec

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-django.spec ++++++
#
# spec file for package python-django
#
# Copyright (c) 2015 SUSE LINUX GmbH, Nuernberg, Germany.
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.

# Please submit bugfixes or comments via http://bugs.opensuse.org/
#


Name:           python-django
Version:        1.5.12
Release:        0.<RELEASE7>
Summary:        A high-level Python Web framework
License:        BSD-3-Clause
Group:          Development/Languages/Python
Url:            http://www.djangoproject.com
# https://www.djangoproject.com/download/1.5.10/tarball/
Source:         Django-%{version}.tar.gz
Source1:        python-django-rpmlintrc
# PATCH-FIX-UPSTREAM Django-1.2-completion-only-for-bash.patch
Patch1:         Django-1.2-completion-only-for-bash.patch
# PATCH-FIX-UPSTREAM - see https://github.com/django/django/pull/150/files
Patch2:         Django-1.4-CSRF_COOKIE_HTTPONLY-support.patch
# PATCH-BACKPORTS from 1.6.x
Patch11:        0001-1.5.x-Stripped-headers-containing-underscores-to-pre.patch
Patch12:        0002-1.5.x-Fixed-is_safe_url-to-handle-leading-whitespace.patch
Patch13:        0003-1.5.x-Prevented-views.static.serve-from-using-large-.patch
Patch14:        0004-1.5.x-Fixed-DoS-possibility-in-ModelMultipleChoiceFi.patch
Patch15:        0005-1.6.x-Method-check_for_test_cookie-is-deprecated.patch
Patch16:        0006-1.5.x-Made-is_safe_url-reject-URLs-that-start-with-c.patch
Patch17:        0007-1.6.x-Fixed-DoS-possiblity-in-contrib.auth.views.log.patch
Patch18:        0008-1.5.x-Fixed-19324-Avoided-creating-a-session-record-.patch
Patch19:        0009-1.5.x-Prevented-newlines-from-being-accepted-in-some.patch
Patch20:        0010-1.5.x-Fixed-a-settings-leak-possibility-in-the-date-.patch
BuildRequires:  python-devel
Requires:       python-xml
BuildRoot:      %{_tmppath}/%{name}-%{version}-build
#Requires:       gettext-tools
%if 0%{?suse_version}
%py_requires
%if 0%{?suse_version} > 1110
BuildArch:      noarch
%endif
%endif
%{!?python_sitelib: %global python_sitelib %(python -c "from 
distutils.sysconfig import get_python_lib; print get_python_lib()")}

%description
Django is a high-level Python Web framework that encourages rapid development 
and clean, pragmatic design.

%prep
%setup -q -n Django-%{version}
%patch1
%patch2
%patch11 -p1
%patch12 -p1
%patch13 -p1
%patch14 -p1
%patch15 -p1
%patch16 -p1
%patch17 -p1
%patch18 -p1
%patch19 -p1
%patch20 -p1

%build
python setup.py build

%install
python setup.py install --prefix=%{_prefix} --root=%{buildroot}
install -D -m 0755 extras/django_bash_completion 
%{buildroot}%{_sysconfdir}/bash_completion.d/django_bash_completion.sh
ln -s %{_bindir}/django-admin.py %{buildroot}%{_bindir}/django-admin

%files
%defattr(-,root,root,-)
%doc AUTHORS LICENSE README.rst
%{_bindir}/django-admin*
%{python_sitelib}/*
%{_sysconfdir}/bash_completion.d/django_bash_completion.sh

%changelog
++++++ 0001-1.5.x-Stripped-headers-containing-underscores-to-pre.patch ++++++
From 3feaba416575f6197b6e08ecb239cf8b9317bbff Mon Sep 17 00:00:00 2001
From: Carl Meyer <[email protected]>
Date: Wed, 10 Sep 2014 11:06:19 -0600
Subject: [PATCH 1/4] [1.5.x] Stripped headers containing underscores to
 prevent spoofing in WSGI environ. (bnc#913053, CVE-2015-0219)

This is a security fix.
WSGI header spoofing via underscore/dash conflation.
`Full description <https://www.djangoproject.com/weblog/2015/jan/13/security/>`

Thanks to Jedediah Smith for the report.

cherry-picked-from: d7597b31d5c03106eeba4be14a33b32a5e25f4ee
---
 django/core/servers/basehttp.py | 11 +++++++
 docs/howto/auth-remote-user.txt | 15 +++++++++
 tests/servers/test_basehttp.py  | 67 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 93 insertions(+)
 create mode 100644 tests/servers/test_basehttp.py

diff --git a/django/core/servers/basehttp.py b/django/core/servers/basehttp.py
index 9aed637..efc13d2 100644
--- a/django/core/servers/basehttp.py
+++ b/django/core/servers/basehttp.py
@@ -174,6 +174,17 @@ class WSGIRequestHandler(simple_server.WSGIRequestHandler, 
object):
 
         sys.stderr.write(msg)
 
+    def get_environ(self):
+        # Strip all headers with underscores in the name before constructing
+        # the WSGI environ. This prevents header-spoofing based on ambiguity
+        # between underscores and dashes both normalized to underscores in WSGI
+        # env vars. Nginx and Apache 2.4+ both do this as well.
+        for k, v in self.headers.items():
+            if '_' in k:
+                del self.headers[k]
+
+        return super(WSGIRequestHandler, self).get_environ()
+
 
 def run(addr, port, wsgi_handler, ipv6=False, threading=False):
     server_address = (addr, port)
diff --git a/docs/howto/auth-remote-user.txt b/docs/howto/auth-remote-user.txt
index 09ae928..cbfc526 100644
--- a/docs/howto/auth-remote-user.txt
+++ b/docs/howto/auth-remote-user.txt
@@ -66,6 +66,21 @@ If your authentication mechanism uses a custom HTTP header 
and not
     class CustomHeaderMiddleware(RemoteUserMiddleware):
         header = 'HTTP_AUTHUSER'
 
+.. warning::
+
+    Be very careful if using a ``RemoteUserMiddleware`` subclass with a custom
+    HTTP header. You must be sure that your front-end web server always sets or
+    strips that header based on the appropriate authentication checks, never
+    permitting an end-user to submit a fake (or "spoofed") header value. Since
+    the HTTP headers ``X-Auth-User`` and ``X-Auth_User`` (for example) both
+    normalize to the ``HTTP_X_AUTH_USER`` key in ``request.META``, you must
+    also check that your web server doesn't allow a spoofed header using
+    underscores in place of dashes.
+
+    This warning doesn't apply to ``RemoteUserMiddleware`` in its default
+    configuration with ``header = 'REMOTE_USER'``, since a key that doesn't
+    start with ``HTTP_`` in ``request.META`` can only be set by your WSGI
+    server, not directly from an HTTP request header.
 
 ``RemoteUserBackend``
 =====================
diff --git a/tests/servers/test_basehttp.py b/tests/servers/test_basehttp.py
new file mode 100644
index 0000000..6bca608
--- /dev/null
+++ b/tests/servers/test_basehttp.py
@@ -0,0 +1,67 @@
+import sys
+
+from django.core.servers.basehttp import WSGIRequestHandler
+from django.test import TestCase
+from django.utils.six import BytesIO, StringIO
+
+
+class Stub(object):
+    def __init__(self, **kwargs):
+        self.__dict__.update(kwargs)
+
+
+class WSGIRequestHandlerTestCase(TestCase):
+
+    def test_strips_underscore_headers(self):
+        """WSGIRequestHandler ignores headers containing underscores.
+
+        This follows the lead of nginx and Apache 2.4, and is to avoid
+        ambiguity between dashes and underscores in mapping to WSGI environ,
+        which can have security implications.
+        """
+        def test_app(environ, start_response):
+            """A WSGI app that just reflects its HTTP environ."""
+            start_response('200 OK', [])
+            http_environ_items = sorted(
+                '%s:%s' % (k, v) for k, v in environ.items()
+                if k.startswith('HTTP_')
+            )
+            yield (','.join(http_environ_items)).encode('utf-8')
+
+        rfile = BytesIO()
+        rfile.write(b"GET / HTTP/1.0\r\n")
+        rfile.write(b"Some-Header: good\r\n")
+        rfile.write(b"Some_Header: bad\r\n")
+        rfile.write(b"Other_Header: bad\r\n")
+        rfile.seek(0)
+
+        # WSGIRequestHandler closes the output file; we need to make this a
+        # no-op so we can still read its contents.
+        class UnclosableBytesIO(BytesIO):
+            def close(self):
+                pass
+
+        wfile = UnclosableBytesIO()
+
+        def makefile(mode, *a, **kw):
+            if mode == 'rb':
+                return rfile
+            elif mode == 'wb':
+                return wfile
+
+        request = Stub(makefile=makefile)
+        server = Stub(base_environ={}, get_app=lambda: test_app)
+
+        # We don't need to check stderr, but we don't want it in test output
+        old_stderr = sys.stderr
+        sys.stderr = StringIO()
+        try:
+            # instantiating a handler runs the request as side effect
+            WSGIRequestHandler(request, '192.168.0.2', server)
+        finally:
+            sys.stderr = old_stderr
+
+        wfile.seek(0)
+        body = list(wfile.readlines())[-1]
+
+        self.assertEqual(body, b'HTTP_SOME_HEADER:good')
-- 
1.8.1.4

++++++ 0002-1.5.x-Fixed-is_safe_url-to-handle-leading-whitespace.patch ++++++
From 4eec954e2ad330d7cd4429450f2d49b414bc72eb Mon Sep 17 00:00:00 2001
From: Tim Graham <[email protected]>
Date: Wed, 3 Dec 2014 16:14:00 -0500
Subject: [PATCH 2/4] [1.5.x] Fixed is_safe_url() to handle leading whitespace.
 (bnc#913054, CVE-2015-0220)

This is a security fix.
Mitigated possible XSS attack via user-supplied redirect URLs. `Full 
description <https://www.djangoproject.com/weblog/2015/jan/13/security/>

cherry-picked-from: 72e0b033662faa11bb7f516f18a132728aa0ae28
---
 django/utils/http.py                | 1 +
 tests/regressiontests/utils/http.py | 3 ++-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/django/utils/http.py b/django/utils/http.py
index 67912f7..f690a79 100644
--- a/django/utils/http.py
+++ b/django/utils/http.py
@@ -237,6 +237,7 @@ def is_safe_url(url, host=None):
     """
     if not url:
         return False
+    url = url.strip()
     # Chrome treats \ completely as /
     url = url.replace('\\', '/')
     # Chrome considers any URL with more than two slashes to be absolute, but
diff --git a/tests/regressiontests/utils/http.py 
b/tests/regressiontests/utils/http.py
index 87a6ba4..88bcfb8 100644
--- a/tests/regressiontests/utils/http.py
+++ b/tests/regressiontests/utils/http.py
@@ -109,7 +109,8 @@ class TestUtilsHttp(unittest.TestCase):
                         'http:/\//example.com',
                         'http:\/example.com',
                         'http:/\example.com',
-                        'javascript:alert("XSS")'):
+                        'javascript:alert("XSS")',
+                        '\njavascript:alert(x)'):
             self.assertFalse(http.is_safe_url(bad_url, host='testserver'), "%s 
should be blocked" % bad_url)
         for good_url in ('/view/?param=http://example.com',
                      '/view/?param=https://example.com',
-- 
1.8.1.4

++++++ 0003-1.5.x-Prevented-views.static.serve-from-using-large-.patch ++++++
From bf621e35f145412e509fddf73f6adaf5901f103f Mon Sep 17 00:00:00 2001
From: Tim Graham <[email protected]>
Date: Tue, 9 Dec 2014 15:32:03 -0500
Subject: [PATCH 3/4] [1.5.x] Prevented views.static.serve() from using large
 memory on large files. (bnc#913056, CVE-2015-0221)

This is a security fix.
Denial-of-service attack against ``django.views.static.serve()``.
Full description <https://www.djangoproject.com/weblog/2015/jan/13/security/>

Conflicts:
        django/views/static.py

cherry-picked-from: 553779c4055e8742cc832ed525b9ee34b174934f
---
 django/views/static.py                      |  7 ++++++-
 tests/regressiontests/views/tests/static.py | 10 +++++++++-
 tests/view_tests/media/long-line.txt        |  1 +
 3 files changed, 16 insertions(+), 2 deletions(-)
 create mode 100644 tests/view_tests/media/long-line.txt

diff --git a/django/views/static.py b/django/views/static.py
index f61ba28..eae8cf1 100644
--- a/django/views/static.py
+++ b/django/views/static.py
@@ -20,6 +20,9 @@ from django.template import loader, Template, Context, 
TemplateDoesNotExist
 from django.utils.http import http_date, parse_http_date
 from django.utils.translation import ugettext as _, ugettext_noop
 
+STREAM_CHUNK_SIZE = 4096
+
+
 def serve(request, path, document_root=None, show_indexes=False):
     """
     Serve static files below a given point in the directory structure.
@@ -63,7 +66,9 @@ def serve(request, path, document_root=None, 
show_indexes=False):
     if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'),
                               statobj.st_mtime, statobj.st_size):
         return HttpResponseNotModified()
-    response = CompatibleStreamingHttpResponse(open(fullpath, 'rb'), 
content_type=mimetype)
+    f = open(fullpath, 'rb')
+    response = CompatibleStreamingHttpResponse(iter(lambda: 
f.read(STREAM_CHUNK_SIZE), b''),
+                                               content_type=mimetype)
     response["Last-Modified"] = http_date(statobj.st_mtime)
     if stat.S_ISREG(statobj.st_mode):
         response["Content-Length"] = statobj.st_size
diff --git a/tests/regressiontests/views/tests/static.py 
b/tests/regressiontests/views/tests/static.py
index 8b8ef8b..eb2a46f 100644
--- a/tests/regressiontests/views/tests/static.py
+++ b/tests/regressiontests/views/tests/static.py
@@ -9,7 +9,7 @@ from django.http import HttpResponseNotModified
 from django.test import TestCase
 from django.test.utils import override_settings
 from django.utils.http import http_date
-from django.views.static import was_modified_since
+from django.views.static import was_modified_since, STREAM_CHUNK_SIZE
 
 from .. import urls
 from ..urls import media_dir
@@ -34,6 +34,14 @@ class StaticTests(TestCase):
             self.assertEqual(len(response_content), 
int(response['Content-Length']))
             self.assertEqual(mimetypes.guess_type(file_path)[1], 
response.get('Content-Encoding', None))
 
+    def test_chunked(self):
+        "The static view should stream files in chunks to avoid large memory 
usage"
+        response = self.client.get('/views/%s/%s' % (self.prefix, 
'long-line.txt'))
+        first_chunk = next(response.streaming_content)
+        self.assertEqual(len(first_chunk), STREAM_CHUNK_SIZE)
+        second_chunk = next(response.streaming_content)
+        self.assertEqual(len(second_chunk), 1451)
+
     def test_unknown_mime_type(self):
         response = self.client.get('/views/%s/file.unknown' % self.prefix)
         response.close()
diff --git a/tests/view_tests/media/long-line.txt 
b/tests/view_tests/media/long-line.txt
new file mode 100644
index 0000000..b4e1948
--- /dev/null
+++ b/tests/view_tests/media/long-line.txt
@@ -0,0 +1 @@
+lorem ipsum dolor sit amet consectetur adipisicing elit sed do eiusmod tempor 
incididunt ut labore et dolore magna aliqua hic tempora est veritatis culpa 
fugiat doloribus fugit in sed harum veniam porro eveniet maxime labore 
assumenda non illum possimus aut vero laudantium cum magni numquam dolorem 
explicabo quidem quasi nesciunt ipsum deleniti facilis neque similique nisi ad 
magnam accusamus quae provident dolor ab atque modi laboriosam fuga suscipit ea 
beatae ipsam consequatur saepe dolore nulla error quo iusto expedita nemo 
commodi aspernatur aliquam enim reiciendis rerum necessitatibus recusandae sint 
amet placeat temporibus autem iste deserunt esse dolores reprehenderit 
doloremque pariatur velit maiores repellat dignissimos asperiores aperiam alias 
a corporis id praesentium voluptatibus soluta voluptatem sit molestiae quas 
odio facere nostrum laborum incidunt eaque nihil ullam rem mollitia at cumque 
iure tenetur tempore totam repudiandae quisquam quod architecto officia vitae 
consectetur cupiditate molestias delectus voluptates earum et impedit quibusdam 
odit sequi perferendis eius perspiciatis eos quam quaerat officiis sunt ratione 
consequuntur quia quis obcaecati repellendus exercitationem vel minima libero 
blanditiis eligendi minus dicta voluptas excepturi nam eum inventore voluptatum 
ducimus sapiente dolorum itaque ipsa qui omnis debitis voluptate quos aliquid 
accusantium ex illo corrupti ut adipisci natus animi distinctio optio nobis 
unde similique excepturi vero culpa molestias fugit dolorum non amet iure 
inventore nihil suscipit explicabo veritatis officiis distinctio nesciunt saepe 
incidunt reprehenderit porro vitae cumque alias ut deleniti expedita ratione 
odio magnam eligendi a nostrum laborum minus esse sit libero quaerat qui id 
illo voluptates soluta neque odit dolore consectetur ducimus nulla est nisi 
impedit quia sapiente ullam temporibus ipsam repudiandae delectus fugiat 
blanditiis maxime voluptatibus aspernatur ea ipsum quisquam sunt eius ipsa 
accusantium enim corporis earum sed sequi dicta accusamus dignissimos illum 
pariatur quos aut reiciendis obcaecati perspiciatis consequuntur nam modi 
praesentium cum repellat possimus iste atque quidem architecto recusandae harum 
eaque sint quae optio voluptate quod quasi beatae magni necessitatibus facilis 
aperiam repellendus nemo aliquam et quibusdam debitis itaque cupiditate 
laboriosam unde tempora commodi laudantium in placeat ad vel maiores aliquid 
hic tempore provident quas officia adipisci rem corrupti iusto natus eum rerum 
at ex quam eveniet totam dolor assumenda error eos doloribus labore fuga facere 
deserunt ab dolores consequatur veniam animi exercitationem asperiores mollitia 
minima numquam voluptatem voluptatum nobis molestiae voluptas omnis velit quis 
quo tenetur perferendis autem dolorem doloremque sequi vitae laudantium magnam 
quae adipisci expedita doloribus minus perferendis vero animi at quos iure 
facere nihil veritatis consectetur similique porro tenetur nobis fugiat quo 
ducimus qui soluta maxime placeat error sunt ullam quaerat provident eos minima 
ab harum ratione inventore unde sint dolorum deserunt veniam laborum quasi 
suscipit facilis eveniet voluptatibus est ipsum sapiente omnis vel repellat 
perspiciatis illo voluptate aliquid magni alias modi odit ea a voluptatem 
reiciendis recusandae mollitia eius distinctio amet atque voluptates obcaecati 
deleniti eligendi commodi debitis dolore laboriosam nam illum pariatur earum 
exercitationem velit in quas explicabo fugit asperiores itaque quam sit dolorem 
beatae quod cumque necessitatibus tempora dolores hic aperiam ex tempore ut 
neque maiores ad dicta voluptatum eum officia assumenda reprehenderit nisi cum 
molestiae et iusto quidem consequuntur repellendus saepe corrupti numquam culpa 
rerum incidunt dolor impedit iste sed non praesentium ipsam consequatur eaque 
possimus quia quibusdam excepturi aspernatur voluptas quisquam autem molestias 
aliquam corporis delectus nostrum labore nesciunt blanditiis quis enim 
accusamus nulla architecto fuga natus ipsa repudiandae cupiditate temporibus 
aut libero optio id officiis esse dignissimos odio totam doloremque accusantium 
nemo rem repudiandae aliquam accusamus autem minima reiciendis debitis quis ut 
ducimus quas dolore ratione neque velit repellat natus est error ea nam 
consequuntur rerum excepturi aspernatur quaerat cumque voluptatibus rem quasi 
eos unde architecto animi sunt veritatis delectus nulla at iusto repellendus 
dolorum obcaecati commodi earum assumenda quisquam cum officiis modi ab tempora 
harum vitae voluptatem explicabo alias maxime nostrum iure consectetur incidunt 
laudantium distinctio deleniti iste facere fugit libero illo nobis expedita 
perferendis labore similique beatae sint dicta dignissimos sapiente dolor 
soluta perspiciatis aut ad illum facilis totam necessitatibus eveniet 
temporibus reprehenderit quidem fugiat magni dolorem doloribus quibusdam 
eligendi fuga quae recusandae eum amet dolores asperiores voluptas inventore 
officia sit vel id vero nihil optio nisi magnam deserunt odit corrupti adipisci 
aliquid odio enim pariatur cupiditate suscipit voluptatum corporis porro 
mollitia eaque quia non quod consequatur ipsa nesciunt itaque exercitationem 
molestias molestiae atque in numquam quo ipsam nemo ex tempore ipsum saepe esse 
sed veniam a voluptates placeat accusantium quos laboriosam voluptate provident 
hic sequi quam doloremque eius impedit omnis possimus laborum tenetur 
praesentium et minus ullam blanditiis culpa qui aperiam maiores quidem numquam 
nulla
-- 
1.8.1.4

++++++ 0004-1.5.x-Fixed-DoS-possibility-in-ModelMultipleChoiceFi.patch ++++++
From 8d6dfa690412465cf41b2524d84d727e121a8576 Mon Sep 17 00:00:00 2001
From: Tim Graham <[email protected]>
Date: Thu, 11 Dec 2014 08:31:03 -0500
Subject: [PATCH 4/4] [1.5.x] Fixed DoS possibility in
 ModelMultipleChoiceField. (bnc#913055, CVE-2015-0222)

This is a security fix.
Database denial-of-service with ``ModelMultipleChoiceField``.
Full description <https://www.djangoproject.com/weblog/2015/jan/13/security/>

Thanks Keryn Knight for the report and initial patch.

cherry-picked-from: d7a06ee7e571b6dad07c0f5b519b1db02e2a476c
by aplanas

Conflicts:
        django/forms/models.py
---
 django/forms/models.py                | 25 ++++++++++++++++++++++---
 tests/modeltests/model_forms/tests.py | 21 +++++++++++++++++++++
 2 files changed, 43 insertions(+), 3 deletions(-)

diff --git a/django/forms/models.py b/django/forms/models.py
index 1e7888a..f0a6dfc 100644
--- a/django/forms/models.py
+++ b/django/forms/models.py
@@ -1039,7 +1039,29 @@ class ModelMultipleChoiceField(ModelChoiceField):
             return self.queryset.none()
         if not isinstance(value, (list, tuple)):
             raise ValidationError(self.error_messages['list'])
+        qs = self._check_values(value)
+        # Since this overrides the inherited ModelChoiceField.clean
+        # we run custom validators here
+        self.run_validators(value)
+        return qs
+
+    def _check_values(self, value):
+        """
+        Given a list of possible PK values, returns a QuerySet of the
+        corresponding objects. Raises a ValidationError if a given value is
+        invalid (not a valid PK, not in the queryset, etc.)
+        """
         key = self.to_field_name or 'pk'
+        # deduplicate given values to avoid creating many querysets or
+        # requiring the database backend deduplicate efficiently.
+        try:
+            value = frozenset(value)
+        except TypeError:
+            # list of lists isn't hashable, for example
+            raise ValidationError(
+                self.error_messages['list'],
+                code='list',
+            )
         for pk in value:
             try:
                 self.queryset.filter(**{key: pk})
@@ -1050,9 +1072,6 @@ class ModelMultipleChoiceField(ModelChoiceField):
         for val in value:
             if force_text(val) not in pks:
                 raise ValidationError(self.error_messages['invalid_choice'] % 
val)
-        # Since this overrides the inherited ModelChoiceField.clean
-        # we run custom validators here
-        self.run_validators(value)
         return qs
 
     def prepare_value(self, value):
diff --git a/tests/modeltests/model_forms/tests.py 
b/tests/modeltests/model_forms/tests.py
index d8f2f76..424f971 100644
--- a/tests/modeltests/model_forms/tests.py
+++ b/tests/modeltests/model_forms/tests.py
@@ -1150,6 +1150,27 @@ class OldFormForXTests(TestCase):
 </select></p>
 <p><label for="id_age">Age:</label> <input type="text" name="age" value="65" 
id="id_age" /></p>''' % (w_woodward.pk, w_bernstein.pk, bw.pk, w_royko.pk))
 
+    def test_show_hidden_initial_changed_queries_efficiently(self):
+        class WriterForm(forms.Form):
+            persons = forms.ModelMultipleChoiceField(
+                show_hidden_initial=True, queryset=Writer.objects.all())
+
+        writers = (Writer.objects.create(name=str(x)) for x in range(0, 50))
+        writer_pks = tuple(x.pk for x in writers)
+        form = WriterForm(data={'initial-persons': writer_pks})
+        with self.assertNumQueries(1):
+            self.assertTrue(form.has_changed())
+
+    def test_clean_does_deduplicate_values(self):
+        class WriterForm(forms.Form):
+            persons = 
forms.ModelMultipleChoiceField(queryset=Writer.objects.all())
+
+        person1 = Writer.objects.create(name="Person 1")
+        form = WriterForm(data={})
+        queryset = form.fields['persons'].clean([str(person1.pk)] * 50)
+        sql, params = queryset.query.sql_with_params()
+        self.assertEqual(len(params), 1)
+
     def test_file_field(self):
         # Test conditions when files is either not given or empty.
 
-- 
1.8.1.4

++++++ 0005-1.6.x-Method-check_for_test_cookie-is-deprecated.patch ++++++
Index: Django-1.5.12/django/contrib/auth/forms.py
===================================================================
--- Django-1.5.12.orig/django/contrib/auth/forms.py
+++ Django-1.5.12/django/contrib/auth/forms.py
@@ -1,5 +1,7 @@
 from __future__ import unicode_literals

+import warnings
+
 from django import forms
 from django.forms.util import flatatt
 from django.template import loader
@@ -150,8 +152,6 @@ class AuthenticationForm(forms.Form):
     error_messages = {
         'invalid_login': _("Please enter a correct %(username)s and password. "
                            "Note that both fields may be case-sensitive."),
-        'no_cookies': _("Your Web browser doesn't appear to have cookies "
-                        "enabled. Cookies are required for logging in."),
         'inactive': _("This account is inactive."),
     }

@@ -186,12 +186,11 @@ class AuthenticationForm(forms.Form):
                     })
             elif not self.user_cache.is_active:
                 raise forms.ValidationError(self.error_messages['inactive'])
-        self.check_for_test_cookie()
         return self.cleaned_data

     def check_for_test_cookie(self):
-        if self.request and not self.request.session.test_cookie_worked():
-            raise forms.ValidationError(self.error_messages['no_cookies'])
+        warnings.warn("check_for_test_cookie is deprecated; ensure your login "
+                "view is CSRF-protected.", DeprecationWarning)

     def get_user_id(self):
         if self.user_cache:
++++++ 0006-1.5.x-Made-is_safe_url-reject-URLs-that-start-with-c.patch ++++++
From b35f28462f7ae376714fad5187bae894aafb606a Mon Sep 17 00:00:00 2001
From: Tim Graham <[email protected]>
Date: Mon, 9 Mar 2015 20:05:13 -0400
Subject: [PATCH 6/6] [1.5.x] Made is_safe_url() reject URLs that start with
 control characters. (bnc#923176)

https://bugzilla.suse.com/show_bug.cgi?id=923176 CVE-2015-2317
This is a security fix; disclosure to follow shortly.

Mitigated possible XSS attack via user-supplied redirect URLs

Django relies on user input in some cases (e.g. django.contrib.auth.views.login 
and i18n) to redirect the user to an "on success" URL. The security checks for 
these redirects (namely django.utils.http.is_safe_url()) accepted URLs with 
leading control characters and so considered URLs like \x08javascript:... safe. 
This issue doesn't affect Django currently, since we only put this URL into the 
Location response header and browsers seem to ignore JavaScript there. Browsers 
we tested also treat URLs prefixed with control characters such as 
%08//example.com as relative paths so redirection to an unsafe target isn't a 
problem either.

However, if a developer relies on is_safe_url() to provide safe redirect 
targets and puts such a URL into a link, they could suffer from an XSS attack 
as some browsers such as Google Chrome ignore control characters at the start 
of a URL in an anchor href.

Thanks Daniel Chatfield for reporting the issue.

Conflicts:
        django/utils/http.py
        docs/releases/1.4.20.txt
        docs/releases/1.6.11.txt
---
 django/utils/http.py                | 14 ++++++++++----
 tests/regressiontests/utils/http.py |  4 +++-
 2 files changed, 13 insertions(+), 5 deletions(-)

diff --git a/django/utils/http.py b/django/utils/http.py
index f690a79..c0ce299 100644
--- a/django/utils/http.py
+++ b/django/utils/http.py
@@ -4,6 +4,7 @@ import calendar
 import datetime
 import re
 import sys
+import unicodedata
 try:
     from urllib import parse as urllib_parse
 except ImportError:     # Python 2
@@ -11,7 +12,6 @@ except ImportError:     # Python 2
     import urlparse
     urllib_parse.urlparse = urlparse.urlparse
 
-
 from email.utils import formatdate
 
 from django.utils.datastructures import MultiValueDict
@@ -235,9 +235,10 @@ def is_safe_url(url, host=None):
 
     Always returns ``False`` on an empty url.
     """
+    if url is not None:
+        url = url.strip()
     if not url:
         return False
-    url = url.strip()
     # Chrome treats \ completely as /
     url = url.replace('\\', '/')
     # Chrome considers any URL with more than two slashes to be absolute, but
@@ -251,5 +252,10 @@ def is_safe_url(url, host=None):
     # allow this syntax.
     if not url_info.netloc and url_info.scheme:
         return False
-    return (not url_info.netloc or url_info.netloc == host) and \
-        (not url_info.scheme or url_info.scheme in ['http', 'https'])
+    # Forbid URLs that start with control characters. Some browsers (like
+    # Chrome) ignore quite a few control characters at the start of a
+    # URL and might consider the URL as scheme relative.
+    if unicodedata.category(url[0])[0] == 'C':
+        return False
+    return ((not url_info.netloc or url_info.netloc == host) and
+            (not url_info.scheme or url_info.scheme in ['http', 'https']))
diff --git a/tests/regressiontests/utils/http.py 
b/tests/regressiontests/utils/http.py
index 88bcfb8..4b62c6c 100644
--- a/tests/regressiontests/utils/http.py
+++ b/tests/regressiontests/utils/http.py
@@ -110,7 +110,9 @@ class TestUtilsHttp(unittest.TestCase):
                         'http:\/example.com',
                         'http:/\example.com',
                         'javascript:alert("XSS")',
-                        '\njavascript:alert(x)'):
+                        '\njavascript:alert(x)',
+                        '\x08//example.com',
+                        '\n'):
             self.assertFalse(http.is_safe_url(bad_url, host='testserver'), "%s 
should be blocked" % bad_url)
         for good_url in ('/view/?param=http://example.com',
                      '/view/?param=https://example.com',
-- 
2.1.4

++++++ 0007-1.6.x-Fixed-DoS-possiblity-in-contrib.auth.views.log.patch ++++++
From 128178b61c7f5d7efd861b93a460a259558e0132 Mon Sep 17 00:00:00 2001
From: Tim Graham <[email protected]>
Date: Wed, 5 Aug 2015 17:44:48 -0400
Subject: [PATCH 7/7] [1.6.x] Fixed DoS possiblity in
 contrib.auth.views.logout()

Refs #20936 -- When logging out/ending a session, don't create a new, empty 
session.

Previously, when logging out, the existing session was overwritten by a
new sessionid instead of deleting the session altogether.

This behavior added overhead by creating a new session record in
whichever backend was in use: db, cache, etc.

This extra session is unnecessary at the time since no session data is
meant to be preserved when explicitly logging out.

Backport of 393c0e24223c701edeb8ce7dc9d0f852f0c081ad,
088579638b160f3716dc81d194be70c72743593f, and
2dee853ed4def42b7ef1b3b472b395055543cc00 from master

Thanks Florian Apolloner and Carl Meyer for review.

This is a security fix.
---
 django/contrib/sessions/backends/base.py      |  9 +++-
 django/contrib/sessions/backends/cached_db.py |  2 +-
 django/contrib/sessions/middleware.py         | 50 +++++++++++--------
 django/contrib/sessions/tests.py              | 70 +++++++++++++++++++++++++++
 docs/topics/http/sessions.txt                 | 14 ++++--
 5 files changed, 118 insertions(+), 27 deletions(-)

diff --git a/django/contrib/sessions/backends/base.py 
b/django/contrib/sessions/backends/base.py
index 44b06d6..e99f11c 100644
--- a/django/contrib/sessions/backends/base.py
+++ b/django/contrib/sessions/backends/base.py
@@ -134,6 +134,13 @@ class SessionBase(object):
         self.accessed = True
         self.modified = True
 
+    def is_empty(self):
+        "Returns True when there is no session_key and the session is empty"
+        try:
+            return not bool(self._session_key) and not self._session_cache
+        except AttributeError:
+            return True
+
     def _get_new_session_key(self):
         "Returns session key that isn't being used."
         while True:
@@ -260,7 +267,7 @@ class SessionBase(object):
         """
         self.clear()
         self.delete()
-        self.create()
+        self._session_key = None
 
     def cycle_key(self):
         """
diff --git a/django/contrib/sessions/backends/cached_db.py 
b/django/contrib/sessions/backends/cached_db.py
index 31c6fbf..91485ae 100644
--- a/django/contrib/sessions/backends/cached_db.py
+++ b/django/contrib/sessions/backends/cached_db.py
@@ -70,7 +70,7 @@ class SessionStore(DBStore):
         """
         self.clear()
         self.delete(self.session_key)
-        self.create()
+        self._session_key = None
 
 
 # At bottom to avoid circular import
diff --git a/django/contrib/sessions/middleware.py 
b/django/contrib/sessions/middleware.py
index 9f65255..94a82df 100644
--- a/django/contrib/sessions/middleware.py
+++ b/django/contrib/sessions/middleware.py
@@ -14,32 +14,40 @@ class SessionMiddleware(object):
     def process_response(self, request, response):
         """
         If request.session was modified, or if the configuration is to save the
-        session every time, save the changes and set a session cookie.
+        session every time, save the changes and set a session cookie or delete
+        the session cookie if the session has been emptied.
         """
         try:
             accessed = request.session.accessed
             modified = request.session.modified
+            empty = request.session.is_empty()
         except AttributeError:
             pass
         else:
-            if accessed:
-                patch_vary_headers(response, ('Cookie',))
-            if modified or settings.SESSION_SAVE_EVERY_REQUEST:
-                if request.session.get_expire_at_browser_close():
-                    max_age = None
-                    expires = None
-                else:
-                    max_age = request.session.get_expiry_age()
-                    expires_time = time.time() + max_age
-                    expires = cookie_date(expires_time)
-                # Save the session data and refresh the client cookie.
-                # Skip session save for 500 responses, refs #3881.
-                if response.status_code != 500:
-                    request.session.save()
-                    response.set_cookie(settings.SESSION_COOKIE_NAME,
-                            request.session.session_key, max_age=max_age,
-                            expires=expires, 
domain=settings.SESSION_COOKIE_DOMAIN,
-                            path=settings.SESSION_COOKIE_PATH,
-                            secure=settings.SESSION_COOKIE_SECURE or None,
-                            httponly=settings.SESSION_COOKIE_HTTPONLY or None)
+            # First check if we need to delete this cookie.
+            # The session should be deleted only if the session is entirely 
empty
+            if settings.SESSION_COOKIE_NAME in request.COOKIES and empty:
+                response.delete_cookie(settings.SESSION_COOKIE_NAME,
+                    domain=settings.SESSION_COOKIE_DOMAIN)
+            else:
+                if accessed:
+                    patch_vary_headers(response, ('Cookie',))
+                if (modified or settings.SESSION_SAVE_EVERY_REQUEST) and not 
empty:
+                    if request.session.get_expire_at_browser_close():
+                        max_age = None
+                        expires = None
+                    else:
+                        max_age = request.session.get_expiry_age()
+                        expires_time = time.time() + max_age
+                        expires = cookie_date(expires_time)
+                    # Save the session data and refresh the client cookie.
+                    # Skip session save for 500 responses, refs #3881.
+                    if response.status_code != 500:
+                        request.session.save()
+                        response.set_cookie(settings.SESSION_COOKIE_NAME,
+                                request.session.session_key, max_age=max_age,
+                                expires=expires, 
domain=settings.SESSION_COOKIE_DOMAIN,
+                                path=settings.SESSION_COOKIE_PATH,
+                                secure=settings.SESSION_COOKIE_SECURE or None,
+                                httponly=settings.SESSION_COOKIE_HTTPONLY or 
None)
         return response
diff --git a/django/contrib/sessions/tests.py b/django/contrib/sessions/tests.py
index 829eb82..1dc92cb 100644
--- a/django/contrib/sessions/tests.py
+++ b/django/contrib/sessions/tests.py
@@ -154,6 +154,7 @@ class SessionTestsMixin(object):
         self.session.flush()
         self.assertFalse(self.session.exists(prev_key))
         self.assertNotEqual(self.session.session_key, prev_key)
+        self.assertIsNone(self.session.session_key)
         self.assertTrue(self.session.modified)
         self.assertTrue(self.session.accessed)
 
@@ -547,6 +548,75 @@ class SessionMiddlewareTests(unittest.TestCase):
         # Check that the value wasn't saved above.
         self.assertNotIn('hello', request.session.load())
 
+    def test_session_delete_on_end(self):
+        request = RequestFactory().get('/')
+        response = HttpResponse('Session test')
+        middleware = SessionMiddleware()
+
+        # Before deleting, there has to be an existing cookie
+        request.COOKIES[settings.SESSION_COOKIE_NAME] = 'abc'
+
+        # Simulate a request that ends the session
+        middleware.process_request(request)
+        request.session.flush()
+
+        # Handle the response through the middleware
+        response = middleware.process_response(request, response)
+
+        # Check that the cookie was deleted, not recreated.
+        # A deleted cookie header looks like:
+        #  Set-Cookie: sessionid=; expires=Thu, 01-Jan-1970 00:00:00 GMT; 
Max-Age=0; Path=/
+        self.assertEqual(
+            'Set-Cookie: {0}=; expires=Thu, 01-Jan-1970 00:00:00 GMT; '
+            'Max-Age=0; Path=/'.format(settings.SESSION_COOKIE_NAME),
+            str(response.cookies[settings.SESSION_COOKIE_NAME])
+        )
+
+    @override_settings(SESSION_COOKIE_DOMAIN='.example.local')
+    def test_session_delete_on_end_with_custom_domain(self):
+        request = RequestFactory().get('/')
+        response = HttpResponse('Session test')
+        middleware = SessionMiddleware()
+
+        # Before deleting, there has to be an existing cookie
+        request.COOKIES[settings.SESSION_COOKIE_NAME] = 'abc'
+
+        # Simulate a request that ends the session
+        middleware.process_request(request)
+        request.session.flush()
+
+        # Handle the response through the middleware
+        response = middleware.process_response(request, response)
+
+        # Check that the cookie was deleted, not recreated.
+        # A deleted cookie header with a custom domain looks like:
+        #  Set-Cookie: sessionid=; Domain=.example.local;
+        #              expires=Thu, 01-Jan-1970 00:00:00 GMT; Max-Age=0; Path=/
+        self.assertEqual(
+            'Set-Cookie: {}=; Domain=.example.local; expires=Thu, '
+            '01-Jan-1970 00:00:00 GMT; Max-Age=0; Path=/'.format(
+                settings.SESSION_COOKIE_NAME,
+            ),
+            str(response.cookies[settings.SESSION_COOKIE_NAME])
+        )
+
+    def test_flush_empty_without_session_cookie_doesnt_set_cookie(self):
+        request = RequestFactory().get('/')
+        response = HttpResponse('Session test')
+        middleware = SessionMiddleware()
+
+        # Simulate a request that ends the session
+        middleware.process_request(request)
+        request.session.flush()
+
+        # Handle the response through the middleware
+        response = middleware.process_response(request, response)
+
+        # A cookie should not be set.
+        self.assertEqual(response.cookies, {})
+        # The session is accessed so "Vary: Cookie" should be set.
+        self.assertEqual(response['Vary'], 'Cookie')
+
 
 class CookieSessionTests(SessionTestsMixin, TestCase):
 
diff --git a/docs/topics/http/sessions.txt b/docs/topics/http/sessions.txt
index 71f6247..a1cf8d3 100644
--- a/docs/topics/http/sessions.txt
+++ b/docs/topics/http/sessions.txt
@@ -222,12 +222,18 @@ You can edit it multiple times.
 
     .. method:: flush()
 
-      Delete the current session data from the session and regenerate the
-      session key value that is sent back to the user in the cookie. This is
-      used if you want to ensure that the previous session data can't be
-      accessed again from the user's browser (for example, the
+      Deletes the current session data from the session and deletes the session
+      cookie. This is used if you want to ensure that the previous session data
+      can't be accessed again from the user's browser (for example, the
       :func:`django.contrib.auth.logout()` function calls it).
 
+      .. versionchanged:: 1.7.10
+
+          Deletion of the session cookie was added. Previously, the behavior
+          was to regenerate the session key value that was sent back to the
+          user in the cookie, but this could be a denial-of-service
+          vulnerability.
+
     .. method:: set_test_cookie()
 
       Sets a test cookie to determine whether the user's browser supports
-- 
2.1.4

++++++ 0008-1.5.x-Fixed-19324-Avoided-creating-a-session-record-.patch ++++++
From 49b743094cfe7dd6925e1a8e57e8cf5e9918b3ec Mon Sep 17 00:00:00 2001
From: Carl Meyer <[email protected]>
Date: Wed, 10 Jun 2015 15:45:20 -0600
Subject: [PATCH 8/9] [1.5.x] Fixed #19324 -- Avoided creating a session record
 when loading the session. (bnc#937522, CVE-2015-5143)

The session record is now only created if/when the session is modified. This
prevents a potential DoS via creation of many empty session records.

Denial-of-service possibility by filling session store

In previous versions of Django, the session backends created a new empty record 
in the session storage anytime request.session was accessed and there was a 
session key provided in the request cookies that didn't already have a session 
record. This could allow an attacker to easily create many new session records 
simply by sending repeated requests with unknown session keys, potentially 
filling up the session store or causing other users' session records to be 
evicted.

The built-in session backends now create a session record only if the session 
is actually modified; empty session records are not created. Thus this 
potential DoS is now only possible if the site chooses to expose a 
session-modifying view to anonymous users.

As each built-in session backend was fixed separately (rather than a fix in the 
core sessions framework), maintainers of third-party session backends should 
check whether the same vulnerability is present in their backend and correct it 
if so.

Thanks Eric Peterson and Lin Hua Cheng for reporting the issue.

This is a security fix

(cherry picked from commit 1828f4341ec53a8684112d24031b767eba557663)
---
 django/contrib/sessions/backends/cache.py     |  6 ++++--
 django/contrib/sessions/backends/cached_db.py |  6 +++---
 django/contrib/sessions/backends/db.py        |  5 +++--
 django/contrib/sessions/backends/file.py      |  5 +++--
 django/contrib/sessions/tests.py              | 20 ++++++++++++++++++++
 5 files changed, 33 insertions(+), 9 deletions(-)

diff --git a/django/contrib/sessions/backends/cache.py 
b/django/contrib/sessions/backends/cache.py
index 26ae6c4..09eae30 100644
--- a/django/contrib/sessions/backends/cache.py
+++ b/django/contrib/sessions/backends/cache.py
@@ -27,7 +27,7 @@ class SessionStore(SessionBase):
             session_data = None
         if session_data is not None:
             return session_data
-        self.create()
+        self._session_key = None
         return {}
 
     def create(self):
@@ -47,6 +47,8 @@ class SessionStore(SessionBase):
         raise RuntimeError("Unable to create a new session key.")
 
     def save(self, must_create=False):
+        if self.session_key is None:
+            return self.create()
         if must_create:
             func = self._cache.add
         else:
@@ -58,7 +60,7 @@ class SessionStore(SessionBase):
             raise CreateError
 
     def exists(self, session_key):
-        return (KEY_PREFIX + session_key) in self._cache
+        return session_key and (KEY_PREFIX + session_key) in self._cache
 
     def delete(self, session_key=None):
         if session_key is None:
diff --git a/django/contrib/sessions/backends/cached_db.py 
b/django/contrib/sessions/backends/cached_db.py
index 91485ae..4b98a01 100644
--- a/django/contrib/sessions/backends/cached_db.py
+++ b/django/contrib/sessions/backends/cached_db.py
@@ -40,14 +40,14 @@ class SessionStore(DBStore):
                 )
                 data = self.decode(s.session_data)
                 cache.set(self.cache_key, data,
-                    self.get_expiry_age(expiry=s.expire_date))
+                          self.get_expiry_age(expiry=s.expire_date))
             except (Session.DoesNotExist, SuspiciousOperation):
-                self.create()
+                self._session_key = None
                 data = {}
         return data
 
     def exists(self, session_key):
-        if (KEY_PREFIX + session_key) in cache:
+        if session_key and (KEY_PREFIX + session_key) in cache:
             return True
         return super(SessionStore, self).exists(session_key)
 
diff --git a/django/contrib/sessions/backends/db.py 
b/django/contrib/sessions/backends/db.py
index 47e89b6..fb89c31 100644
--- a/django/contrib/sessions/backends/db.py
+++ b/django/contrib/sessions/backends/db.py
@@ -19,7 +19,7 @@ class SessionStore(SessionBase):
             )
             return self.decode(s.session_data)
         except (Session.DoesNotExist, SuspiciousOperation):
-            self.create()
+            self._session_key = None
             return {}
 
     def exists(self, session_key):
@@ -36,7 +36,6 @@ class SessionStore(SessionBase):
                 # Key wasn't unique. Try again.
                 continue
             self.modified = True
-            self._session_cache = {}
             return
 
     def save(self, must_create=False):
@@ -46,6 +45,8 @@ class SessionStore(SessionBase):
         create a *new* entry (as opposed to possibly updating an existing
         entry).
         """
+        if self.session_key is None:
+            return self.create()
         obj = Session(
             session_key=self._get_or_create_session_key(),
             session_data=self.encode(self._get_session(no_load=must_create)),
diff --git a/django/contrib/sessions/backends/file.py 
b/django/contrib/sessions/backends/file.py
index 7d933c6..83a0456 100644
--- a/django/contrib/sessions/backends/file.py
+++ b/django/contrib/sessions/backends/file.py
@@ -86,7 +86,7 @@ class SessionStore(SessionBase):
                     self.delete()
                     self.create()
         except IOError:
-            self.create()
+            self._session_key = None
         return session_data
 
     def create(self):
@@ -97,10 +97,11 @@ class SessionStore(SessionBase):
             except CreateError:
                 continue
             self.modified = True
-            self._session_cache = {}
             return
 
     def save(self, must_create=False):
+        if self.session_key is None:
+            return self.create()
         # Get the session data now, before we start messing
         # with the file it is stored within.
         session_data = self._get_session(no_load=must_create)
diff --git a/django/contrib/sessions/tests.py b/django/contrib/sessions/tests.py
index 1dc92cb..230777b 100644
--- a/django/contrib/sessions/tests.py
+++ b/django/contrib/sessions/tests.py
@@ -167,6 +167,11 @@ class SessionTestsMixin(object):
         self.assertNotEqual(self.session.session_key, prev_key)
         self.assertEqual(list(self.session.items()), prev_data)
 
+    def test_save_doesnt_clear_data(self):
+        self.session['a'] = 'b'
+        self.session.save()
+        self.assertEqual(self.session['a'], 'b')
+
     def test_invalid_key(self):
         # Submitting an invalid session key (either by guessing, or if the db 
has
         # removed the key) results in a new key being generated.
@@ -294,6 +299,21 @@ class SessionTestsMixin(object):
                 self.session.delete(old_session_key)
                 self.session.delete(new_session_key)
 
+    def test_session_load_does_not_create_record(self):
+        """
+        Loading an unknown session key does not create a session record.
+
+        Creating session records on load is a DOS vulnerability.
+        """
+        if self.backend is CookieSession:
+            raise unittest.SkipTest("Cookie backend doesn't have an external 
store to create records in.")
+        session = self.backend('someunknownkey')
+        session.load()
+
+        self.assertFalse(session.exists(session.session_key))
+        # provided unknown key was cycled, not reused
+        self.assertNotEqual(session.session_key, 'someunknownkey')
+
 
 class DatabaseSessionTests(SessionTestsMixin, TestCase):
 
-- 
2.1.4

++++++ 0009-1.5.x-Prevented-newlines-from-being-accepted-in-some.patch ++++++
From 38960f1d924c3dcc3b2d38ea591cc7019568fc96 Mon Sep 17 00:00:00 2001
From: Tim Graham <[email protected]>
Date: Fri, 12 Jun 2015 13:49:31 -0400
Subject: [PATCH 9/9] [1.5.x] Prevented newlines from being accepted in some
 validators. (bnc#937523, CVE-2015-5144)

Header injection possibility since validators accept newlines in input

Some of Django's built-in validators (django.core.validators.EmailValidator, 
most seriously) didn't prohibit newline characters (due to the usage of $ 
instead of \Z in the regular expressions). If you use values with newlines in 
HTTP response or email headers, you can suffer from header injection attacks. 
Django itself isn't vulnerable because django.http.HttpResponse and the mail 
sending utilities in django.core.mail prohibit newlines in HTTP and SMTP 
headers, respectively. While the validators have been fixed in Django, if 
you're creating HTTP responses or email messages in other ways, it's a good 
idea to ensure that those methods prohibit newlines as well. You might also 
want to validate that any existing data in your application doesn't contain 
unexpected newlines.

django.core.validators.validate_ipv4_address(), 
django.core.validators.validate_slug(), and django.core.validators.URLValidator 
are also affected, however, as of Django 1.6 the GenericIPAddresseField, 
IPAddressField, SlugField, and URLField form fields which use these validators 
all strip the input, so the possibility of newlines entering your data only 
exists if you are using these validators outside of the form fields.

The undocumented, internally unused validate_integer() function is now stricter 
as it validates using a regular expression instead of simply casting the value 
using int() and checking if an exception was raised.

This is a security fix

Thanks to Sjoerd Job Postmus for the report and draft patch.

Conflicts:
        django/core/validators.py
        docs/releases/1.4.21.txt
---
 django/core/validators.py            | 25 +++++++++++++++----------
 tests/modeltests/validators/tests.py | 16 +++++++++++++++-
 2 files changed, 30 insertions(+), 11 deletions(-)

diff --git a/django/core/validators.py b/django/core/validators.py
index 251b5d8..ff9dfdb 100644
--- a/django/core/validators.py
+++ b/django/core/validators.py
@@ -49,7 +49,7 @@ class URLValidator(RegexValidator):
         r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|'  # ...or ipv4
         r'\[?[A-F0-9]*:[A-F0-9:]+\]?)'  # ...or ipv6
         r'(?::\d+)?'  # optional port
-        r'(?:/?|[/?]\S+)$', re.IGNORECASE)
+        r'(?:/?|[/?]\S+)\Z', re.IGNORECASE)
 
     def __call__(self, value):
         try:
@@ -71,11 +71,16 @@ class URLValidator(RegexValidator):
             url = value
 
 
+integer_validator = RegexValidator(
+    re.compile('^-?\d+\Z'),
+    message=_('Enter a valid integer.'),
+    code='invalid',
+)
+
+
 def validate_integer(value):
-    try:
-        int(value)
-    except (ValueError, TypeError):
-        raise ValidationError('')
+    return integer_validator(value)
+
 
 
 class EmailValidator(RegexValidator):
@@ -99,14 +104,14 @@ email_re = re.compile(
     r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*"  # 
dot-atom
     # quoted-string, see also http://tools.ietf.org/html/rfc2822#section-3.2.5
     
r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"'
-    
r')@((?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)$)'
  # domain
-    
r'|\[(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\]$', 
re.IGNORECASE)  # literal form, ipv4 address (SMTP 4.1.3)
+    
r')@((?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)\Z)'
  # domain
+    
r'|\[(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\]\Z', 
re.IGNORECASE)  # literal form, ipv4 address (SMTP 4.1.3)
 validate_email = EmailValidator(email_re, _('Enter a valid email address.'), 
'invalid')
 
-slug_re = re.compile(r'^[-a-zA-Z0-9_]+$')
+slug_re = re.compile(r'^[-a-zA-Z0-9_]+\Z')
 validate_slug = RegexValidator(slug_re, _("Enter a valid 'slug' consisting of 
letters, numbers, underscores or hyphens."), 'invalid')
 
-ipv4_re = 
re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$')
+ipv4_re = 
re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\Z')
 validate_ipv4_address = RegexValidator(ipv4_re, _('Enter a valid IPv4 
address.'), 'invalid')
 
 
@@ -147,7 +152,7 @@ def ip_address_validators(protocol, unpack_ipv4):
         raise ValueError("The protocol '%s' is unknown. Supported: %s"
                          % (protocol, list(ip_address_validator_map)))
 
-comma_separated_int_list_re = re.compile('^[\d,]+$')
+comma_separated_int_list_re = re.compile('^[\d,]+\Z')
 validate_comma_separated_integer_list = 
RegexValidator(comma_separated_int_list_re, _('Enter only digits separated by 
commas.'), 'invalid')
 
 
diff --git a/tests/modeltests/validators/tests.py 
b/tests/modeltests/validators/tests.py
index 0174a60..a417515 100644
--- a/tests/modeltests/validators/tests.py
+++ b/tests/modeltests/validators/tests.py
@@ -15,13 +15,16 @@ NOW = datetime.now()
 
 TEST_DATA = (
     # (validator, value, expected),
+    # (validator, value, expected),
     (validate_integer, '42', None),
     (validate_integer, '-42', None),
     (validate_integer, -42, None),
-    (validate_integer, -42.5, None),
 
+    (validate_integer, -42.5, ValidationError),
     (validate_integer, None, ValidationError),
     (validate_integer, 'a', ValidationError),
+    (validate_integer, '\n42', ValidationError),
+    (validate_integer, '42\n', ValidationError),
 
     (validate_email, '[email protected]', None),
     (validate_email, '[email protected]', None),
@@ -46,6 +49,11 @@ TEST_DATA = (
     # Quoted-string format (CR not allowed)
     (validate_email, '"\\\011"@here.com', None),
     (validate_email, '"\\\012"@here.com', ValidationError),
+    # Trailing newlines in username or domain not allowed
+    (validate_email, '[email protected]\n', ValidationError),
+    (validate_email, 'a\[email protected]', ValidationError),
+    (validate_email, '"test@test"\[email protected]', ValidationError),
+    (validate_email, 'a@[127.0.0.1]\n', ValidationError),
 
     (validate_slug, 'slug-ok', None),
     (validate_slug, 'longer-slug-still-ok', None),
@@ -58,6 +66,7 @@ TEST_DATA = (
     (validate_slug, '[email protected]', ValidationError),
     (validate_slug, '你好', ValidationError),
     (validate_slug, '\n', ValidationError),
+    (validate_slug, 'trailing-newline\n', ValidationError),
 
     (validate_ipv4_address, '1.1.1.1', None),
     (validate_ipv4_address, '255.0.0.0', None),
@@ -67,6 +76,7 @@ TEST_DATA = (
     (validate_ipv4_address, '25.1.1.', ValidationError),
     (validate_ipv4_address, '25,1,1,1', ValidationError),
     (validate_ipv4_address, '25.1 .1.1', ValidationError),
+    (validate_ipv4_address, '1.1.1.1\n', ValidationError),
 
     # validate_ipv6_address uses django.utils.ipv6, which
     # is tested in much greater detail in it's own testcase
@@ -100,6 +110,7 @@ TEST_DATA = (
     (validate_comma_separated_integer_list, '', ValidationError),
     (validate_comma_separated_integer_list, 'a,b,c', ValidationError),
     (validate_comma_separated_integer_list, '1, 2, 3', ValidationError),
+    (validate_comma_separated_integer_list, '1,2,3\n', ValidationError),
 
     (MaxValueValidator(10), 10, None),
     (MaxValueValidator(10), -10, None),
@@ -151,6 +162,9 @@ TEST_DATA = (
     (URLValidator(), 'http://-invalid.com', ValidationError),
     (URLValidator(), 'http://inv-.alid-.com', ValidationError),
     (URLValidator(), 'http://inv-.-alid.com', ValidationError),
+    # Trailing newlines not accepted
+    (URLValidator(), 'http://www.djangoproject.com/\n', ValidationError),
+    (URLValidator(), 'http://[::ffff:192.9.5.5]\n', ValidationError),
 
     (BaseValidator(True), True, None),
     (BaseValidator(True), False, ValidationError),
-- 
2.1.4

++++++ 0010-1.5.x-Fixed-a-settings-leak-possibility-in-the-date-.patch ++++++
From 9c2fa72cb5da9a44f04dce3eeb9c5408b6d9d895 Mon Sep 17 00:00:00 2001
From: "Bernhard M. Wiedemann" <[email protected]>
Date: Wed, 18 Nov 2015 10:04:24 +0100
Subject: [PATCH 10/10] [1.5.x] Fixed a settings leak possibility in the date
 template filter.

This is a security fix.

bnc#955412
CVE-2015-8213: Settings leak possibility in ``date`` template filter
====================================================================

If an application allows users to specify an unvalidated format for
dates and passes this format to the ``date`` filter, e.g.
``{{ last_updated|date:user_date_format }}``, then a malicious user
could obtain any secret in the application's settings by specifying a
settings key instead of a date format. e.g. ``"SECRET_KEY"`` instead
of ``"j/m/Y"``.

To remedy this, the underlying function used by the ``date`` template
filter, ``django.utils.formats.get_format()``, now only allows
accessing the date/time formatting settings.
---
 django/utils/formats.py             | 21 +++++++++++++++++++++
 tests/regressiontests/i18n/tests.py |  4 ++++
 2 files changed, 25 insertions(+)

diff --git a/django/utils/formats.py b/django/utils/formats.py
index 03b9918..4c04473 100644
--- a/django/utils/formats.py
+++ b/django/utils/formats.py
@@ -27,6 +27,25 @@ ISO_INPUT_FORMATS = {
     ),
 }
 
+
+FORMAT_SETTINGS = frozenset([
+    'DECIMAL_SEPARATOR',
+    'THOUSAND_SEPARATOR',
+    'NUMBER_GROUPING',
+    'FIRST_DAY_OF_WEEK',
+    'MONTH_DAY_FORMAT',
+    'TIME_FORMAT',
+    'DATE_FORMAT',
+    'DATETIME_FORMAT',
+    'SHORT_DATE_FORMAT',
+    'SHORT_DATETIME_FORMAT',
+    'YEAR_MONTH_FORMAT',
+    'DATE_INPUT_FORMATS',
+    'TIME_INPUT_FORMATS',
+    'DATETIME_INPUT_FORMATS',
+])
+
+
 def reset_format_cache():
     """Clear any cached formats.
 
@@ -78,6 +97,8 @@ def get_format(format_type, lang=None, use_l10n=None):
     be localized (or not), overriding the value of settings.USE_L10N.
     """
     format_type = force_str(format_type)
+    if format_type not in FORMAT_SETTINGS:
+        return format_type
     if use_l10n or (use_l10n is None and settings.USE_L10N):
         if lang is None:
             lang = get_language()
diff --git a/tests/regressiontests/i18n/tests.py 
b/tests/regressiontests/i18n/tests.py
index d8af6f1..b974b08 100644
--- a/tests/regressiontests/i18n/tests.py
+++ b/tests/regressiontests/i18n/tests.py
@@ -693,6 +693,10 @@ class FormattingTests(TestCase):
                 self.assertEqual(template2.render(context), output2)
                 self.assertEqual(template3.render(context), output3)
 
+    def test_format_arbitrary_settings(self):
+        self.assertEqual(get_format('DEBUG'), 'DEBUG')
+
+
 class MiscTests(TestCase):
 
     def setUp(self):
-- 
2.6.2

++++++ Django-1.2-completion-only-for-bash.patch ++++++
Index: extras/django_bash_completion
===================================================================
--- extras/django_bash_completion.orig
+++ extras/django_bash_completion
@@ -31,6 +31,8 @@
 #
 # To uninstall, just remove the line from your .bash_profile and .bashrc.
 
+test -z "$BASH_VERSION" && return
+
 _django_completion()
 {
     COMPREPLY=( $( COMP_WORDS="${COMP_WORDS[*]}" \
++++++ Django-1.4-CSRF_COOKIE_HTTPONLY-support.patch ++++++
Index: django/conf/global_settings.py
===================================================================
--- django/conf/global_settings.py.orig
+++ django/conf/global_settings.py
@@ -535,6 +535,7 @@ CSRF_COOKIE_NAME = 'csrftoken'
 CSRF_COOKIE_DOMAIN = None
 CSRF_COOKIE_PATH = '/'
 CSRF_COOKIE_SECURE = False
+CSRF_COOKIE_HTTPONLY = False
 
 ############
 # MESSAGES #
Index: django/middleware/csrf.py
===================================================================
--- django/middleware/csrf.py.orig
+++ django/middleware/csrf.py
@@ -208,7 +208,8 @@ class CsrfViewMiddleware(object):
                             max_age = 60 * 60 * 24 * 7 * 52,
                             domain=settings.CSRF_COOKIE_DOMAIN,
                             path=settings.CSRF_COOKIE_PATH,
-                            secure=settings.CSRF_COOKIE_SECURE
+                            secure=settings.CSRF_COOKIE_SECURE,
+                            httponly=settings.CSRF_COOKIE_HTTPONLY
                             )
         # Content varies with the CSRF cookie, so set the Vary header.
         patch_vary_headers(response, ('Cookie',))
Index: docs/ref/contrib/csrf.txt
===================================================================
--- docs/ref/contrib/csrf.txt.orig
+++ docs/ref/contrib/csrf.txt
@@ -543,6 +543,17 @@ Whether to use a secure cookie for the C
 the cookie will be marked as "secure," which means browsers may ensure that the
 cookie is only sent under an HTTPS connection.
 
+CSRF_COOKIE_HTTPONLY
+------------------
+
+.. versionadded:: 1.5
+
+Default: ``False``
+
+Whether to use HttpOnly flag on the CSRF cookie. If this is set to
+``True``, client-side JavaScript will not to be able to access the
+session cookie.
+
 CSRF_FAILURE_VIEW
 -----------------
 
Index: docs/ref/settings.txt
===================================================================
--- docs/ref/settings.txt.orig
+++ docs/ref/settings.txt
@@ -362,6 +362,19 @@ Whether to use a secure cookie for the C
 the cookie will be marked as "secure," which means browsers may ensure that the
 cookie is only sent under an HTTPS connection.
 
+.. setting:: CSRF_COOKIE_HTTPONLY
+
+CSRF_COOKIE_HTTPONLY
+------------------
+
+.. versionadded:: 1.5
+
+Default: ``False``
+
+Whether to use HttpOnly flag on the CSRF cookie. If this is set to
+``True``, client-side JavaScript will not to be able to access the
+session cookie. See :setting:`SESSION_COOKIE_HTTPONLY`.
+
 .. setting:: CSRF_FAILURE_VIEW
 
 CSRF_FAILURE_VIEW
Index: tests/regressiontests/csrf_tests/tests.py
===================================================================
--- tests/regressiontests/csrf_tests/tests.py.orig
+++ tests/regressiontests/csrf_tests/tests.py
@@ -101,7 +101,8 @@ class CsrfViewMiddlewareTest(TestCase):
         with self.settings(CSRF_COOKIE_NAME='myname',
                            CSRF_COOKIE_DOMAIN='.example.com',
                            CSRF_COOKIE_PATH='/test/',
-                           CSRF_COOKIE_SECURE=True):
+                           CSRF_COOKIE_SECURE=True,
+                           CSRF_COOKIE_HTTPONLY=True):
             # token_view calls get_token() indirectly
             CsrfViewMiddleware().process_view(req, token_view, (), {})
             resp = token_view(req)
@@ -110,6 +111,7 @@ class CsrfViewMiddlewareTest(TestCase):
         self.assertNotEqual(csrf_cookie, False)
         self.assertEqual(csrf_cookie['domain'], '.example.com')
         self.assertEqual(csrf_cookie['secure'], True)
+        self.assertEqual(csrf_cookie['httponly'], True)
         self.assertEqual(csrf_cookie['path'], '/test/')
         self.assertTrue('Cookie' in resp2.get('Vary',''))
 
++++++ python-django-rpmlintrc ++++++
addFilter("file-not-in-%lang")
addFilter("zero-length")

Reply via email to