From fa83180ea2092d3a7237f676b70622141675f5d6 Mon Sep 17 00:00:00 2001
From: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
Date: Thu, 6 Mar 2025 09:59:11 +0100
Subject: [PATCH 1/7] [5.0.x] Post-release version bump.
---
django/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/django/__init__.py b/django/__init__.py
index 85122852ebb3..f8b1e22298c4 100644
--- a/django/__init__.py
+++ b/django/__init__.py
@@ -1,6 +1,6 @@
from django.utils.version import get_version
-VERSION = (5, 0, 13, "final", 0)
+VERSION = (5, 0, 14, "alpha", 0)
__version__ = get_version(VERSION)
From d1f0a1a544861811bc163b4466cb5a838934eb84 Mon Sep 17 00:00:00 2001
From: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
Date: Thu, 6 Mar 2025 14:04:36 +0100
Subject: [PATCH 2/7] [5.0.x] Added CVE-2025-26699 to security archive.
Backport of bad1a18ff28a671f2fdfd447bdf8f43602f882c2 from main.
---
docs/releases/security.txt | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/docs/releases/security.txt b/docs/releases/security.txt
index f997fe94a3a3..acc143770b11 100644
--- a/docs/releases/security.txt
+++ b/docs/releases/security.txt
@@ -36,6 +36,17 @@ Issues under Django's security process
All security issues have been handled under versions of Django's security
process. These are listed below.
+March 6, 2025 - :cve:`2025-26699`
+---------------------------------
+
+Potential denial-of-service in ``django.utils.text.wrap()``.
+`Full description
+`__
+
+* Django 5.1 :commit:`(patch) <8dbb44d34271637099258391dfc79df33951b841>`
+* Django 5.0 :commit:`(patch) <4f2765232336b8ad0afd8017d9d912ae93470017>`
+* Django 4.2 :commit:`(patch) `
+
January 14, 2025 - :cve:`2024-56374`
------------------------------------
From f00239cbf6e37b698562ca0f02cfcc0a367609c0 Mon Sep 17 00:00:00 2001
From: nessita <124304+nessita@users.noreply.github.com>
Date: Wed, 9 Oct 2024 10:19:12 -0300
Subject: [PATCH 3/7] [5.0.x] Added GitHub Action workflow to test all Python
versions listed in the project config file.
Backport of 470f4c2436e00873a31673a5992c5260b2de4e97 from main.
---
.github/workflows/python_matrix.yml | 52 +++++++++++++++++++++++++++++
1 file changed, 52 insertions(+)
create mode 100644 .github/workflows/python_matrix.yml
diff --git a/.github/workflows/python_matrix.yml b/.github/workflows/python_matrix.yml
new file mode 100644
index 000000000000..314d9301b885
--- /dev/null
+++ b/.github/workflows/python_matrix.yml
@@ -0,0 +1,52 @@
+name: Python Matrix from config file
+
+on:
+ pull_request:
+ types: [labeled, synchronize, opened, reopened]
+ paths-ignore:
+ - 'docs/**'
+ workflow_dispatch:
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+permissions:
+ contents: read
+
+jobs:
+ define-matrix:
+ if: contains(github.event.pull_request.labels.*.name, 'python-matrix')
+ runs-on: ubuntu-latest
+ outputs:
+ python_versions_output: ${{ steps.set-matrix.outputs.python_versions }}
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ - id: set-matrix
+ run: |
+ python_versions=$(sed -n "s/^.*Programming Language :: Python :: \([[:digit:]]\+\.[[:digit:]]\+\).*$/'\1', /p" setup.cfg | tr -d '\n' | sed 's/, $//g')
+ echo "Supported Python versions: $python_versions"
+ echo "python_versions=[$python_versions]" >> "$GITHUB_OUTPUT"
+ python:
+ runs-on: ubuntu-latest
+ needs: define-matrix
+ strategy:
+ matrix:
+ python-version: ${{ fromJson(needs.define-matrix.outputs.python_versions_output) }}
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v5
+ with:
+ python-version: ${{ matrix.python-version }}
+ cache: 'pip'
+ cache-dependency-path: 'tests/requirements/py3.txt'
+ - name: Install libmemcached-dev for pylibmc
+ run: sudo apt-get install libmemcached-dev
+ - name: Install and upgrade packaging tools
+ run: python -m pip install --upgrade pip setuptools wheel
+ - run: python -m pip install -r tests/requirements/py3.txt -e .
+ - name: Run tests
+ run: python tests/runtests.py -v2
From 717e34bd48f2686e324b799329910df106971753 Mon Sep 17 00:00:00 2001
From: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
Date: Tue, 25 Mar 2025 17:19:07 +0100
Subject: [PATCH 4/7] [5.0.x] Pinned black == 24.10.0 in GitHub actions,
pre-commit and test requirements.
---
.github/workflows/linters.yml | 2 +-
.pre-commit-config.yaml | 4 ++--
tests/requirements/py3.txt | 2 +-
tox.ini | 2 +-
4 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml
index f590b5a7dc4d..a06c0ac6367e 100644
--- a/.github/workflows/linters.yml
+++ b/.github/workflows/linters.yml
@@ -59,4 +59,4 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- name: black
- uses: psf/black@stable
+ uses: psf/black@24.10.0
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index d6ea11e8e06f..00d9b65a59f9 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/psf/black-pre-commit-mirror
- rev: 24.1.0
+ rev: 24.10.0
hooks:
- id: black
exclude: \.py-tpl$
@@ -9,7 +9,7 @@ repos:
hooks:
- id: blacken-docs
additional_dependencies:
- - black==24.1.0
+ - black==24.10.0
files: 'docs/.*\.txt$'
- repo: https://github.com/PyCQA/isort
rev: 5.13.2
diff --git a/tests/requirements/py3.txt b/tests/requirements/py3.txt
index aad91469ec33..467a086b4b36 100644
--- a/tests/requirements/py3.txt
+++ b/tests/requirements/py3.txt
@@ -2,7 +2,7 @@ aiosmtpd
asgiref >= 3.7.0
argon2-cffi >= 19.2.0
bcrypt
-black
+black == 24.10.0
docutils >= 0.19
geoip2; python_version < '3.12'
jinja2 >= 2.11.0
diff --git a/tox.ini b/tox.ini
index 2e0a3b421a22..9d412e53a707 100644
--- a/tox.ini
+++ b/tox.ini
@@ -37,7 +37,7 @@ commands =
[testenv:black]
basepython = python3
usedevelop = false
-deps = black
+deps = black == 24.10.0
changedir = {toxinidir}
commands = black --check --diff .
From 2be56bc534a1ef7c9bae63182e6053513daa0d25 Mon Sep 17 00:00:00 2001
From: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
Date: Wed, 26 Mar 2025 09:00:27 +0100
Subject: [PATCH 5/7] [5.0.x] Added stub release notes and release date for
5.0.14.
Backport of c75fbe843079ca249d7015926490dd21107e63a4 from main.
---
docs/releases/5.0.14.txt | 7 +++++++
docs/releases/index.txt | 1 +
2 files changed, 8 insertions(+)
create mode 100644 docs/releases/5.0.14.txt
diff --git a/docs/releases/5.0.14.txt b/docs/releases/5.0.14.txt
new file mode 100644
index 000000000000..8684a270dc13
--- /dev/null
+++ b/docs/releases/5.0.14.txt
@@ -0,0 +1,7 @@
+===========================
+Django 5.0.14 release notes
+===========================
+
+*April 2, 2025*
+
+Django 5.0.14 fixes a security issue with severity "moderate" in 5.0.13.
diff --git a/docs/releases/index.txt b/docs/releases/index.txt
index 25e79aef280d..134001c061d4 100644
--- a/docs/releases/index.txt
+++ b/docs/releases/index.txt
@@ -25,6 +25,7 @@ versions of the documentation contain the release notes for any later releases.
.. toctree::
:maxdepth: 1
+ 5.0.14
5.0.13
5.0.12
5.0.11
From 8c6871b097b6c49d2a782c0d80d908bcbe2116f1 Mon Sep 17 00:00:00 2001
From: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
Date: Thu, 6 Mar 2025 15:24:56 +0100
Subject: [PATCH 6/7] [5.0.x] Fixed CVE-2025-27556 -- Mitigated potential DoS
in url_has_allowed_host_and_scheme() on Windows.
Thank you sw0rd1ight for the report.
Backport of 39e2297210d9d2938c75fc911d45f0e863dc4821 from main.
---
django/core/validators.py | 3 ++-
django/utils/html.py | 3 +--
django/utils/http.py | 6 +++++-
docs/releases/5.0.14.txt | 10 ++++++++++
tests/utils_tests/test_http.py | 16 ++++++++++++++++
5 files changed, 34 insertions(+), 4 deletions(-)
diff --git a/django/core/validators.py b/django/core/validators.py
index fe8d46526ab5..14b89ff11e05 100644
--- a/django/core/validators.py
+++ b/django/core/validators.py
@@ -7,6 +7,7 @@
from django.core.exceptions import ValidationError
from django.utils.deconstruct import deconstructible
from django.utils.encoding import punycode
+from django.utils.http import MAX_URL_LENGTH
from django.utils.ipv6 import is_valid_ipv6_address
from django.utils.regex_helper import _lazy_re_compile
from django.utils.translation import gettext_lazy as _
@@ -104,7 +105,7 @@ class URLValidator(RegexValidator):
message = _("Enter a valid URL.")
schemes = ["http", "https", "ftp", "ftps"]
unsafe_chars = frozenset("\t\r\n")
- max_length = 2048
+ max_length = MAX_URL_LENGTH
def __init__(self, schemes=None, **kwargs):
super().__init__(**kwargs)
diff --git a/django/utils/html.py b/django/utils/html.py
index d04e594d13a1..df0b3984df7b 100644
--- a/django/utils/html.py
+++ b/django/utils/html.py
@@ -11,7 +11,7 @@
from django.utils.deprecation import RemovedInDjango60Warning
from django.utils.encoding import punycode
from django.utils.functional import Promise, cached_property, keep_lazy, keep_lazy_text
-from django.utils.http import RFC3986_GENDELIMS, RFC3986_SUBDELIMS
+from django.utils.http import MAX_URL_LENGTH, RFC3986_GENDELIMS, RFC3986_SUBDELIMS
from django.utils.regex_helper import _lazy_re_compile
from django.utils.safestring import SafeData, SafeString, mark_safe
from django.utils.text import normalize_newlines
@@ -37,7 +37,6 @@
"spacer",
}
-MAX_URL_LENGTH = 2048
MAX_STRIP_TAGS_DEPTH = 50
diff --git a/django/utils/http.py b/django/utils/http.py
index cfd982fc016d..cc340ce84977 100644
--- a/django/utils/http.py
+++ b/django/utils/http.py
@@ -37,6 +37,7 @@
RFC3986_GENDELIMS = ":/?#[]@"
RFC3986_SUBDELIMS = "!$&'()*+,;="
+MAX_URL_LENGTH = 2048
def urlencode(query, doseq=False):
@@ -273,7 +274,10 @@ def url_has_allowed_host_and_scheme(url, allowed_hosts, require_https=False):
def _url_has_allowed_host_and_scheme(url, allowed_hosts, require_https=False):
# Chrome considers any URL with more than two slashes to be absolute, but
# urlparse is not so flexible. Treat any url with three slashes as unsafe.
- if url.startswith("///"):
+ if url.startswith("///") or len(url) > MAX_URL_LENGTH:
+ # urlparse does not perform validation of inputs. Unicode normalization
+ # is very slow on Windows and can be a DoS attack vector.
+ # https://docs.python.org/3/library/urllib.parse.html#url-parsing-security
return False
try:
url_info = urlparse(url)
diff --git a/docs/releases/5.0.14.txt b/docs/releases/5.0.14.txt
index 8684a270dc13..230bfed652e0 100644
--- a/docs/releases/5.0.14.txt
+++ b/docs/releases/5.0.14.txt
@@ -5,3 +5,13 @@ Django 5.0.14 release notes
*April 2, 2025*
Django 5.0.14 fixes a security issue with severity "moderate" in 5.0.13.
+
+CVE-2025-27556: Potential denial-of-service vulnerability in ``LoginView``, ``LogoutView``, and ``set_language()`` on Windows
+=============================================================================================================================
+
+Python's :func:`NFKC normalization ` is slow on
+Windows. As a consequence, :class:`~django.contrib.auth.views.LoginView`,
+:class:`~django.contrib.auth.views.LogoutView`, and
+:func:`~django.views.i18n.set_language` were subject to a potential
+denial-of-service attack via certain inputs with a very large number of Unicode
+characters.
diff --git a/tests/utils_tests/test_http.py b/tests/utils_tests/test_http.py
index 818c35e59727..20fe2c4c70d9 100644
--- a/tests/utils_tests/test_http.py
+++ b/tests/utils_tests/test_http.py
@@ -6,6 +6,7 @@
from django.test import SimpleTestCase
from django.utils.datastructures import MultiValueDict
from django.utils.http import (
+ MAX_URL_LENGTH,
base36_to_int,
content_disposition_header,
escape_leading_slashes,
@@ -273,6 +274,21 @@ def test_secure_param_non_https_urls(self):
False,
)
+ def test_max_url_length(self):
+ allowed_host = "example.com"
+ max_extra_characters = "é" * (MAX_URL_LENGTH - len(allowed_host) - 1)
+ max_length_boundary_url = f"{allowed_host}/{max_extra_characters}"
+ cases = [
+ (max_length_boundary_url, True),
+ (max_length_boundary_url + "ú", False),
+ ]
+ for url, expected in cases:
+ with self.subTest(url=url):
+ self.assertIs(
+ url_has_allowed_host_and_scheme(url, allowed_hosts={allowed_host}),
+ expected,
+ )
+
class URLSafeBase64Tests(unittest.TestCase):
def test_roundtrip(self):
From 7c128112a6adff20b6d67a02c4098a2c10f88191 Mon Sep 17 00:00:00 2001
From: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
Date: Wed, 2 Apr 2025 10:59:06 +0200
Subject: [PATCH 7/7] [5.0.x] Bumped version for 5.0.14 release.
---
django/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/django/__init__.py b/django/__init__.py
index f8b1e22298c4..52ed8a2c4550 100644
--- a/django/__init__.py
+++ b/django/__init__.py
@@ -1,6 +1,6 @@
from django.utils.version import get_version
-VERSION = (5, 0, 14, "alpha", 0)
+VERSION = (5, 0, 14, "final", 0)
__version__ = get_version(VERSION)