diff --git a/.gitignore b/.gitignore
index cdf0f19389ec0b4b813ff0f8b13b6ed1eeb5e114..6f16c22f0f69c6d4e7ab5607960ee35c81d32a77 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,3 @@
/5.0.8.tar.gz
+
+django-5.2.7.tar.gz
diff --git a/python-django-5.2.7-CVE-2026-1285.patch b/python-django-5.2.7-CVE-2026-1285.patch
new file mode 100644
index 0000000000000000000000000000000000000000..79195fcba7eb1a1f1690e909f2ef1307e7e21d0c
--- /dev/null
+++ b/python-django-5.2.7-CVE-2026-1285.patch
@@ -0,0 +1,70 @@
+From 9f2ada875bbee62ac46032e38ddb22755d67ae5a Mon Sep 17 00:00:00 2001
+From: Natalia <124304+nessita@users.noreply.github.com>
+Date: Wed, 21 Jan 2026 09:53:10 -0300
+Subject: [PATCH] [5.2.x] Fixed CVE-2026-1285 -- Mitigated potential DoS in
+ django.utils.text.Truncator for HTML input.
+
+The `TruncateHTMLParser` used `deque.remove()` to remove tags from the
+stack when processing end tags. With crafted input containing many
+unmatched end tags, this caused repeated full scans of the tag stack,
+leading to quadratic time complexity.
+
+The fix uses LIFO semantics, only removing a tag from the stack when it
+matches the most recently opened tag. This avoids linear scans for
+unmatched end tags and reduces complexity to linear time.
+
+Refs #30686 and 6ee37ada3241ed263d8d1c2901b030d964cbd161.
+
+Thanks Seokchan Yoon for the report, and Jake Howard and Jacob Walls for
+reviews.
+
+Backport of a33540b3e20b5d759aa8b2e4b9ca0e8edd285344 from main.
+
+Adapted-by: PkgAgent (modified to adapt to opencloudos-stream)
+
+---
+ django/utils/text.py | 9 +++++----
+ tests/utils_tests/test_text.py | 10 ++++++++++
+ 2 files changed, 15 insertions(+), 4 deletions(-)
+
+diff --git a/django/utils/text.py b/django/utils/text.py
+index 26edde9..21efb00 100644
+--- a/django/utils/text.py
++++ b/django/utils/text.py
+@@ -126,10 +126,11 @@ class TruncateHTMLParser(HTMLParser):
+ def handle_endtag(self, tag):
+ if tag not in self.void_elements:
+ self.output += f"{tag}>"
+- try:
+- self.tags.remove(tag)
+- except ValueError:
+- pass
++ # Remove from the stack only if the tag matches the most recently
++ # opened tag (LIFO). This avoids O(n) linear scans for unmatched
++ # end tags if `deque.remove()` would be called.
++ if self.tags and self.tags[0] == tag:
++ self.tags.popleft()
+
+ def handle_data(self, data):
+ data, output = self.process(data)
+diff --git a/tests/utils_tests/test_text.py b/tests/utils_tests/test_text.py
+index 63c7889..11c0187 100644
+--- a/tests/utils_tests/test_text.py
++++ b/tests/utils_tests/test_text.py
+@@ -202,6 +202,16 @@ class TestUtilsText(SimpleTestCase):
+ truncator = text.Truncator("
I <3 python, what about you?
")
+ self.assertEqual("I <3 python, wh…
", truncator.chars(16, html=True))
+
++ def test_truncate_chars_html_with_misnested_tags(self):
++ # LIFO removal keeps all tags when a middle tag is closed out of order.
++ # With , the doesn't match , so all tags remain
++ # in the stack and are properly closed at truncation.
++ truncator = text.Truncator("XXXX")
++ self.assertEqual(
++ truncator.chars(2, html=True, truncate=""),
++ "XX",
++ )
++
+ def test_truncate_words(self):
+ truncator = text.Truncator("The quick brown fox jumped over the lazy dog.")
+ self.assertEqual(
diff --git a/python-django-5.2.7-CVE-2026-1287.patch b/python-django-5.2.7-CVE-2026-1287.patch
new file mode 100644
index 0000000000000000000000000000000000000000..8a1a329ddadb84aec5195eb188c3434a08b04479
--- /dev/null
+++ b/python-django-5.2.7-CVE-2026-1287.patch
@@ -0,0 +1,302 @@
+From 3e68ccdc11c127758745ddf0b4954990b14892bc Mon Sep 17 00:00:00 2001
+From: Jake Howard
+Date: Wed, 21 Jan 2026 11:14:48 +0000
+Subject: [PATCH] [5.2.x] Fixed CVE-2026-1287 -- Protected against SQL
+ injection in column aliases via control characters.
+
+Control characters in FilteredRelation column aliases could be used for
+SQL injection attacks. This affected QuerySet.annotate(), aggregate(),
+extra(), values(), values_list(), and alias() when using dictionary
+expansion with **kwargs.
+
+Thanks Solomon Kebede for the report, and Simon Charette, Jacob Walls,
+and Natalia Bidart for reviews.
+
+Backport of e891a84c7ef9962bfcc3b4685690219542f86a22 from main.
+
+Adapted-by: PkgAgent (modified to adapt to opencloudos-stream)
+
+---
+ django/db/models/sql/query.py | 10 ++--
+ tests/aggregation/tests.py | 18 +++++--
+ tests/annotations/tests.py | 74 +++++++++++++++++++----------
+ tests/expressions/test_queryset_values.py | 36 +++++++++-----
+ tests/queries/tests.py | 18 +++++--
+ 5 files changed, 102 insertions(+), 54 deletions(-)
+
+diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
+index 3a1cd73..baeac3a 100644
+--- a/django/db/models/sql/query.py
++++ b/django/db/models/sql/query.py
+@@ -48,9 +48,11 @@ from django.utils.tree import Node
+
+ __all__ = ["Query", "RawQuery"]
+
+-# Quotation marks ('"`[]), whitespace characters, semicolons, hashes, or inline
+-# SQL comments are forbidden in column aliases.
+-FORBIDDEN_ALIAS_PATTERN = _lazy_re_compile(r"['`\"\]\[;\s]|#|--|/\*|\*/")
++# Quotation marks ('"`[]), whitespace characters, control characters,
++# semicolons, hashes, or inline SQL comments are forbidden in column aliases.
++FORBIDDEN_ALIAS_PATTERN = _lazy_re_compile(
++ r"['`\"\]\[;\s\x00-\x1F\x7F-\x9F]|#|--|/\*|\*/"
++)
+
+ # Inspired from
+ # https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
+@@ -1209,7 +1211,7 @@ class Query(BaseExpression):
+ if FORBIDDEN_ALIAS_PATTERN.search(alias):
+ raise ValueError(
+ "Column aliases cannot contain whitespace characters, hashes, "
+- "quotation marks, semicolons, or SQL comments."
++ "control characters, quotation marks, semicolons, or SQL comments."
+ )
+
+ def add_annotation(self, annotation, alias, select=True):
+diff --git a/tests/aggregation/tests.py b/tests/aggregation/tests.py
+index 2e41f19..f621c53 100644
+--- a/tests/aggregation/tests.py
++++ b/tests/aggregation/tests.py
+@@ -2,6 +2,7 @@ import datetime
+ import math
+ import re
+ from decimal import Decimal
++from itertools import chain
+
+ from django.core.exceptions import FieldError
+ from django.db import connection
+@@ -2134,13 +2135,18 @@ class AggregateTestCase(TestCase):
+ self.assertEqual(len(qs), 6)
+
+ def test_alias_sql_injection(self):
+- crafted_alias = """injected_name" from "aggregation_author"; --"""
+ msg = (
+- "Column aliases cannot contain whitespace characters, hashes, quotation "
+- "marks, semicolons, or SQL comments."
+- )
+- with self.assertRaisesMessage(ValueError, msg):
+- Author.objects.aggregate(**{crafted_alias: Avg("age")})
++ "Column aliases cannot contain whitespace characters, hashes, "
++ "control characters, quotation marks, semicolons, or SQL comments."
++ )
++ for crafted_alias in [
++ """injected_name" from "aggregation_author"; --""",
++ # Control characters.
++ *(f"name{chr(c)}" for c in chain(range(32), range(0x7F, 0xA0))),
++ ]:
++ with self.subTest(crafted_alias):
++ with self.assertRaisesMessage(ValueError, msg):
++ Author.objects.aggregate(**{crafted_alias: Avg("age")})
+
+ def test_exists_extra_where_with_aggregate(self):
+ qs = Book.objects.annotate(
+diff --git a/tests/annotations/tests.py b/tests/annotations/tests.py
+index 78e5408..0430c68 100644
+--- a/tests/annotations/tests.py
++++ b/tests/annotations/tests.py
+@@ -1,5 +1,6 @@
+ import datetime
+ from decimal import Decimal
++from itertools import chain
+ from unittest import skipUnless
+
+ from django.core.exceptions import FieldDoesNotExist, FieldError
+@@ -1157,22 +1158,32 @@ class NonAggregateAnnotationTestCase(TestCase):
+ )
+
+ def test_alias_sql_injection(self):
+- crafted_alias = """injected_name" from "annotations_book"; --"""
+ msg = (
+- "Column aliases cannot contain whitespace characters, hashes, quotation "
+- "marks, semicolons, or SQL comments."
+- )
+- with self.assertRaisesMessage(ValueError, msg):
+- Book.objects.annotate(**{crafted_alias: Value(1)})
++ "Column aliases cannot contain whitespace characters, hashes, "
++ "control characters, quotation marks, semicolons, or SQL comments."
++ )
++ for crafted_alias in [
++ """injected_name" from "annotations_book"; --""",
++ # Control characters.
++ *(f"name{chr(c)}" for c in chain(range(32), range(0x7F, 0xA0))),
++ ]:
++ with self.subTest(crafted_alias):
++ with self.assertRaisesMessage(ValueError, msg):
++ Book.objects.annotate(**{crafted_alias: Value(1)})
+
+ def test_alias_filtered_relation_sql_injection(self):
+- crafted_alias = """injected_name" from "annotations_book"; --"""
+ msg = (
+- "Column aliases cannot contain whitespace characters, hashes, quotation "
+- "marks, semicolons, or SQL comments."
+- )
+- with self.assertRaisesMessage(ValueError, msg):
+- Book.objects.annotate(**{crafted_alias: FilteredRelation("author")})
++ "Column aliases cannot contain whitespace characters, hashes, "
++ "control characters, quotation marks, semicolons, or SQL comments."
++ )
++ for crafted_alias in [
++ """injected_name" from "annotations_book"; --""",
++ # Control characters.
++ *(f"name{chr(c)}" for c in chain(range(32), range(0x7F, 0xA0))),
++ ]:
++ with self.subTest(crafted_alias):
++ with self.assertRaisesMessage(ValueError, msg):
++ Book.objects.annotate(**{crafted_alias: FilteredRelation("author")})
+
+ def test_alias_forbidden_chars(self):
+ tests = [
+@@ -1190,10 +1201,11 @@ class NonAggregateAnnotationTestCase(TestCase):
+ "alias[",
+ "alias]",
+ "ali#as",
++ "ali\0as",
+ ]
+ msg = (
+- "Column aliases cannot contain whitespace characters, hashes, quotation "
+- "marks, semicolons, or SQL comments."
++ "Column aliases cannot contain whitespace characters, hashes, "
++ "control characters, quotation marks, semicolons, or SQL comments."
+ )
+ for crafted_alias in tests:
+ with self.subTest(crafted_alias):
+@@ -1491,22 +1503,32 @@ class AliasTests(TestCase):
+ self.assertEqual(qs.get(pk=self.b1.pk), (self.b1.pk,))
+
+ def test_alias_sql_injection(self):
+- crafted_alias = """injected_name" from "annotations_book"; --"""
+ msg = (
+- "Column aliases cannot contain whitespace characters, hashes, quotation "
+- "marks, semicolons, or SQL comments."
+- )
+- with self.assertRaisesMessage(ValueError, msg):
+- Book.objects.alias(**{crafted_alias: Value(1)})
++ "Column aliases cannot contain whitespace characters, hashes, "
++ "control characters, quotation marks, semicolons, or SQL comments."
++ )
++ for crafted_alias in [
++ """injected_name" from "annotations_book"; --""",
++ # Control characters.
++ *(f"name{chr(c)}" for c in chain(range(32), range(0x7F, 0xA0))),
++ ]:
++ with self.subTest(crafted_alias):
++ with self.assertRaisesMessage(ValueError, msg):
++ Book.objects.alias(**{crafted_alias: Value(1)})
+
+ def test_alias_filtered_relation_sql_injection(self):
+- crafted_alias = """injected_name" from "annotations_book"; --"""
+ msg = (
+- "Column aliases cannot contain whitespace characters, hashes, quotation "
+- "marks, semicolons, or SQL comments."
+- )
+- with self.assertRaisesMessage(ValueError, msg):
+- Book.objects.alias(**{crafted_alias: FilteredRelation("authors")})
++ "Column aliases cannot contain whitespace characters, hashes, "
++ "control characters, quotation marks, semicolons, or SQL comments."
++ )
++ for crafted_alias in [
++ """injected_name" from "annotations_book"; --""",
++ # Control characters.
++ *(f"name{chr(c)}" for c in chain(range(32), range(0x7F, 0xA0))),
++ ]:
++ with self.subTest(crafted_alias):
++ with self.assertRaisesMessage(ValueError, msg):
++ Book.objects.alias(**{crafted_alias: FilteredRelation("authors")})
+
+ def test_alias_filtered_relation_sql_injection_dollar_sign(self):
+ qs = Book.objects.alias(
+diff --git a/tests/expressions/test_queryset_values.py b/tests/expressions/test_queryset_values.py
+index 080ee06..afd8a51 100644
+--- a/tests/expressions/test_queryset_values.py
++++ b/tests/expressions/test_queryset_values.py
+@@ -1,3 +1,5 @@
++from itertools import chain
++
+ from django.db.models import F, Sum
+ from django.test import TestCase, skipUnlessDBFeature
+
+@@ -35,26 +37,36 @@ class ValuesExpressionsTests(TestCase):
+ )
+
+ def test_values_expression_alias_sql_injection(self):
+- crafted_alias = """injected_name" from "expressions_company"; --"""
+ msg = (
+- "Column aliases cannot contain whitespace characters, hashes, quotation "
+- "marks, semicolons, or SQL comments."
++ "Column aliases cannot contain whitespace characters, hashes, "
++ "control characters, quotation marks, semicolons, or SQL comments."
+ )
+- with self.assertRaisesMessage(ValueError, msg):
+- Company.objects.values(**{crafted_alias: F("ceo__salary")})
++ for crafted_alias in [
++ """injected_name" from "expressions_company"; --""",
++ # Control characters.
++ *(f"name{chr(c)}" for c in chain(range(32), range(0x7F, 0xA0))),
++ ]:
++ with self.subTest(crafted_alias):
++ with self.assertRaisesMessage(ValueError, msg):
++ Company.objects.values(**{crafted_alias: F("ceo__salary")})
+
+ @skipUnlessDBFeature("supports_json_field")
+ def test_values_expression_alias_sql_injection_json_field(self):
+- crafted_alias = """injected_name" from "expressions_company"; --"""
+ msg = (
+- "Column aliases cannot contain whitespace characters, hashes, quotation "
+- "marks, semicolons, or SQL comments."
++ "Column aliases cannot contain whitespace characters, hashes, "
++ "control characters, quotation marks, semicolons, or SQL comments."
+ )
+- with self.assertRaisesMessage(ValueError, msg):
+- JSONFieldModel.objects.values(f"data__{crafted_alias}")
++ for crafted_alias in [
++ """injected_name" from "expressions_company"; --""",
++ # Control characters.
++ *(chr(c) for c in chain(range(32), range(0x7F, 0xA0))),
++ ]:
++ with self.subTest(crafted_alias):
++ with self.assertRaisesMessage(ValueError, msg):
++ JSONFieldModel.objects.values(f"data__{crafted_alias}")
+
+- with self.assertRaisesMessage(ValueError, msg):
+- JSONFieldModel.objects.values_list(f"data__{crafted_alias}")
++ with self.assertRaisesMessage(ValueError, msg):
++ JSONFieldModel.objects.values_list(f"data__{crafted_alias}")
+
+ def test_values_expression_group_by(self):
+ # values() applies annotate() first, so values selected are grouped by
+diff --git a/tests/queries/tests.py b/tests/queries/tests.py
+index ffaabf4..ce7fd59 100644
+--- a/tests/queries/tests.py
++++ b/tests/queries/tests.py
+@@ -2,6 +2,7 @@ import datetime
+ import pickle
+ import sys
+ import unittest
++from itertools import chain
+ from operator import attrgetter
+
+ from django.core.exceptions import EmptyResultSet, FieldError, FullResultSet
+@@ -1959,13 +1960,18 @@ class Queries5Tests(TestCase):
+ )
+
+ def test_extra_select_alias_sql_injection(self):
+- crafted_alias = """injected_name" from "queries_note"; --"""
+ msg = (
+- "Column aliases cannot contain whitespace characters, hashes, quotation "
+- "marks, semicolons, or SQL comments."
+- )
+- with self.assertRaisesMessage(ValueError, msg):
+- Note.objects.extra(select={crafted_alias: "1"})
++ "Column aliases cannot contain whitespace characters, hashes, "
++ "control characters, quotation marks, semicolons, or SQL comments."
++ )
++ for crafted_alias in [
++ """injected_name" from "queries_note"; --""",
++ # Control characters.
++ *(f"name{chr(c)}" for c in chain(range(32), range(0x7F, 0xA0))),
++ ]:
++ with self.subTest(crafted_alias):
++ with self.assertRaisesMessage(ValueError, msg):
++ Note.objects.extra(select={crafted_alias: "1"})
+
+ def test_queryset_reuse(self):
+ # Using querysets doesn't mutate aliases.
diff --git a/python-django.spec b/python-django.spec
index 143a5ac3dbb83aecc6d125d7581601ba5de20c75..dc990e71ceb67a244bf79831354c0a14742ba6b8 100644
--- a/python-django.spec
+++ b/python-django.spec
@@ -4,7 +4,7 @@
Summary: A high-level Python Web framework
Name: python-django
Version: 5.2.7
-Release: 3%{?dist}
+Release: 5%{?dist}
License: BSD
URL: https://www.djangoproject.com/
Source0: https://github.com/django/django/archive/refs/tags/django-%{version}.tar.gz
@@ -15,6 +15,8 @@ Patch001: python-django-5.2.7-CVE-2025-64458.patch
Patch002: python-django-5.2.7-CVE-2025-64459.patch
Patch003: python-django-5.2.7-CVE-2025-64460.patch
Patch004: python-django-5.2.7-CVE-2025-13372.patch
+Patch005: python-django-5.2.7-CVE-2026-1285.patch
+Patch006: python-django-5.2.7-CVE-2026-1287.patch
BuildArch: noarch
@@ -128,6 +130,10 @@ python3 runtests.py --settings=test_sqlite --verbosity=2 --parallel 1
%changelog
+* Mon Mar 02 2026 ze-you-liu - 5.2.7-5
+- [Type] security
+- [DESC] Fix CVE-2026-1285, CVE-2026-1287
+
* Thu Dec 04 2025 Shop You - 5.2.7-3
- [Type] security
- [DESC] Fix CVE-2025-64460 and CVE-2025-13372