Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions django/contrib/gis/gdal/raster/source.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import json
import os
import sys
import uuid
from ctypes import (
addressof,
Expand Down Expand Up @@ -103,7 +102,7 @@ def __init__(self, ds_input, write=False):
# Create a new raster in write mode.
self._write = 1
# Get size of buffer.
size = sys.getsizeof(ds_input)
size = len(ds_input)
# Pass data to ctypes, keeping a reference to the ctypes object so
# that the vsimem file remains available until the GDALRaster is
# deleted.
Expand Down
28 changes: 14 additions & 14 deletions django/core/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,20 +99,20 @@ class DomainNameValidator(RegexValidator):
def __init__(self, **kwargs):
self.accept_idna = kwargs.pop("accept_idna", True)

if self.accept_idna:
self.regex = _lazy_re_compile(
r"^" + self.hostname_re + self.domain_re + self.tld_re + r"$",
re.IGNORECASE,
)
else:
self.regex = _lazy_re_compile(
r"^"
+ self.ascii_only_hostname_re
+ self.ascii_only_domain_re
+ self.ascii_only_tld_re
+ r"$",
re.IGNORECASE,
)
regex_parts = [
"^",
*(
(self.hostname_re, self.domain_re, self.tld_re)
if self.accept_idna
else (
self.ascii_only_hostname_re,
self.ascii_only_domain_re,
self.ascii_only_tld_re,
)
),
r"\Z",
]
self.regex = _lazy_re_compile("".join(regex_parts), re.IGNORECASE)
super().__init__(**kwargs)

def __call__(self, value):
Expand Down
8 changes: 2 additions & 6 deletions django/middleware/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,8 @@ def process_response(self, request, response):
return response

# Don't cache responses that set a user-specific (and maybe security
# sensitive) cookie in response to a cookie-less request.
if (
not request.COOKIES
and response.cookies
and has_vary_header(response, "Cookie")
):
# sensitive) cookie while varying on Cookie.
if response.cookies and has_vary_header(response, "Cookie"):
return response

# Don't cache responses when the Cache-Control header is set to
Expand Down
42 changes: 42 additions & 0 deletions docs/releases/5.2.16.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,45 @@ Django 5.2.16 release notes
*July 7, 2026*

Django 5.2.16 fixes three security issues with severity "low" in 5.2.15.

CVE-2026-48588: Potential exposure of private data via cached ``Set-Cookie`` response
=====================================================================================

:class:`~django.middleware.cache.UpdateCacheMiddleware` and
:func:`~django.views.decorators.cache.cache_page` avoided caching responses
that set a cookie while varying on ``Cookie`` only when the incoming request
contained no cookies at all. When the request already carried an unrelated
cookie (such as a language or theme preference cookie), the protection did not
apply, allowing a response that sets a session or other sensitive cookie to be
stored in Django's shared cache.

This issue has severity "low" according to the :ref:`Django security policy
<severity-levels>`.

CVE-2026-53877: Heap buffer over-read in ``GDALRaster``
=======================================================

When :class:`~django.contrib.gis.gdal.GDALRaster` was instantiated with a bytes
object representing a raster file, the
:attr:`~django.contrib.gis.gdal.GDALRaster.vsi_buffer` property could over-read
the allocated buffer by approximately 32 bytes. This could result in
information disclosure of adjacent heap memory or, in rare cases, a
segmentation fault. Only rasters stored in GDAL's virtual filesystem were
affected.

This issue has severity "low" according to the :ref:`Django security policy
<severity-levels>`.

CVE-2026-53878: Header injection possibility since ``DomainNameValidator`` accepted newlines in input
=====================================================================================================

:class:`~django.core.validators.DomainNameValidator` accepted newlines in
domain names. If such values were included in HTTP responses, header injection
attacks were possible. Django itself wasn't vulnerable because
:class:`~django.http.HttpResponse` prohibits newlines in HTTP headers.

The vulnerability only affected uses of ``DomainNameValidator`` outside Django
form fields, as ``CharField`` strips newlines by default.

This issue has severity "low" according to the :ref:`Django security policy
<severity-levels>`.
42 changes: 42 additions & 0 deletions docs/releases/6.0.7.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,48 @@ Django 6.0.7 release notes
Django 6.0.7 fixes three security issues with severity "low" and one bug in
6.0.6.

CVE-2026-48588: Potential exposure of private data via cached ``Set-Cookie`` response
=====================================================================================

:class:`~django.middleware.cache.UpdateCacheMiddleware` and
:func:`~django.views.decorators.cache.cache_page` avoided caching responses
that set a cookie while varying on ``Cookie`` only when the incoming request
contained no cookies at all. When the request already carried an unrelated
cookie (such as a language or theme preference cookie), the protection did not
apply, allowing a response that sets a session or other sensitive cookie to be
stored in Django's shared cache.

This issue has severity "low" according to the :ref:`Django security policy
<severity-levels>`.

CVE-2026-53877: Heap buffer over-read in ``GDALRaster``
=======================================================

When :class:`~django.contrib.gis.gdal.GDALRaster` was instantiated with a bytes
object representing a raster file, the
:attr:`~django.contrib.gis.gdal.GDALRaster.vsi_buffer` property could over-read
the allocated buffer by approximately 32 bytes. This could result in
information disclosure of adjacent heap memory or, in rare cases, a
segmentation fault. Only rasters stored in GDAL's virtual filesystem were
affected.

This issue has severity "low" according to the :ref:`Django security policy
<severity-levels>`.

CVE-2026-53878: Header injection possibility since ``DomainNameValidator`` accepted newlines in input
=====================================================================================================

:class:`~django.core.validators.DomainNameValidator` accepted newlines in
domain names. If such values were included in HTTP responses, header injection
attacks were possible. Django itself wasn't vulnerable because
:class:`~django.http.HttpResponse` prohibits newlines in HTTP headers.

The vulnerability only affected uses of ``DomainNameValidator`` outside Django
form fields, as ``CharField`` strips newlines by default.

This issue has severity "low" according to the :ref:`Django security policy
<severity-levels>`.

Bugfixes
========

Expand Down
12 changes: 12 additions & 0 deletions docs/releases/6.0.8.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
==========================
Django 6.0.8 release notes
==========================

*Expected August 4, 2026*

Django 6.0.8 fixes several bugs in 6.0.7.

Bugfixes
========

* ...
1 change: 1 addition & 0 deletions docs/releases/index.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ versions of the documentation contain the release notes for any later releases.
.. toctree::
:maxdepth: 1

6.0.8
6.0.7
6.0.6
6.0.5
Expand Down
34 changes: 34 additions & 0 deletions docs/releases/security.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,40 @@ Issues under Django's security process
All security issues have been handled under versions of Django's security
process. These are listed below.

July 7, 2026 - :cve:`2026-48588`
--------------------------------

Potential exposure of private data via cached ``Set-Cookie`` response.
`Full description
<https://www.djangoproject.com/weblog/2026/jul/07/security-releases/>`__

* Django 6.1 :commit:`(patch) <c2a936ab7d6048acfc341dd61e6094c2dc84782f>`
* Django 6.0 :commit:`(patch) <64f9a2b2283fde3ec69fb0dfe441cf0f6f411ba3>`
* Django 5.2 :commit:`(patch) <721685aa7799cc9327bd202cd1f70bd012ca95a7>`

July 7, 2026 - :cve:`2026-53877`
--------------------------------

Heap buffer over-read in ``GDALRaster``.
`Full description
<https://www.djangoproject.com/weblog/2026/jul/07/security-releases/>`__

* Django 6.1 :commit:`(patch) <a46b417d9e441379e7f86933ec1b2fb05f63d492>`
* Django 6.0 :commit:`(patch) <38dfbd27d7d4f4e6eaa087d7a90f2613fbf55b3a>`
* Django 5.2 :commit:`(patch) <6c66eb8cec52b303af85c2c6e4dd00aa37654dbc>`

July 7, 2026 - :cve:`2026-53878`
--------------------------------

Header injection possibility since ``DomainNameValidator`` accepted newlines in
input.
`Full description
<https://www.djangoproject.com/weblog/2026/jul/07/security-releases/>`__

* Django 6.1 :commit:`(patch) <fe3e8a0de7fad7897fa4917013d8abd507e4d756>`
* Django 6.0 :commit:`(patch) <a5de13f1491f1dbf2bb0ad9b91570524ebbc8acd>`
* Django 5.2 :commit:`(patch) <d5d60ed0323cddaa0ce0237a26a3d49ac21ee05e>`

June 3, 2026 - :cve:`2026-6873`
-------------------------------

Expand Down
38 changes: 31 additions & 7 deletions tests/cache/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3181,18 +3181,42 @@ def test_authorization_header_exception_superstring(self):

def test_sensitive_cookie_not_cached(self):
"""
Django must prevent caching of responses that set a user-specific (and
maybe security sensitive) cookie in response to a cookie-less request.
Responses that set a new cookie not present in the request are not
cached, regardless of whether the request already had other cookies.
"""
request = self.factory.get("/view/")
csrf_middleware = CsrfViewMiddleware(csrf_view)
csrf_middleware.process_view(request, csrf_view, (), {})
cache_middleware = CacheMiddleware(csrf_middleware)
for headers in ({}, {"HTTP_COOKIE": "unrelated=value"}):
with self.subTest(headers=headers):
request = self.factory.get("/view/", **headers)
csrf_middleware = CsrfViewMiddleware(csrf_view)
csrf_middleware.process_view(request, csrf_view, (), {})
cache_middleware = CacheMiddleware(csrf_middleware)

self.assertIsNone(cache_middleware.process_request(request))
cache_middleware(request)

# Inserting a CSRF cookie prevented caching.
self.assertIsNone(cache_middleware.process_request(request))

def test_refreshed_cookie_not_cached(self):
"""
A response that updates the value of a cookie already present in the
request is not cached. Caching it would allow a stale cookie value to
be served to a different client via a Vary: Cookie cache hit.
"""

def refreshing_cookie_view(request):
response = HttpResponse("content")
response.set_cookie("session", "new_value")
patch_vary_headers(response, ("Cookie",))
return response

request = self.factory.get("/view/", HTTP_COOKIE="session=old_value")
cache_middleware = CacheMiddleware(refreshing_cookie_view)

self.assertIsNone(cache_middleware.process_request(request))
cache_middleware(request)

# Inserting a CSRF cookie in a cookie-less request prevented caching.
# The response refreshed an existing cookie so it must not be cached.
self.assertIsNone(cache_middleware.process_request(request))

def test_304_response_has_http_caching_headers_but_not_cached(self):
Expand Down
6 changes: 6 additions & 0 deletions tests/gis_tests/gdal_tests/test_raster.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,12 @@ def test_vsi_buffer_property(self):
# The vsi buffer is None for rasters that are not vsi based.
self.assertIsNone(self.rs.vsi_buffer)

def test_vsi_buffer_length(self):
with open(self.rs_path, "rb") as rst_file:
rst_bytes = rst_file.read()
vsimem = GDALRaster(rst_bytes)
self.assertEqual(len(vsimem.vsi_buffer), len(rst_bytes))

def test_vsi_vsizip_filesystem(self):
rst_zipfile = NamedTemporaryFile(suffix=".zip")
with zipfile.ZipFile(rst_zipfile, mode="w") as zf:
Expand Down
4 changes: 4 additions & 0 deletions tests/validators/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,8 @@
(validate_domain_name, "dashinpunytld.xn---c", None),
(validate_domain_name, "python..org", ValidationError),
(validate_domain_name, "python-.org", ValidationError),
(validate_domain_name, "example.com\n", ValidationError),
(validate_domain_name, "example.com\r\n", ValidationError),
(validate_domain_name, "too-long-name." * 20 + "com", ValidationError),
(validate_domain_name, "stupid-name试", ValidationError),
(validate_domain_name, "255.0.0.0", ValidationError),
Expand All @@ -677,6 +679,8 @@
ValidationError,
),
(DomainNameValidator(accept_idna=False), "ıçğü.com", ValidationError),
(DomainNameValidator(accept_idna=False), "example.com\n", ValidationError),
(DomainNameValidator(accept_idna=False), "example.com\r\n", ValidationError),
(DomainNameValidator(accept_idna=False), "not-domain-name", ValidationError),
(
DomainNameValidator(accept_idna=False),
Expand Down
Loading