Skip to content

Commit 4ceaaee

Browse files
felixxmjacobtylerwalls
authored andcommitted
[6.0.x] Fixed CVE-2025-59681 -- Protected QuerySet.annotate(), alias(), aggregate(), and extra() against SQL injection in column aliases on MySQL/MariaDB.
Thanks sw0rd1ight for the report. Follow up to 93cae5c. Backport of 41b43c7 from main.
1 parent ee06106 commit 4ceaaee

File tree

8 files changed

+64
-39
lines changed

8 files changed

+64
-39
lines changed

django/db/models/sql/query.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,12 @@
5252
__all__ = ["Query", "RawQuery"]
5353

5454
# RemovedInDjango70Warning: When the deprecation ends, replace with:
55-
# Quotation marks ('"`[]), whitespace characters, semicolons, percent signs
56-
# or inline SQL comments are forbidden in column aliases.
57-
# FORBIDDEN_ALIAS_PATTERN = _lazy_re_compile(r"['`\"\]\[;\s]|%|--|/\*|\*/")
58-
# Quotation marks ('"`[]), whitespace characters, semicolons, or inline
55+
# Quotation marks ('"`[]), whitespace characters, semicolons, percent signs,
56+
# hashes, or inline SQL comments are forbidden in column aliases.
57+
# FORBIDDEN_ALIAS_PATTERN = _lazy_re_compile(r"['`\"\]\[;\s]|%|#|--|/\*|\*/")
58+
# Quotation marks ('"`[]), whitespace characters, semicolons, hashes, or inline
5959
# SQL comments are forbidden in column aliases.
60-
FORBIDDEN_ALIAS_PATTERN = _lazy_re_compile(r"['`\"\]\[;\s]|--|/\*|\*/")
60+
FORBIDDEN_ALIAS_PATTERN = _lazy_re_compile(r"['`\"\]\[;\s]|#|--|/\*|\*/")
6161

6262
# Inspired from
6363
# https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
@@ -1228,11 +1228,12 @@ def check_alias(self, alias):
12281228
)
12291229
if FORBIDDEN_ALIAS_PATTERN.search(alias):
12301230
raise ValueError(
1231-
"Column aliases cannot contain whitespace characters, quotation marks, "
1231+
"Column aliases cannot contain whitespace characters, hashes, "
12321232
# RemovedInDjango70Warning: When the deprecation ends, replace
12331233
# with:
1234-
# "semicolons, percent signs, or SQL comments."
1235-
"semicolons, or SQL comments."
1234+
# "quotation marks, semicolons, percent signs, or SQL "
1235+
# "comments."
1236+
"quotation marks, semicolons, or SQL comments."
12361237
)
12371238

12381239
def add_annotation(self, annotation, alias, select=True):

docs/releases/4.2.25.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,11 @@ Django 4.2.25 release notes
77
Django 4.2.25 fixes one security issue with severity "high" and one security
88
issue with severity "low" in 4.2.24.
99

10-
...
10+
CVE-2025-59681: Potential SQL injection in ``QuerySet.annotate()``, ``alias()``, ``aggregate()``, and ``extra()`` on MySQL and MariaDB
11+
======================================================================================================================================
12+
13+
:meth:`.QuerySet.annotate`, :meth:`~.QuerySet.alias`,
14+
:meth:`~.QuerySet.aggregate`, and :meth:`~.QuerySet.extra` methods were subject
15+
to SQL injection in column aliases, using a suitably crafted dictionary, with
16+
dictionary expansion, as the ``**kwargs`` passed to these methods (follow up to
17+
:cve:`2022-28346`).

docs/releases/5.1.13.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,11 @@ Django 5.1.13 release notes
77
Django 5.1.13 fixes one security issue with severity "high" and one security
88
issue with severity "low" in 5.1.12.
99

10-
...
10+
CVE-2025-59681: Potential SQL injection in ``QuerySet.annotate()``, ``alias()``, ``aggregate()``, and ``extra()`` on MySQL and MariaDB
11+
======================================================================================================================================
12+
13+
:meth:`.QuerySet.annotate`, :meth:`~.QuerySet.alias`,
14+
:meth:`~.QuerySet.aggregate`, and :meth:`~.QuerySet.extra` methods were subject
15+
to SQL injection in column aliases, using a suitably crafted dictionary, with
16+
dictionary expansion, as the ``**kwargs`` passed to these methods (follow up to
17+
:cve:`2022-28346`).

