+71
-5
lines changedFilter options
+71
-5
lines changed Original file line number Diff line number Diff line change
@@ -32,9 +32,10 @@
32
32
CONTEXT_SEPARATOR = "\x04"
33
33
34
34
# Maximum number of characters that will be parsed from the Accept-Language
35
-
# header to prevent possible denial of service or memory exhaustion attacks.
36
-
# About 10x longer than the longest value shown on MDN’s Accept-Language page.
37
-
ACCEPT_LANGUAGE_HEADER_MAX_LENGTH = 500
35
+
# header or cookie to prevent possible denial of service or memory exhaustion
36
+
# attacks. About 10x longer than the longest value shown on MDN’s
37
+
# Accept-Language page.
38
+
LANGUAGE_CODE_MAX_LENGTH = 500
38
39
39
40
# Format of Accept-Language header values. From RFC 9110 Sections 12.4.2 and
40
41
# 12.5.4, and RFC 5646 Section 2.1.
@@ -498,11 +499,25 @@ def get_supported_language_variant(lang_code, strict=False):
498
499
If `strict` is False (the default), look for a country-specific variant
499
500
when neither the language code nor its generic variant is found.
500
501
502
+
The language code is truncated to a maximum length to avoid potential
503
+
denial of service attacks.
504
+
501
505
lru_cache should have a maxsize to prevent from memory exhaustion attacks,
502
506
as the provided language codes are taken from the HTTP request. See also
503
507
<https://www.djangoproject.com/weblog/2007/oct/26/security-fix/>.
504
508
"""
505
509
if lang_code:
510
+
# Truncate the language code to a maximum length to avoid potential
511
+
# denial of service attacks.
512
+
if len(lang_code) > LANGUAGE_CODE_MAX_LENGTH:
513
+
if (
514
+
not strict
515
+
and (index := lang_code.rfind("-", 0, LANGUAGE_CODE_MAX_LENGTH)) > 0
516
+
):
517
+
# There is a generic variant under the maximum length accepted length.
518
+
lang_code = lang_code[:index]
519
+
else:
520
+
raise ValueError("'lang_code' exceeds the maximum accepted length")
506
521
# If 'zh-hant-tw' is not supported, try special fallback or subsequent
507
522
# language codes i.e. 'zh-hant' and 'zh'.
508
523
possible_lang_codes = [lang_code]
@@ -626,13 +641,13 @@ def parse_accept_lang_header(lang_string):
626
641
functools.lru_cache() to avoid repetitive parsing of common header values.
627
642
"""
628
643
# If the header value doesn't exceed the maximum allowed length, parse it.
629
-
if len(lang_string) <= ACCEPT_LANGUAGE_HEADER_MAX_LENGTH:
644
+
if len(lang_string) <= LANGUAGE_CODE_MAX_LENGTH:
630
645
return _parse_accept_lang_header(lang_string)
631
646
632
647
# If there is at least one comma in the value, parse up to the last comma
633
648
# before the max length, skipping any truncated parts at the end of the
634
649
# header value.
635
-
if (index := lang_string.rfind(",", 0, ACCEPT_LANGUAGE_HEADER_MAX_LENGTH)) > 0:
650
+
if (index := lang_string.rfind(",", 0, LANGUAGE_CODE_MAX_LENGTH)) > 0:
636
651
return _parse_accept_lang_header(lang_string[:index])
637
652
638
653
# Don't attempt to parse if there is only one language-range value which is
Original file line number Diff line number Diff line change
@@ -1113,6 +1113,11 @@ For a complete discussion on the usage of the following see the
1113
1113
``lang_code`` is ``'es-ar'`` and ``'es'`` is in :setting:`LANGUAGES` but
1114
1114
``'es-ar'`` isn't.
1115
1115
1116
+
``lang_code`` has a maximum accepted length of 500 characters. A
1117
+
:exc:`ValueError` is raised if ``lang_code`` exceeds this limit and
1118
+
``strict`` is ``True``, or if there is no generic variant and ``strict``
1119
+
is ``False``.
1120
+
1116
1121
If ``strict`` is ``False`` (the default), a country-specific variant may
1117
1122
be returned when neither the language code nor its generic variant is found.
1118
1123
For example, if only ``'es-co'`` is in :setting:`LANGUAGES`, that's
@@ -1121,6 +1126,11 @@ For a complete discussion on the usage of the following see the
1121
1126
1122
1127
Raises :exc:`LookupError` if nothing is found.
1123
1128
1129
+
.. versionchanged:: 4.2.14
1130
+
1131
+
In older versions, ``lang_code`` values over 500 characters were
1132
+
processed without raising a :exc:`ValueError`.
1133
+
1124
1134
.. function:: to_locale(language)
1125
1135
1126
1136
Turns a language name (en-us) into a locale name (en_US).
Original file line number Diff line number Diff line change
@@ -32,3 +32,18 @@ directory-traversal via certain inputs when calling :meth:`save()
32
32
<django.core.files.storage.Storage.save()>`.
33
33
34
34
Built-in ``Storage`` sub-classes were not affected by this vulnerability.
35
+
36
+
CVE-2024-39614: Potential denial-of-service vulnerability in ``get_supported_language_variant()``
37
+
=================================================================================================
38
+
39
+
:meth:`~django.utils.translation.get_supported_language_variant` was subject to
40
+
a potential denial-of-service attack when used with very long strings
41
+
containing specific characters.
42
+
43
+
To mitigate this vulnerability, the language code provided to
44
+
:meth:`~django.utils.translation.get_supported_language_variant` is now parsed
45
+
up to a maximum length of 500 characters.
46
+
47
+
When the language code is over 500 characters, a :exc:`ValueError` will now be
48
+
raised if ``strict`` is ``True``, or if there is no generic variant and
49
+
``strict`` is ``False``.
Original file line number Diff line number Diff line change
@@ -33,6 +33,21 @@ directory-traversal via certain inputs when calling :meth:`save()
33
33
34
34
Built-in ``Storage`` sub-classes were not affected by this vulnerability.
35
35
36
+
CVE-2024-39614: Potential denial-of-service vulnerability in ``get_supported_language_variant()``
37
+
=================================================================================================
38
+
39
+
:meth:`~django.utils.translation.get_supported_language_variant` was subject to
40
+
a potential denial-of-service attack when used with very long strings
41
+
containing specific characters.
42
+
43
+
To mitigate this vulnerability, the language code provided to
44
+
:meth:`~django.utils.translation.get_supported_language_variant` is now parsed
45
+
up to a maximum length of 500 characters.
46
+
47
+
When the language code is over 500 characters, a :exc:`ValueError` will now be
48
+
raised if ``strict`` is ``True``, or if there is no generic variant and
49
+
``strict`` is ``False``.
50
+
36
51
Bugfixes
37
52
========
38
53
Original file line number Diff line number Diff line change
@@ -58,6 +58,7 @@
58
58
translation_file_changed,
59
59
watch_for_translation_changes,
60
60
)
61
+
from django.utils.translation.trans_real import LANGUAGE_CODE_MAX_LENGTH
61
62
62
63
from .forms import CompanyForm, I18nForm, SelectDateForm
63
64
from .models import Company, TestModel
@@ -1672,6 +1673,16 @@ def test_get_supported_language_variant_real(self):
1672
1673
g("xyz")
1673
1674
with self.assertRaises(LookupError):
1674
1675
g("xy-zz")
1676
+
msg = "'lang_code' exceeds the maximum accepted length"
1677
+
with self.assertRaises(LookupError):
1678
+
g("x" * LANGUAGE_CODE_MAX_LENGTH)
1679
+
with self.assertRaisesMessage(ValueError, msg):
1680
+
g("x" * (LANGUAGE_CODE_MAX_LENGTH + 1))
1681
+
# 167 * 3 = 501 which is LANGUAGE_CODE_MAX_LENGTH + 1.
1682
+
self.assertEqual(g("en-" * 167), "en")
1683
+
with self.assertRaisesMessage(ValueError, msg):
1684
+
g("en-" * 167, strict=True)
1685
+
self.assertEqual(g("en-" * 30000), "en") # catastrophic test
1675
1686
1676
1687
def test_get_supported_language_variant_null(self):
1677
1688
g = trans_null.get_supported_language_variant
You can’t perform that action at this time.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4