diff options
| -rw-r--r-- | meta-python/recipes-devtools/python/python3-django/CVE-2024-38875.patch | 161 | ||||
| -rw-r--r-- | meta-python/recipes-devtools/python/python3-django_2.2.28.bb | 1 |
2 files changed, 162 insertions, 0 deletions
diff --git a/meta-python/recipes-devtools/python/python3-django/CVE-2024-38875.patch b/meta-python/recipes-devtools/python/python3-django/CVE-2024-38875.patch new file mode 100644 index 0000000000..8ccb888c60 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-django/CVE-2024-38875.patch | |||
| @@ -0,0 +1,161 @@ | |||
| 1 | From 79f368764295df109a37192f6182fb6f361d85b5 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Adam Johnson <me@adamj.eu> | ||
| 3 | Date: Mon, 24 Jun 2024 15:30:59 +0200 | ||
| 4 | Subject: [PATCH] [4.2.x] Fixed CVE-2024-38875 -- Mitigated potential DoS in | ||
| 5 | urlize and urlizetrunc template filters. | ||
| 6 | |||
| 7 | Thank you to Elias Myllymäki for the report. | ||
| 8 | |||
| 9 | Co-authored-by: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com> | ||
| 10 | |||
| 11 | CVE: CVE-2024-38875 | ||
| 12 | |||
| 13 | Upstream-Status: Backport [https://github.com/django/django/commit/79f368764295df109a37192f6182fb6f361d85b5] | ||
| 14 | |||
| 15 | Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com> | ||
| 16 | --- | ||
| 17 | django/utils/html.py | 90 +++++++++++++++++++++++++--------- | ||
| 18 | tests/utils_tests/test_html.py | 7 +++ | ||
| 19 | 2 files changed, 73 insertions(+), 21 deletions(-) | ||
| 20 | |||
| 21 | diff --git a/django/utils/html.py b/django/utils/html.py | ||
| 22 | index 7a33d5f..f1b74ab 100644 | ||
| 23 | --- a/django/utils/html.py | ||
| 24 | +++ b/django/utils/html.py | ||
| 25 | @@ -234,6 +234,15 @@ def smart_urlquote(url): | ||
| 26 | |||
| 27 | return urlunsplit((scheme, netloc, path, query, fragment)) | ||
| 28 | |||
| 29 | +class CountsDict(dict): | ||
| 30 | + def __init__(self, *args, word, **kwargs): | ||
| 31 | + super().__init__(*args, *kwargs) | ||
| 32 | + self.word = word | ||
| 33 | + | ||
| 34 | + def __missing__(self, key): | ||
| 35 | + self[key] = self.word.count(key) | ||
| 36 | + return self[key] | ||
| 37 | + | ||
| 38 | |||
| 39 | @keep_lazy_text | ||
| 40 | def urlize(text, trim_url_limit=None, nofollow=False, autoescape=False): | ||
| 41 | @@ -268,36 +277,69 @@ def urlize(text, trim_url_limit=None, nofollow=False, autoescape=False): | ||
| 42 | return text.replace('&', '&').replace('<', '<').replace( | ||
| 43 | '>', '>').replace('"', '"').replace(''', "'") | ||
| 44 | |||
| 45 | - def trim_punctuation(lead, middle, trail): | ||
| 46 | + def wrapping_punctuation_openings(): | ||
| 47 | + return "".join(dict(WRAPPING_PUNCTUATION).keys()) | ||
| 48 | + | ||
| 49 | + def trailing_punctuation_chars_no_semicolon(): | ||
| 50 | + return TRAILING_PUNCTUATION_CHARS.replace(";", "") | ||
| 51 | + | ||
| 52 | + def trailing_punctuation_chars_has_semicolon(): | ||
| 53 | + return ";" in TRAILING_PUNCTUATION_CHARS | ||
| 54 | + | ||
| 55 | + def trim_punctuation(word): | ||
| 56 | """ | ||
| 57 | Trim trailing and wrapping punctuation from `middle`. Return the items | ||
| 58 | of the new state. | ||
| 59 | """ | ||
| 60 | + # Strip all opening wrapping punctuation. | ||
| 61 | + middle = word.lstrip(wrapping_punctuation_openings()) | ||
| 62 | + lead = word[: len(word) - len(middle)] | ||
| 63 | + trail = "" | ||
| 64 | + | ||
| 65 | # Continue trimming until middle remains unchanged. | ||
| 66 | trimmed_something = True | ||
| 67 | - while trimmed_something: | ||
| 68 | + counts = CountsDict(word=middle) | ||
| 69 | + while trimmed_something and middle: | ||
| 70 | trimmed_something = False | ||
| 71 | # Trim wrapping punctuation. | ||
| 72 | for opening, closing in WRAPPING_PUNCTUATION: | ||
| 73 | - if middle.startswith(opening): | ||
| 74 | - middle = middle[len(opening):] | ||
| 75 | - lead += opening | ||
| 76 | - trimmed_something = True | ||
| 77 | - # Keep parentheses at the end only if they're balanced. | ||
| 78 | - if (middle.endswith(closing) and | ||
| 79 | - middle.count(closing) == middle.count(opening) + 1): | ||
| 80 | - middle = middle[:-len(closing)] | ||
| 81 | - trail = closing + trail | ||
| 82 | - trimmed_something = True | ||
| 83 | - # Trim trailing punctuation (after trimming wrapping punctuation, | ||
| 84 | - # as encoded entities contain ';'). Unescape entites to avoid | ||
| 85 | - # breaking them by removing ';'. | ||
| 86 | - middle_unescaped = unescape(middle) | ||
| 87 | - stripped = middle_unescaped.rstrip(TRAILING_PUNCTUATION_CHARS) | ||
| 88 | - if middle_unescaped != stripped: | ||
| 89 | - trail = middle[len(stripped):] + trail | ||
| 90 | - middle = middle[:len(stripped) - len(middle_unescaped)] | ||
| 91 | + if counts[opening] < counts[closing]: | ||
| 92 | + rstripped = middle.rstrip(closing) | ||
| 93 | + if rstripped != middle: | ||
| 94 | + strip = counts[closing] - counts[opening] | ||
| 95 | + trail = middle[-strip:] | ||
| 96 | + middle = middle[:-strip] | ||
| 97 | + trimmed_something = True | ||
| 98 | + counts[closing] -= strip | ||
| 99 | + | ||
| 100 | + rstripped = middle.rstrip(trailing_punctuation_chars_no_semicolon()) | ||
| 101 | + if rstripped != middle: | ||
| 102 | + trail = middle[len(rstripped) :] + trail | ||
| 103 | + middle = rstripped | ||
| 104 | trimmed_something = True | ||
| 105 | + | ||
| 106 | + if trailing_punctuation_chars_has_semicolon() and middle.endswith(";"): | ||
| 107 | + # Only strip if not part of an HTML entity. | ||
| 108 | + amp = middle.rfind("&") | ||
| 109 | + if amp == -1: | ||
| 110 | + can_strip = True | ||
| 111 | + else: | ||
| 112 | + potential_entity = middle[amp:] | ||
| 113 | + escaped = unescape(potential_entity) | ||
| 114 | + can_strip = (escaped == potential_entity) or escaped.endswith(";") | ||
| 115 | + | ||
| 116 | + if can_strip: | ||
| 117 | + rstripped = middle.rstrip(";") | ||
| 118 | + amount_stripped = len(middle) - len(rstripped) | ||
| 119 | + if amp > -1 and amount_stripped > 1: | ||
| 120 | + # Leave a trailing semicolon as might be an entity. | ||
| 121 | + trail = middle[len(rstripped) + 1 :] + trail | ||
| 122 | + middle = rstripped + ";" | ||
| 123 | + else: | ||
| 124 | + trail = middle[len(rstripped) :] + trail | ||
| 125 | + middle = rstripped | ||
| 126 | + trimmed_something = True | ||
| 127 | + | ||
| 128 | return lead, middle, trail | ||
| 129 | |||
| 130 | def is_email_simple(value): | ||
| 131 | @@ -321,9 +363,7 @@ def urlize(text, trim_url_limit=None, no | ||
| 132 | # lead: Current punctuation trimmed from the beginning of the word. | ||
| 133 | # middle: Current state of the word. | ||
| 134 | # trail: Current punctuation trimmed from the end of the word. | ||
| 135 | - lead, middle, trail = '', word, '' | ||
| 136 | - # Deal with punctuation. | ||
| 137 | - lead, middle, trail = trim_punctuation(lead, middle, trail) | ||
| 138 | + lead, middle, trail = trim_punctuation(word) | ||
| 139 | |||
| 140 | # Make URL we want to point to. | ||
| 141 | url = None | ||
| 142 | diff --git a/tests/utils_tests/test_html.py b/tests/utils_tests/test_html.py | ||
| 143 | index 5cc2d9b..715c1c6 100644 | ||
| 144 | --- a/tests/utils_tests/test_html.py | ||
| 145 | +++ b/tests/utils_tests/test_html.py | ||
| 146 | @@ -267,6 +267,13 @@ class TestUtilsHtml(SimpleTestCase): | ||
| 147 | 'foo@.example.com', | ||
| 148 | 'foo@localhost', | ||
| 149 | 'foo@localhost.', | ||
| 150 | + # trim_punctuation catastrophic tests | ||
| 151 | + "(" * 100_000 + ":" + ")" * 100_000, | ||
| 152 | + "(" * 100_000 + "&:" + ")" * 100_000, | ||
| 153 | + "([" * 100_000 + ":" + "])" * 100_000, | ||
| 154 | + "[(" * 100_000 + ":" + ")]" * 100_000, | ||
| 155 | + "([[" * 100_000 + ":" + "]])" * 100_000, | ||
| 156 | + "&:" + ";" * 100_000, | ||
| 157 | ) | ||
| 158 | for value in tests: | ||
| 159 | with self.subTest(value=value): | ||
| 160 | -- | ||
| 161 | 2.40.0 | ||
diff --git a/meta-python/recipes-devtools/python/python3-django_2.2.28.bb b/meta-python/recipes-devtools/python/python3-django_2.2.28.bb index 7f5861f5d3..f082de9d72 100644 --- a/meta-python/recipes-devtools/python/python3-django_2.2.28.bb +++ b/meta-python/recipes-devtools/python/python3-django_2.2.28.bb | |||
| @@ -12,6 +12,7 @@ SRC_URI += "file://CVE-2023-31047.patch \ | |||
| 12 | file://CVE-2023-46695.patch \ | 12 | file://CVE-2023-46695.patch \ |
| 13 | file://CVE-2024-24680.patch \ | 13 | file://CVE-2024-24680.patch \ |
| 14 | file://CVE-2024-42005.patch \ | 14 | file://CVE-2024-42005.patch \ |
| 15 | file://CVE-2024-38875.patch \ | ||
| 15 | " | 16 | " |
| 16 | 17 | ||
| 17 | SRC_URI[sha256sum] = "0200b657afbf1bc08003845ddda053c7641b9b24951e52acd51f6abda33a7413" | 18 | SRC_URI[sha256sum] = "0200b657afbf1bc08003845ddda053c7641b9b24951e52acd51f6abda33a7413" |