docs/releases/5.2.7.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@ Django 5.2.7 fixes one security issue with severity "high", one security issue
88
with severity "low", and one bug in 5.2.6. Also, the latest string translations
99
from Transifex are incorporated.
1010

11+
CVE-2025-59681: Potential SQL injection in ``QuerySet.annotate()``, ``alias()``, ``aggregate()``, and ``extra()`` on MySQL and MariaDB
12+
======================================================================================================================================
13+
14+
:meth:`.QuerySet.annotate`, :meth:`~.QuerySet.alias`,
15+
:meth:`~.QuerySet.aggregate`, and :meth:`~.QuerySet.extra` methods were subject
16+
to SQL injection in column aliases, using a suitably crafted dictionary, with
17+
dictionary expansion, as the ``**kwargs`` passed to these methods (follow up to
18+
:cve:`2022-28346`).
19+
1120
Bugfixes
1221
========
1322

tests/aggregation/tests.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2244,8 +2244,8 @@ def test_exists_none_with_aggregate(self):
22442244
def test_alias_sql_injection(self):
22452245
crafted_alias = """injected_name" from "aggregation_author"; --"""
22462246
msg = (
2247-
"Column aliases cannot contain whitespace characters, quotation marks, "
2248-
"semicolons, or SQL comments."
2247+
"Column aliases cannot contain whitespace characters, hashes, quotation "
2248+
"marks, semicolons, or SQL comments."
22492249
)
22502250
with self.assertRaisesMessage(ValueError, msg):
22512251
Author.objects.aggregate(**{crafted_alias: Avg("age")})

tests/annotations/tests.py

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,12 +1161,12 @@ def test_alias_sql_injection(self):
11611161
crafted_alias = """injected_name" from "annotations_book"; --"""
11621162
# RemovedInDjango70Warning: When the deprecation ends, replace with:
11631163
# msg = (
1164-
# "Column aliases cannot contain whitespace characters, quotation "
1165-
# "marks, semicolons, percent signs, or SQL comments."
1164+
# "Column aliases cannot contain whitespace characters, hashes, "
1165+
# "quotation marks, semicolons, percent signs, or SQL comments."
11661166
# )
11671167
msg = (
1168-
"Column aliases cannot contain whitespace characters, quotation marks, "
1169-
"semicolons, or SQL comments."
1168+
"Column aliases cannot contain whitespace characters, hashes, quotation "
1169+
"marks, semicolons, or SQL comments."
11701170
)
11711171
with self.assertRaisesMessage(ValueError, msg):
11721172
Book.objects.annotate(**{crafted_alias: Value(1)})
@@ -1175,12 +1175,12 @@ def test_alias_filtered_relation_sql_injection(self):
11751175
crafted_alias = """injected_name" from "annotations_book"; --"""
11761176
# RemovedInDjango70Warning: When the deprecation ends, replace with:
11771177
# msg = (
1178-
# "Column aliases cannot contain whitespace characters, quotation "
1179-
# "marks, semicolons, percent signs, or SQL comments."
1178+
# "Column aliases cannot contain whitespace characters, hashes, "
1179+
# "quotation marks, semicolons, percent signs, or SQL comments."
11801180
# )
11811181
msg = (
1182-
"Column aliases cannot contain whitespace characters, quotation marks, "
1183-
"semicolons, or SQL comments."
1182+
"Column aliases cannot contain whitespace characters, hashes, quotation "
1183+
"marks, semicolons, or SQL comments."
11841184
)
11851185
with self.assertRaisesMessage(ValueError, msg):
11861186
Book.objects.annotate(**{crafted_alias: FilteredRelation("author")})
@@ -1199,18 +1199,19 @@ def test_alias_forbidden_chars(self):
11991199
"alias;",
12001200
# RemovedInDjango70Warning: When the deprecation ends, add this:
12011201
# "alias%",
1202-
# [] are used by MSSQL.
1202+
# [] and # are used by MSSQL.
12031203
"alias[",
12041204
"alias]",
1205+
"ali#as",
12051206
]
12061207
# RemovedInDjango70Warning: When the deprecation ends, replace with:
12071208
# msg = (
1208-
# "Column aliases cannot contain whitespace characters, quotation "
1209-
# "marks, semicolons, percent signs, or SQL comments."
1209+
# "Column aliases cannot contain whitespace characters, hashes, "
1210+
# "quotation marks, semicolons, percent signs, or SQL comments."
12101211
# )
12111212
msg = (
1212-
"Column aliases cannot contain whitespace characters, quotation marks, "
1213-
"semicolons, or SQL comments."
1213+
"Column aliases cannot contain whitespace characters, hashes, quotation "
1214+
"marks, semicolons, or SQL comments."
12141215
)
12151216
for crafted_alias in tests:
12161217
with self.subTest(crafted_alias):
@@ -1516,12 +1517,12 @@ def test_alias_sql_injection(self):
15161517
crafted_alias = """injected_name" from "annotations_book"; --"""
15171518
# RemovedInDjango70Warning: When the deprecation ends, replace with:
15181519
# msg = (
1519-
# "Column aliases cannot contain whitespace characters, quotation "
1520-
# "marks, semicolons, percent signs, or SQL comments."
1520+
# "Column aliases cannot contain whitespace characters, hashes, "
1521+
# "quotation marks, semicolons, percent signs, or SQL comments."
15211522
# )
15221523
msg = (
1523-
"Column aliases cannot contain whitespace characters, quotation marks, "
1524-
"semicolons, or SQL comments."
1524+
"Column aliases cannot contain whitespace characters, hashes, quotation "
1525+
"marks, semicolons, or SQL comments."
15251526
)
15261527
with self.assertRaisesMessage(ValueError, msg):
15271528
Book.objects.alias(**{crafted_alias: Value(1)})
@@ -1530,12 +1531,12 @@ def test_alias_filtered_relation_sql_injection(self):
15301531
crafted_alias = """injected_name" from "annotations_book"; --"""
15311532
# RemovedInDjango70Warning: When the deprecation ends, replace with:
15321533
# msg = (
1533-
# "Column aliases cannot contain whitespace characters, quotation "
1534-
# "marks, semicolons, percent signs, or SQL comments."
1534+
# "Column aliases cannot contain whitespace characters, hashes, "
1535+
# "quotation marks, semicolons, percent signs, or SQL comments."
15351536
# )
15361537
msg = (
1537-
"Column aliases cannot contain whitespace characters, quotation marks, "
1538-
"semicolons, or SQL comments."
1538+
"Column aliases cannot contain whitespace characters, hashes, quotation "
1539+
"marks, semicolons, or SQL comments."
15391540
)
15401541
with self.assertRaisesMessage(ValueError, msg):
15411542
Book.objects.alias(**{crafted_alias: FilteredRelation("authors")})

