Skip to content

Commit f997037

Browse files
jacobtylerwallsnessita
authored andcommitted
[4.2.x] Fixed CVE-2025-13372 -- Protected FilteredRelation against SQL injection in column aliases on PostgreSQL.
Follow-up to CVE-2025-57833. Thanks Stackered for the report, and Simon Charette and Mariusz Felisiak for the reviews. Backport of 5b90ca1 from main.
1 parent 4b5dcc9 commit f997037

File tree

4 files changed

+45
-0
lines changed

4 files changed

+45
-0
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from django.db.models.sql.compiler import ( # isort:skip
2+
SQLAggregateCompiler,
3+
SQLCompiler as BaseSQLCompiler,
4+
SQLDeleteCompiler,
5+
SQLInsertCompiler,
6+
SQLUpdateCompiler,
7+
)
8+
9+
__all__ = [
10+
"SQLAggregateCompiler",
11+
"SQLCompiler",
12+
"SQLDeleteCompiler",
13+
"SQLInsertCompiler",
14+
"SQLUpdateCompiler",
15+
]
16+
17+
18+
class SQLCompiler(BaseSQLCompiler):
19+
def quote_name_unless_alias(self, name):
20+
if "$" in name:
21+
raise ValueError(
22+
"Dollar signs are not permitted in column aliases on PostgreSQL."
23+
)
24+
return super().quote_name_unless_alias(name)

django/db/backends/postgresql/operations.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def get_json_dumps(encoder):
2323

2424

2525
class DatabaseOperations(BaseDatabaseOperations):
26+
compiler_module = "django.db.backends.postgresql.compiler"
2627
cast_char_field_without_max_length = "varchar"
2728
explain_prefix = "EXPLAIN"
2829
explain_options = frozenset(

docs/releases/4.2.27.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ Django 4.2.27 release notes
77
Django 4.2.27 fixes one security issue with severity "high", one security issue
88
with severity "moderate", and one bug in 4.2.26.
99

10+
CVE-2025-13372: Potential SQL injection in ``FilteredRelation`` column aliases on PostgreSQL
11+
============================================================================================
12+
13+
:class:`.FilteredRelation` was subject to SQL injection in column aliases,
14+
using a suitably crafted dictionary, with dictionary expansion, as the
15+
``**kwargs`` passed to :meth:`.QuerySet.annotate` or :meth:`.QuerySet.alias` on
16+
PostgreSQL.
17+
1018
Bugfixes
1119
========
1220

tests/annotations/tests.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from decimal import Decimal
33

44
from django.core.exceptions import FieldDoesNotExist, FieldError
5+
from django.db import connection
56
from django.db.models import (
67
BooleanField,
78
Case,
@@ -1443,3 +1444,14 @@ def test_alias_filtered_relation_sql_injection(self):
14431444
)
14441445
with self.assertRaisesMessage(ValueError, msg):
14451446
Book.objects.alias(**{crafted_alias: FilteredRelation("authors")})
1447+
1448+
def test_alias_filtered_relation_sql_injection_dollar_sign(self):
1449+
qs = Book.objects.alias(
1450+
**{"crafted_alia$": FilteredRelation("authors")}
1451+
).values("name", "crafted_alia$")
1452+
if connection.vendor == "postgresql":
1453+
msg = "Dollar signs are not permitted in column aliases on PostgreSQL."
1454+
with self.assertRaisesMessage(ValueError, msg):
1455+
list(qs)
1456+
else:
1457+
self.assertEqual(qs.first()["name"], self.b1.name)

0 commit comments

Comments
 (0)