diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index 1b1cd91c9a..8742de00d6 100644 --- a/django/db/models/sql/compiler.py +++ b/django/db/models/sql/compiler.py @@ -433,7 +433,7 @@ class SQLCompiler: yield OrderBy(expr, descending=descending), False continue - if "." in field: + if "." in field and field in self.query.extra_order_by: # This came in through an extra(order_by=...) addition. Pass it # on verbatim. table, col = col.split(".", 1) diff --git a/docs/releases/4.2.28.txt b/docs/releases/4.2.28.txt index 473e44f577..1d81095b3e 100644 --- a/docs/releases/4.2.28.txt +++ b/docs/releases/4.2.28.txt @@ -66,3 +66,13 @@ expansion, as the ``**kwargs`` passed to :meth:`.QuerySet.annotate`, This issue has severity "high" according to the :ref:`Django security policy `. + +CVE-2026-1312: Potential SQL injection via ``QuerySet.order_by`` and ``FilteredRelation`` +========================================================================================= + +:meth:`.QuerySet.order_by` was subject to SQL injection in column aliases +containing periods when the same alias was, using a suitably crafted +dictionary, with dictionary expansion, used in :class:`.FilteredRelation`. + +This issue has severity "high" according to the :ref:`Django security policy +`. diff --git a/docs/releases/5.2.11.txt b/docs/releases/5.2.11.txt index fa14a88c0a..76efc4aa8d 100644 --- a/docs/releases/5.2.11.txt +++ b/docs/releases/5.2.11.txt @@ -66,3 +66,13 @@ expansion, as the ``**kwargs`` passed to :meth:`.QuerySet.annotate`, This issue has severity "high" according to the :ref:`Django security policy `. + +CVE-2026-1312: Potential SQL injection via ``QuerySet.order_by`` and ``FilteredRelation`` +========================================================================================= + +:meth:`.QuerySet.order_by` was subject to SQL injection in column aliases +containing periods when the same alias was, using a suitably crafted +dictionary, with dictionary expansion, used in :class:`.FilteredRelation`. + +This issue has severity "high" according to the :ref:`Django security policy +`. diff --git a/docs/releases/6.0.2.txt b/docs/releases/6.0.2.txt index 884c873a6d..0372cf9eb0 100644 --- a/docs/releases/6.0.2.txt +++ b/docs/releases/6.0.2.txt @@ -67,6 +67,16 @@ expansion, as the ``**kwargs`` passed to :meth:`.QuerySet.annotate`, This issue has severity "high" according to the :ref:`Django security policy `. +CVE-2026-1312: Potential SQL injection via ``QuerySet.order_by`` and ``FilteredRelation`` +========================================================================================= + +:meth:`.QuerySet.order_by` was subject to SQL injection in column aliases +containing periods when the same alias was, using a suitably crafted +dictionary, with dictionary expansion, used in :class:`.FilteredRelation`. + +This issue has severity "high" according to the :ref:`Django security policy +`. + Bugfixes ======== diff --git a/tests/ordering/tests.py b/tests/ordering/tests.py index 421689b9fa..afe2e3c22c 100644 --- a/tests/ordering/tests.py +++ b/tests/ordering/tests.py @@ -8,6 +8,7 @@ from django.db.models import ( Count, DateTimeField, F, + FilteredRelation, IntegerField, Max, OrderBy, @@ -17,6 +18,7 @@ from django.db.models import ( When, ) from django.db.models.functions import Length, Upper +from django.db.utils import DatabaseError from django.test import TestCase from .models import ( @@ -395,6 +397,29 @@ class OrderingTests(TestCase): attrgetter("headline"), ) + def test_alias_with_period_shadows_table_name(self): + """ + Aliases with periods are not confused for table names from extra(). + """ + Article.objects.update(author=self.author_2) + Article.objects.create( + headline="Backdated", pub_date=datetime(1900, 1, 1), author=self.author_1 + ) + crafted = "ordering_article.pub_date" + + qs = Article.objects.annotate(**{crafted: F("author")}).order_by("-" + crafted) + self.assertNotEqual(qs[0].headline, "Backdated") + + relation = FilteredRelation("author") + qs2 = Article.objects.annotate(**{crafted: relation}).order_by(crafted) + with self.assertRaises(DatabaseError): + # Before, unlike F(), which causes ordering expressions to be + # replaced by ordinals like n in ORDER BY n, these were ordered by + # pub_date instead of author. + # The Article model orders by -pk, so sorting on author will place + # first any article by author2 instead of the backdated one. + self.assertNotEqual(qs2[0].headline, "Backdated") + def test_order_by_pk(self): """ 'pk' works as an ordering option in Meta.