diff --git a/CVE-2022-34265.patch b/CVE-2022-34265.patch deleted file mode 100644 index 757432fbcb896ab19fde87cc23dc943154da4c1f..0000000000000000000000000000000000000000 --- a/CVE-2022-34265.patch +++ /dev/null @@ -1,41 +0,0 @@ -diff -Naur a/django/db/backends/base/operations.py b/django/db/backends/base/operations.py ---- a/django/db/backends/base/operations.py 2022-12-06 17:12:38.000000000 +0800 -+++ b/django/db/backends/base/operations.py 2022-12-09 11:49:05.347986919 +0800 -@@ -9,6 +9,7 @@ - from django.db.backends import utils - from django.utils import timezone - from django.utils.encoding import force_str -+from django.utils.regex_helper import _lazy_re_compile - - - class BaseDatabaseOperations: -@@ -53,6 +54,8 @@ - - # Prefix for EXPLAIN queries, or None EXPLAIN isn't supported. - explain_prefix = None -+ extract_trunc_lookup_pattern = _lazy_re_compile(r"[\w\-_()]+") -+ - - def __init__(self, connection): - self.connection = connection -diff -Naur a/django/db/models/functions/datetime.py b/django/db/models/functions/datetime.py ---- a/django/db/models/functions/datetime.py 2022-12-06 17:12:38.000000000 +0800 -+++ b/django/db/models/functions/datetime.py 2022-12-09 11:50:50.011981885 +0800 -@@ -51,6 +51,8 @@ - super().__init__(expression, **extra) - - def as_sql(self, compiler, connection): -+ if not connection.ops.extract_trunc_lookup_pattern.fullmatch(self.lookup_name): -+ raise ValueError("Invalid lookup_name: %s" % self.lookup_name) - sql, params = compiler.compile(self.lhs) - lhs_output_field = self.lhs.output_field - if isinstance(lhs_output_field, DateTimeField): -@@ -243,6 +245,8 @@ - super().__init__(expression, output_field=output_field, **extra) - - def as_sql(self, compiler, connection): -+ if not connection.ops.extract_trunc_lookup_pattern.fullmatch(self.kind): -+ raise ValueError("Invalid kind: %s" % self.kind) - sql, params = compiler.compile(self.lhs) - tzname = None - if isinstance(self.lhs.output_field, DateTimeField): diff --git a/CVE-2023-23969.patch b/CVE-2023-23969.patch deleted file mode 100644 index 2d73ffa0b91a7f74130a566c1877a41cb417deb8..0000000000000000000000000000000000000000 --- a/CVE-2023-23969.patch +++ /dev/null @@ -1,100 +0,0 @@ -From ffde9b888863f30c62c728c6f9538a09d7396ed9 Mon Sep 17 00:00:00 2001 -From: starlet-dx <15929766099@163.com> -Date: Mon, 13 Feb 2023 19:26:27 +0800 -Subject: [PATCH 1/1] [4.1.x] Fixed CVE-2023-23969 -- Prevented DoS with pathological values for Accept-Language. - -The parsed values of Accept-Language headers are cached in order to avoid repetitive parsing. This leads to a potential denial-of-service vector via excessive memory usage if the raw value of Accept-Language headers is very large. - -Accept-Language headers are now limited to a maximum length in order to -avoid this issue. ---- - django/utils/translation/trans_real.py | 31 +++++++++++++++++++++++++- - tests/i18n/tests.py | 12 ++++++++++ - 2 files changed, 42 insertions(+), 1 deletion(-) - -diff --git a/django/utils/translation/trans_real.py b/django/utils/translation/trans_real.py -index 423f30e..2974cc6 100644 ---- a/django/utils/translation/trans_real.py -+++ b/django/utils/translation/trans_real.py -@@ -30,6 +30,11 @@ _default = None - # magic gettext number to separate context from message - CONTEXT_SEPARATOR = "\x04" - -+# Maximum number of characters that will be parsed from the Accept-Language -+# header to prevent possible denial of service or memory exhaustion attacks. -+# About 10x longer than the longest value shown on MDN’s Accept-Language page. -+ACCEPT_LANGUAGE_HEADER_MAX_LENGTH = 500 -+ - # Format of Accept-Language header values. From RFC 2616, section 14.4 and 3.9 - # and RFC 3066, section 2.1 - accept_language_re = _lazy_re_compile( -@@ -586,7 +591,7 @@ def get_language_from_request(request, check_path=False): - - - @functools.lru_cache(maxsize=1000) --def parse_accept_lang_header(lang_string): -+def _parse_accept_lang_header(lang_string): - """ - Parse the lang_string, which is the body of an HTTP Accept-Language - header, and return a tuple of (lang, q-value), ordered by 'q' values. -@@ -608,3 +613,27 @@ def parse_accept_lang_header(lang_string): - result.append((lang, priority)) - result.sort(key=lambda k: k[1], reverse=True) - return tuple(result) -+ -+ -+def parse_accept_lang_header(lang_string): -+ """ -+ Parse the value of the Accept-Language header up to a maximum length. -+ -+ The value of the header is truncated to a maximum length to avoid potential -+ denial of service and memory exhaustion attacks. Excessive memory could be -+ used if the raw value is very large as it would be cached due to the use of -+ functools.lru_cache() to avoid repetitive parsing of common header values. -+ """ -+ # If the header value doesn't exceed the maximum allowed length, parse it. -+ if len(lang_string) <= ACCEPT_LANGUAGE_HEADER_MAX_LENGTH: -+ return _parse_accept_lang_header(lang_string) -+ -+ # If there is at least one comma in the value, parse up to the last comma -+ # before the max length, skipping any truncated parts at the end of the -+ # header value. -+ if (index := lang_string.rfind(",", 0, ACCEPT_LANGUAGE_HEADER_MAX_LENGTH)) > 0: -+ return _parse_accept_lang_header(lang_string[:index]) -+ -+ # Don't attempt to parse if there is only one language-range value which is -+ # longer than the maximum allowed length and so truncated. -+ return () -diff --git a/tests/i18n/tests.py b/tests/i18n/tests.py -index 40fc306..b361a84 100644 ---- a/tests/i18n/tests.py -+++ b/tests/i18n/tests.py -@@ -1730,6 +1730,14 @@ class MiscTests(SimpleTestCase): - ("de;q=0.", [("de", 0.0)]), - ("en; q=1,", [("en", 1.0)]), - ("en; q=1.0, * ; q=0.5", [("en", 1.0), ("*", 0.5)]), -+ ( -+ "en" + "-x" * 20, -+ [("en-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x", 1.0)], -+ ), -+ ( -+ ", ".join(["en; q=1.0"] * 20), -+ [("en", 1.0)] * 20, -+ ), - # Bad headers - ("en-gb;q=1.0000", []), - ("en;q=0.1234", []), -@@ -1746,6 +1754,10 @@ class MiscTests(SimpleTestCase): - ("", []), - ("en;q=1e0", []), - ("en-au;q=1.0", []), -+ # Invalid as language-range value too long. -+ ("xxxxxxxx" + "-xxxxxxxx" * 500, []), -+ # Header value too long, only parse up to limit. -+ (", ".join(["en; q=1.0"] * 500), [("en", 1.0)] * 45), - ] - for value, expected in tests: - with self.subTest(value=value): --- -2.30.0 - diff --git a/CVE-2023-24580.patch b/CVE-2023-24580.patch deleted file mode 100644 index 8bd5473ab67f81b78b7d519249254b12f1f52506..0000000000000000000000000000000000000000 --- a/CVE-2023-24580.patch +++ /dev/null @@ -1,415 +0,0 @@ -From 628b33a854a9c68ec8a0c51f382f304a0044ec92 Mon Sep 17 00:00:00 2001 -From: Markus Holtermann -Date: Tue, 13 Dec 2022 10:27:39 +0100 -Subject: [PATCH] [4.1.x] Fixed CVE-2023-24580 -- Prevented DoS with too many - uploaded files. - -Thanks to Jakob Ackermann for the report. ---- - django/conf/global_settings.py | 4 ++ - django/core/exceptions.py | 9 +++ - django/core/handlers/exception.py | 3 +- - django/http/multipartparser.py | 64 ++++++++++++++++----- - django/http/request.py | 8 ++- - docs/ref/exceptions.txt | 5 ++ - docs/ref/settings.txt | 23 ++++++++ - docs/releases/3.2.18.txt | 10 +++- - docs/releases/4.0.10.txt | 10 +++- - docs/releases/4.1.7.txt | 14 ++++- - tests/handlers/test_exception.py | 31 +++++++++- - tests/requests/test_data_upload_settings.py | 55 +++++++++++++++++- - 12 files changed, 213 insertions(+), 23 deletions(-) - -diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py -index 40b34bb71c90..7ac700228f37 100644 ---- a/django/conf/global_settings.py -+++ b/django/conf/global_settings.py -@@ -313,6 +313,10 @@ def gettext_noop(s): - # SuspiciousOperation (TooManyFieldsSent) is raised. - DATA_UPLOAD_MAX_NUMBER_FIELDS = 1000 - -+# Maximum number of files encoded in a multipart upload that will be read -+# before a SuspiciousOperation (TooManyFilesSent) is raised. -+DATA_UPLOAD_MAX_NUMBER_FILES = 100 -+ - # Directory in which upload streamed files will be temporarily saved. A value of - # `None` will make Django use the operating system's default temporary directory - # (i.e. "/tmp" on *nix systems). -diff --git a/django/core/exceptions.py b/django/core/exceptions.py -index 7be4e16bc55a..e06b33e7bc2d 100644 ---- a/django/core/exceptions.py -+++ b/django/core/exceptions.py -@@ -67,6 +67,15 @@ class TooManyFieldsSent(SuspiciousOperation): - pass - - -+class TooManyFilesSent(SuspiciousOperation): -+ """ -+ The number of fields in a GET or POST request exceeded -+ settings.DATA_UPLOAD_MAX_NUMBER_FILES. -+ """ -+ -+ pass -+ -+ - class RequestDataTooBig(SuspiciousOperation): - """ - The size of the request (excluding any file uploads) exceeded -diff --git a/django/core/handlers/exception.py b/django/core/handlers/exception.py -index 79577c2d0a6d..fd64584bbafb 100644 ---- a/django/core/handlers/exception.py -+++ b/django/core/handlers/exception.py -@@ -13,6 +13,7 @@ - RequestDataTooBig, - SuspiciousOperation, - TooManyFieldsSent, -+ TooManyFilesSent, - ) - from django.http import Http404 - from django.http.multipartparser import MultiPartParserError -@@ -111,7 +112,7 @@ def response_for_exception(request, exc): - exception=exc, - ) - elif isinstance(exc, SuspiciousOperation): -- if isinstance(exc, (RequestDataTooBig, TooManyFieldsSent)): -+ if isinstance(exc, (RequestDataTooBig, TooManyFieldsSent, TooManyFilesSent)): - # POST data can't be accessed again, otherwise the original - # exception would be raised. - request._mark_post_parse_error() -diff --git a/django/http/multipartparser.py b/django/http/multipartparser.py -index 26fb2bc41f86..944ca4aa6c2f 100644 ---- a/django/http/multipartparser.py -+++ b/django/http/multipartparser.py -@@ -15,6 +15,7 @@ - RequestDataTooBig, - SuspiciousMultipartForm, - TooManyFieldsSent, -+ TooManyFilesSent, - ) - from django.core.files.uploadhandler import SkipFile, StopFutureHandlers, StopUpload - from django.utils.datastructures import MultiValueDict -@@ -39,6 +40,7 @@ class InputStreamExhausted(Exception): - RAW = "raw" - FILE = "file" - FIELD = "field" -+FIELD_TYPES = frozenset([FIELD, RAW]) - - - class MultiPartParser: -@@ -111,6 +113,22 @@ def __init__(self, META, input_data, upload_handlers, encoding=None): - self._upload_handlers = upload_handlers - - def parse(self): -+ # Call the actual parse routine and close all open files in case of -+ # errors. This is needed because if exceptions are thrown the -+ # MultiPartParser will not be garbage collected immediately and -+ # resources would be kept alive. This is only needed for errors because -+ # the Request object closes all uploaded files at the end of the -+ # request. -+ try: -+ return self._parse() -+ except Exception: -+ if hasattr(self, "_files"): -+ for _, files in self._files.lists(): -+ for fileobj in files: -+ fileobj.close() -+ raise -+ -+ def _parse(self): - """ - Parse the POST data and break it into a FILES MultiValueDict and a POST - MultiValueDict. -@@ -156,6 +174,8 @@ def parse(self): - num_bytes_read = 0 - # To count the number of keys in the request. - num_post_keys = 0 -+ # To count the number of files in the request. -+ num_files = 0 - # To limit the amount of data read from the request. - read_size = None - # Whether a file upload is finished. -@@ -171,6 +191,20 @@ def parse(self): - old_field_name = None - uploaded_file = True - -+ if ( -+ item_type in FIELD_TYPES -+ and settings.DATA_UPLOAD_MAX_NUMBER_FIELDS is not None -+ ): -+ # Avoid storing more than DATA_UPLOAD_MAX_NUMBER_FIELDS. -+ num_post_keys += 1 -+ # 2 accounts for empty raw fields before and after the -+ # last boundary. -+ if settings.DATA_UPLOAD_MAX_NUMBER_FIELDS + 2 < num_post_keys: -+ raise TooManyFieldsSent( -+ "The number of GET/POST parameters exceeded " -+ "settings.DATA_UPLOAD_MAX_NUMBER_FIELDS." -+ ) -+ - try: - disposition = meta_data["content-disposition"][1] - field_name = disposition["name"].strip() -@@ -183,17 +217,6 @@ def parse(self): - field_name = force_str(field_name, encoding, errors="replace") - - if item_type == FIELD: -- # Avoid storing more than DATA_UPLOAD_MAX_NUMBER_FIELDS. -- num_post_keys += 1 -- if ( -- settings.DATA_UPLOAD_MAX_NUMBER_FIELDS is not None -- and settings.DATA_UPLOAD_MAX_NUMBER_FIELDS < num_post_keys -- ): -- raise TooManyFieldsSent( -- "The number of GET/POST parameters exceeded " -- "settings.DATA_UPLOAD_MAX_NUMBER_FIELDS." -- ) -- - # Avoid reading more than DATA_UPLOAD_MAX_MEMORY_SIZE. - if settings.DATA_UPLOAD_MAX_MEMORY_SIZE is not None: - read_size = ( -@@ -228,6 +251,16 @@ def parse(self): - field_name, force_str(data, encoding, errors="replace") - ) - elif item_type == FILE: -+ # Avoid storing more than DATA_UPLOAD_MAX_NUMBER_FILES. -+ num_files += 1 -+ if ( -+ settings.DATA_UPLOAD_MAX_NUMBER_FILES is not None -+ and num_files > settings.DATA_UPLOAD_MAX_NUMBER_FILES -+ ): -+ raise TooManyFilesSent( -+ "The number of files exceeded " -+ "settings.DATA_UPLOAD_MAX_NUMBER_FILES." -+ ) - # This is a file, use the handler... - file_name = disposition.get("filename") - if file_name: -@@ -305,8 +338,13 @@ def parse(self): - # Handle file upload completions on next iteration. - old_field_name = field_name - else: -- # If this is neither a FIELD or a FILE, just exhaust the stream. -- exhaust(stream) -+ # If this is neither a FIELD nor a FILE, exhaust the field -+ # stream. Note: There could be an error here at some point, -+ # but there will be at least two RAW types (before and -+ # after the other boundaries). This branch is usually not -+ # reached at all, because a missing content-disposition -+ # header will skip the whole boundary. -+ exhaust(field_stream) - except StopUpload as e: - self._close_files() - if not e.connection_reset: -diff --git a/django/http/request.py b/django/http/request.py -index 4b160bc5f4e9..0789b24c154e 100644 ---- a/django/http/request.py -+++ b/django/http/request.py -@@ -13,7 +13,11 @@ - TooManyFieldsSent, - ) - from django.core.files import uploadhandler --from django.http.multipartparser import MultiPartParser, MultiPartParserError -+from django.http.multipartparser import ( -+ MultiPartParser, -+ MultiPartParserError, -+ TooManyFilesSent, -+) - from django.utils.datastructures import ( - CaseInsensitiveMapping, - ImmutableList, -@@ -367,7 +371,7 @@ def _load_post_and_files(self): - data = self - try: - self._post, self._files = self.parse_file_upload(self.META, data) -- except MultiPartParserError: -+ except (MultiPartParserError, TooManyFilesSent): - # An error occurred while parsing POST data. Since when - # formatting the error the request handler might access - # self.POST, set self._post and self._file to prevent -diff --git a/docs/ref/exceptions.txt b/docs/ref/exceptions.txt -index 2b567414e6d0..a2bf41499b0d 100644 ---- a/docs/ref/exceptions.txt -+++ b/docs/ref/exceptions.txt -@@ -84,12 +84,17 @@ Django core exception classes are defined in ``django.core.exceptions``. - * ``SuspiciousMultipartForm`` - * ``SuspiciousSession`` - * ``TooManyFieldsSent`` -+ * ``TooManyFilesSent`` - - If a ``SuspiciousOperation`` exception reaches the ASGI/WSGI handler level - it is logged at the ``Error`` level and results in - a :class:`~django.http.HttpResponseBadRequest`. See the :doc:`logging - documentation ` for more information. - -+.. versionchanged:: 3.2.18 -+ -+ ``SuspiciousOperation`` is raised when too many files are submitted. -+ - ``PermissionDenied`` - -------------------- - -diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt -index a61612f49ba2..218de8f1d76a 100644 ---- a/docs/ref/settings.txt -+++ b/docs/ref/settings.txt -@@ -1108,6 +1108,28 @@ could be used as a denial-of-service attack vector if left unchecked. Since web - servers don't typically perform deep request inspection, it's not possible to - perform a similar check at that level. - -+.. setting:: DATA_UPLOAD_MAX_NUMBER_FILES -+ -+``DATA_UPLOAD_MAX_NUMBER_FILES`` -+-------------------------------- -+ -+.. versionadded:: 3.2.18 -+ -+Default: ``100`` -+ -+The maximum number of files that may be received via POST in a -+``multipart/form-data`` encoded request before a -+:exc:`~django.core.exceptions.SuspiciousOperation` (``TooManyFiles``) is -+raised. You can set this to ``None`` to disable the check. Applications that -+are expected to receive an unusually large number of file fields should tune -+this setting. -+ -+The number of accepted files is correlated to the amount of time and memory -+needed to process the request. Large requests could be used as a -+denial-of-service attack vector if left unchecked. Since web servers don't -+typically perform deep request inspection, it's not possible to perform a -+similar check at that level. -+ - .. setting:: DATABASE_ROUTERS - - ``DATABASE_ROUTERS`` -@@ -3727,6 +3749,7 @@ HTTP - ---- - * :setting:`DATA_UPLOAD_MAX_MEMORY_SIZE` - * :setting:`DATA_UPLOAD_MAX_NUMBER_FIELDS` -+* :setting:`DATA_UPLOAD_MAX_NUMBER_FILES` - * :setting:`DEFAULT_CHARSET` - * :setting:`DISALLOWED_USER_AGENTS` - * :setting:`FORCE_SCRIPT_NAME` -diff --git a/tests/handlers/test_exception.py b/tests/handlers/test_exception.py -index 3a483be78441..878fff7cc0c8 100644 ---- a/tests/handlers/test_exception.py -+++ b/tests/handlers/test_exception.py -@@ -1,6 +1,11 @@ - from django.core.handlers.wsgi import WSGIHandler - from django.test import SimpleTestCase, override_settings --from django.test.client import FakePayload -+from django.test.client import ( -+ BOUNDARY, -+ MULTIPART_CONTENT, -+ FakePayload, -+ encode_multipart, -+) - - - class ExceptionHandlerTests(SimpleTestCase): -@@ -24,3 +29,27 @@ def test_data_upload_max_memory_size_exceeded(self): - def test_data_upload_max_number_fields_exceeded(self): - response = WSGIHandler()(self.get_suspicious_environ(), lambda *a, **k: None) - self.assertEqual(response.status_code, 400) -+ -+ @override_settings(DATA_UPLOAD_MAX_NUMBER_FILES=2) -+ def test_data_upload_max_number_files_exceeded(self): -+ payload = FakePayload( -+ encode_multipart( -+ BOUNDARY, -+ { -+ "a.txt": "Hello World!", -+ "b.txt": "Hello Django!", -+ "c.txt": "Hello Python!", -+ }, -+ ) -+ ) -+ environ = { -+ "REQUEST_METHOD": "POST", -+ "CONTENT_TYPE": MULTIPART_CONTENT, -+ "CONTENT_LENGTH": len(payload), -+ "wsgi.input": payload, -+ "SERVER_NAME": "test", -+ "SERVER_PORT": "8000", -+ } -+ -+ response = WSGIHandler()(environ, lambda *a, **k: None) -+ self.assertEqual(response.status_code, 400) -diff --git a/tests/requests/test_data_upload_settings.py b/tests/requests/test_data_upload_settings.py -index 0199296293d9..e89af0a39b82 100644 ---- a/tests/requests/test_data_upload_settings.py -+++ b/tests/requests/test_data_upload_settings.py -@@ -1,6 +1,10 @@ - from io import BytesIO - --from django.core.exceptions import RequestDataTooBig, TooManyFieldsSent -+from django.core.exceptions import ( -+ RequestDataTooBig, -+ TooManyFieldsSent, -+ TooManyFilesSent, -+) - from django.core.handlers.wsgi import WSGIRequest - from django.test import SimpleTestCase - from django.test.client import FakePayload -@@ -8,6 +12,9 @@ - TOO_MANY_FIELDS_MSG = ( - "The number of GET/POST parameters exceeded settings.DATA_UPLOAD_MAX_NUMBER_FIELDS." - ) -+TOO_MANY_FILES_MSG = ( -+ "The number of files exceeded settings.DATA_UPLOAD_MAX_NUMBER_FILES." -+) - TOO_MUCH_DATA_MSG = "Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE." - - -@@ -191,6 +198,52 @@ def test_no_limit(self): - self.request._load_post_and_files() - - -+class DataUploadMaxNumberOfFilesMultipartPost(SimpleTestCase): -+ def setUp(self): -+ payload = FakePayload( -+ "\r\n".join( -+ [ -+ "--boundary", -+ ( -+ 'Content-Disposition: form-data; name="name1"; ' -+ 'filename="name1.txt"' -+ ), -+ "", -+ "value1", -+ "--boundary", -+ ( -+ 'Content-Disposition: form-data; name="name2"; ' -+ 'filename="name2.txt"' -+ ), -+ "", -+ "value2", -+ "--boundary--", -+ ] -+ ) -+ ) -+ self.request = WSGIRequest( -+ { -+ "REQUEST_METHOD": "POST", -+ "CONTENT_TYPE": "multipart/form-data; boundary=boundary", -+ "CONTENT_LENGTH": len(payload), -+ "wsgi.input": payload, -+ } -+ ) -+ -+ def test_number_exceeded(self): -+ with self.settings(DATA_UPLOAD_MAX_NUMBER_FILES=1): -+ with self.assertRaisesMessage(TooManyFilesSent, TOO_MANY_FILES_MSG): -+ self.request._load_post_and_files() -+ -+ def test_number_not_exceeded(self): -+ with self.settings(DATA_UPLOAD_MAX_NUMBER_FILES=2): -+ self.request._load_post_and_files() -+ -+ def test_no_limit(self): -+ with self.settings(DATA_UPLOAD_MAX_NUMBER_FILES=None): -+ self.request._load_post_and_files() -+ -+ - class DataUploadMaxNumberOfFieldsFormPost(SimpleTestCase): - def setUp(self): - payload = FakePayload("\r\n".join(["a=1&a=2&a=3", ""])) diff --git a/4.1.4.tar.gz b/django-4.1.7.tar.gz similarity index 57% rename from 4.1.4.tar.gz rename to django-4.1.7.tar.gz index 6cbcdef860c2e5be2b71fa1461eebf7c54c998ad..33db06a10f8a2c18320dad1fa3c56715c4b8e004 100644 Binary files a/4.1.4.tar.gz and b/django-4.1.7.tar.gz differ diff --git a/python-django.spec b/python-django.spec index a61179b2f2d419eb1f5149ced895b9811bf74d1a..074754250ebcabb235b050b9d98a0b9ded79f9ce 100644 --- a/python-django.spec +++ b/python-django.spec @@ -1,16 +1,11 @@ %global _empty_manifest_terminate_build 0 Name: python-django -Version: 4.1.4 -Release: 3 +Version: 4.1.7 +Release: 1 Summary: A high-level Python Web framework that encourages rapid development and clean, pragmatic design. License: Apache-2.0 and Python-2.0 and BSD-3-Clause URL: https://www.djangoproject.com/ -Source0: https://github.com/django/django/archive/refs/tags/4.1.4.tar.gz -#https://github.com/django/django/commit/a9010fe5555e6086a9d9ae50069579400ef0685e -Patch0: CVE-2022-34265.patch -Patch1: CVE-2023-23969.patch -Patch2: CVE-2023-24580.patch - +Source0: https://github.com/django/django/archive/%{version}/django-%{version}.tar.gz BuildArch: noarch %description @@ -77,6 +72,9 @@ mv %{buildroot}/doclist.lst . %{_docdir}/* %changelog +* Tue Apr 11 2023 yaoxin - 4.1.7-1 +- Update to 4.1.7 + * Sat Feb 25 2023 yaoxin - 4.1.4-3 - Fix CVE-2023-24580