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)