tests/expressions/test_queryset_values.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ def test_values_expression_containing_percent_sign_deprecation_warns_once(self):
4444
def test_values_expression_alias_sql_injection(self):
4545
crafted_alias = """injected_name" from "expressions_company"; --"""
4646
msg = (
47-
"Column aliases cannot contain whitespace characters, quotation marks, "
48-
"semicolons, or SQL comments."
47+
"Column aliases cannot contain whitespace characters, hashes, quotation "
48+
"marks, semicolons, or SQL comments."
4949
)
5050
with self.assertRaisesMessage(ValueError, msg):
5151
Company.objects.values(**{crafted_alias: F("ceo__salary")})
@@ -54,8 +54,8 @@ def test_values_expression_alias_sql_injection(self):
5454
def test_values_expression_alias_sql_injection_json_field(self):
5555
crafted_alias = """injected_name" from "expressions_company"; --"""
5656
msg = (
57-
"Column aliases cannot contain whitespace characters, quotation marks, "
58-
"semicolons, or SQL comments."
57+
"Column aliases cannot contain whitespace characters, hashes, quotation "
58+
"marks, semicolons, or SQL comments."
5959
)
6060
with self.assertRaisesMessage(ValueError, msg):
6161
JSONFieldModel.objects.values(f"data__{crafted_alias}")

tests/queries/tests.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1967,8 +1967,8 @@ def test_extra_select_literal_percent_s(self):
19671967
def test_extra_select_alias_sql_injection(self):
19681968
crafted_alias = """injected_name" from "queries_note"; --"""
19691969
msg = (
1970-
"Column aliases cannot contain whitespace characters, quotation marks, "
1971-
"semicolons, or SQL comments."
1970+
"Column aliases cannot contain whitespace characters, hashes, quotation "
1971+
"marks, semicolons, or SQL comments."
19721972
)
19731973
with self.assertRaisesMessage(ValueError, msg):
19741974
Note.objects.extra(select={crafted_alias: "1"})

0 commit comments

Comments
 (0)