From e6108b7388775f4996a5906e0525fbdd40d2df51 Mon Sep 17 00:00:00 2001 From: VIZZARD-X Date: Sat, 27 Dec 2025 13:54:14 +0530 Subject: [PATCH 1/3] Fixed #36750 -- Made ordering of M2M objects deterministic in serializers. Co-authored-by: Simon Charette Co-authored-by: Jacob Walls --- django/core/serializers/python.py | 17 ++++++++++++++++- django/core/serializers/xml_serializer.py | 17 ++++++++++++++++- tests/serializers/test_natural.py | 23 ++++++++++++++++++++++- 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/django/core/serializers/python.py b/django/core/serializers/python.py index 53a73e19e51f..73ba24368b0b 100644 --- a/django/core/serializers/python.py +++ b/django/core/serializers/python.py @@ -77,7 +77,15 @@ def queryset_iterator(obj, field): chunk_size = ( 2000 if getattr(attr, "prefetch_cache_name", None) else None ) - return attr.iterator(chunk_size) + query_set = attr.all() + if not query_set.totally_ordered: + current_ordering = ( + query_set.query.order_by + or query_set.model._meta.ordering + or [] + ) + query_set = query_set.order_by(*current_ordering, "pk") + return query_set.iterator(chunk_size) else: @@ -86,6 +94,13 @@ def m2m_value(value): def queryset_iterator(obj, field): query_set = getattr(obj, field.name).select_related(None).only("pk") + if not query_set.totally_ordered: + current_ordering = ( + query_set.query.order_by + or query_set.model._meta.ordering + or [] + ) + query_set = query_set.order_by(*current_ordering, "pk") chunk_size = 2000 if query_set._prefetch_related_lookups else None return query_set.iterator(chunk_size=chunk_size) diff --git a/django/core/serializers/xml_serializer.py b/django/core/serializers/xml_serializer.py index d8ffbdf00a98..3d1f79ea0d00 100644 --- a/django/core/serializers/xml_serializer.py +++ b/django/core/serializers/xml_serializer.py @@ -177,7 +177,15 @@ def queryset_iterator(obj, field): chunk_size = ( 2000 if getattr(attr, "prefetch_cache_name", None) else None ) - return attr.iterator(chunk_size) + query_set = attr.all() + if not query_set.totally_ordered: + current_ordering = ( + query_set.query.order_by + or query_set.model._meta.ordering + or [] + ) + query_set = query_set.order_by(*current_ordering, "pk") + return query_set.iterator(chunk_size) else: @@ -186,6 +194,13 @@ def handle_m2m(value): def queryset_iterator(obj, field): query_set = getattr(obj, field.name).select_related(None).only("pk") + if not query_set.totally_ordered: + current_ordering = ( + query_set.query.order_by + or query_set.model._meta.ordering + or [] + ) + query_set = query_set.order_by(*current_ordering, "pk") chunk_size = 2000 if query_set._prefetch_related_lookups else None return query_set.iterator(chunk_size=chunk_size) diff --git a/tests/serializers/test_natural.py b/tests/serializers/test_natural.py index b405dc3e284b..4dff100cc7af 100644 --- a/tests/serializers/test_natural.py +++ b/tests/serializers/test_natural.py @@ -1,5 +1,7 @@ +from unittest import mock + from django.core import serializers -from django.db import connection +from django.db import connection, models from django.test import TestCase from .models import ( @@ -336,6 +338,20 @@ def nullable_natural_key_m2m_test(self, format): ) +def natural_key_m2m_totally_ordered_test(self, format): + t1 = NaturalKeyThing.objects.create(key="t1") + t2 = NaturalKeyThing.objects.create(key="t2") + t3 = NaturalKeyThing.objects.create(key="t3") + t1.other_things.add(t2, t3) + + with mock.patch.object(models.QuerySet, "order_by") as mock_order_by: + serializers.serialize(format, [t1], use_natural_foreign_keys=True) + mock_order_by.assert_called_once_with("pk") + mock_order_by.reset_mock() + serializers.serialize(format, [t1], use_natural_foreign_keys=False) + mock_order_by.assert_called_once_with("pk") + + # Dynamically register tests for each serializer register_tests( NaturalKeySerializerTests, @@ -385,3 +401,8 @@ def nullable_natural_key_m2m_test(self, format): "test_%s_nullable_natural_key_m2m", nullable_natural_key_m2m_test, ) +register_tests( + NaturalKeySerializerTests, + "test_%s_natural_key_m2m_totally_ordered", + natural_key_m2m_totally_ordered_test, +) From 71d1e92e11093feb9a337f6d77078c5e75cbb92f Mon Sep 17 00:00:00 2001 From: Pete Rosenblum Date: Wed, 25 Feb 2026 23:55:18 -0500 Subject: [PATCH 2/3] Refs #36936 - Adjusted tests to set PYTHON_COLORS environment variable. When "FORCE_COLOR" was set in the environment (such as in django-docker-box) several test cases that rely on string comparisons against exception messages or command line results would fail due to ANSI escape sequences being present in the output. This change resolved the issue by setting the "PYTHON_COLORS" environment variable to "0" for those specific tests, which takes precendence over the presence of "FORCE_COLOR". --- tests/admin_scripts/tests.py | 2 ++ tests/test_runner/test_debug_sql.py | 2 ++ tests/test_utils/test_simpletestcase.py | 2 ++ 3 files changed, 6 insertions(+) diff --git a/tests/admin_scripts/tests.py b/tests/admin_scripts/tests.py index 3b5b0380f0f9..114d819847dc 100644 --- a/tests/admin_scripts/tests.py +++ b/tests/admin_scripts/tests.py @@ -134,6 +134,7 @@ def run_test(self, args, settings_file=None, apps=None, umask=-1): python_path.extend(ext_backend_base_dirs) test_environ["PYTHONPATH"] = os.pathsep.join(python_path) test_environ["PYTHONWARNINGS"] = "" + test_environ["PYTHON_COLORS"] = "0" p = subprocess.run( [sys.executable, *args], @@ -2551,6 +2552,7 @@ def _test(self, args, option_b="'2'"): ) +@mock.patch.dict(os.environ, {"PYTHON_COLORS": "0"}) class ExecuteFromCommandLine(SimpleTestCase): def test_program_name_from_argv(self): """ diff --git a/tests/test_runner/test_debug_sql.py b/tests/test_runner/test_debug_sql.py index acf66633ef16..61b60643c57d 100644 --- a/tests/test_runner/test_debug_sql.py +++ b/tests/test_runner/test_debug_sql.py @@ -1,4 +1,5 @@ import logging +import os import unittest from io import StringIO from time import time @@ -183,6 +184,7 @@ def test_log_record_sql_extra_none(self): @unittest.skipUnless( connection.vendor == "sqlite", "Only run on sqlite so we can check output SQL." ) +@mock.patch.dict(os.environ, {"PYTHON_COLORS": "0"}) class TestDebugSQL(unittest.TestCase): class PassingTest(TestCase): def runTest(self): diff --git a/tests/test_utils/test_simpletestcase.py b/tests/test_utils/test_simpletestcase.py index c6aa31494445..c09c68db0c75 100644 --- a/tests/test_utils/test_simpletestcase.py +++ b/tests/test_utils/test_simpletestcase.py @@ -1,3 +1,4 @@ +import os import unittest from io import StringIO from unittest import mock @@ -19,6 +20,7 @@ def skipped_test(self): pass +@mock.patch.dict(os.environ, {"PYTHON_COLORS": "0"}) @mock.patch.object(ErrorTestCase, "_post_teardown") @mock.patch.object(ErrorTestCase, "_pre_setup") class DebugInvocationTests(SimpleTestCase): From 1f2a56567c565d91d797b8a9071ff77a75b52080 Mon Sep 17 00:00:00 2001 From: Natalia <124304+nessita@users.noreply.github.com> Date: Thu, 26 Feb 2026 10:20:21 -0300 Subject: [PATCH 3/3] Adjusted default DoS severity level in Security Policy. --- docs/internals/security.txt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/internals/security.txt b/docs/internals/security.txt index a3d57e8d7c4f..5214bf0704a4 100644 --- a/docs/internals/security.txt +++ b/docs/internals/security.txt @@ -347,8 +347,10 @@ will not issue patches or new releases for those versions. Security issue severity levels ============================== -The severity level of a security vulnerability is determined by the attack -type. +The severity level of a security vulnerability is determined primarily by the +attack type. The Django Security Team retains the authority to adjust severity +levels based on the specific characteristics, context, and potential real-world +impact of individual vulnerabilities. Severity levels are: @@ -361,16 +363,21 @@ Severity levels are: * Cross site scripting (XSS) * Cross site request forgery (CSRF) - * Denial-of-service attacks * Broken authentication * **Low** + * Denial-of-service attacks * Sensitive data exposure * Broken session management * Unvalidated redirects/forwards * Issues requiring an uncommon configuration option +For example, a denial-of-service vulnerability that is exploitable by +unauthenticated attackers and affects default Django configurations, causing +severe performance degradation or service unavailability, may be elevated to +**Moderate**, given the potential impact across the Django ecosystem. + .. _security-disclosure: How Django discloses security issues