Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Step:15-SP4
python-Django.35187
CVE-2024-38875.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File CVE-2024-38875.patch of Package python-Django.35187
From 5c13dd367d46ec9efaf70ba15d87c8dfff837aea Mon Sep 17 00:00:00 2001 From: nkrapp <nico.krapp@suse.com> Date: Fri, 26 Jul 2024 11:19:09 +0200 Subject: [PATCH] [4.2.x] Fixed CVE-2024-38875 -- Mitigated potential DoS in urlize and urlizetrunc template filters. --- django/utils/html.py | 73 +++++++++++++++++++++++++++++----- tests/utils_tests/test_html.py | 16 +++++++- 2 files changed, 77 insertions(+), 12 deletions(-) diff --git a/django/utils/html.py b/django/utils/html.py index 1ef0b39cdc..517a72372d 100644 --- a/django/utils/html.py +++ b/django/utils/html.py @@ -1,5 +1,6 @@ """HTML utilities suitable for global use.""" +import html import re from urllib.parse import ( parse_qsl, quote, unquote, urlencode, urlsplit, urlunsplit, @@ -217,6 +218,16 @@ def smart_urlquote(url): return urlunsplit((scheme, netloc, path, query, fragment)) +class CountsDict(dict): + def __init__(self, *args, word, **kwargs): + super().__init__(*args, *kwargs) + self.word = word + + def __missing__(self, key): + self[key] = self.word.count(key) + return self[key] + + @keep_lazy_text def urlize(text, trim_url_limit=None, nofollow=False, autoescape=False): """ @@ -241,6 +252,16 @@ def urlize(text, trim_url_limit=None, nofollow=False, autoescape=False): return x return '%s...' % x[:max(0, limit - 3)] + def wrapping_punctuation_openings(): + return "".join(dict(WRAPPING_PUNCTUATION).keys()) + + def trailing_punctuation_chars_no_semicolon(): + return TRAILING_PUNCTUATION_CHARS.replace(";", "") + + def trailing_punctuation_chars_has_semicolon(): + return ";" in TRAILING_PUNCTUATION_CHARS + + def unescape(text, trail): """ If input URL is HTML-escaped, unescape it so that it can be safely fed @@ -264,9 +285,15 @@ def urlize(text, trim_url_limit=None, nofollow=False, autoescape=False): Trim trailing and wrapping punctuation from `middle`. Return the items of the new state. """ + # Strip all opening wrapping punctuation. + middle = word.lstrip(wrapping_punctuation_openings()) + lead = word[: len(word) - len(middle)] + trail = "" + # Continue trimming until middle remains unchanged. trimmed_something = True - while trimmed_something: + counts = CountsDict(word=middle) + while trimmed_something and middle: trimmed_something = False # Trim trailing punctuation. @@ -278,15 +305,41 @@ def urlize(text, trim_url_limit=None, nofollow=False, autoescape=False): # Trim wrapping punctuation. for opening, closing in WRAPPING_PUNCTUATION: - if middle.startswith(opening): - middle = middle[len(opening):] - lead += opening - trimmed_something = True - # Keep parentheses at the end only if they're balanced. - if (middle.endswith(closing) and - middle.count(closing) == middle.count(opening) + 1): - middle = middle[:-len(closing)] - trail = closing + trail + if counts[opening] < counts[closing]: + rstripped = middle.rstrip(closing) + if rstripped != middle: + strip = counts[closing] - counts[opening] + trail = middle[-strip:] + middle = middle[:-strip] + trimmed_something = True + counts[closing] -= strip + + rstripped = middle.rstrip(trailing_punctuation_chars_no_semicolon()) + if rstripped != middle: + trail = middle[len(rstripped) :] + trail + middle = rstripped + trimmed_something = True + + if trailing_punctuation_chars_has_semicolon() and middle.endswith(";"): + # Only strip if not part of an HTML entity. + amp = middle.rfind("&") + if amp == -1: + can_strip = True + else: + potential_entity = middle[amp:] + escaped = html.unescape(potential_entity) + can_strip = (escaped == potential_entity) or escaped.endswith(";") + + if can_strip: + rstripped = middle.rstrip(";") + amount_stripped = len(middle) - len(rstripped) + if amp > -1 and amount_stripped > 1: + # Leave a trailing semicolon as might be an entity. + trail = middle[len(rstripped) + 1 :] + trail + middle = rstripped + ";" + else: + trail = middle[len(rstripped) :] + trail + middle = rstripped trimmed_something = True return lead, middle, trail diff --git a/tests/utils_tests/test_html.py b/tests/utils_tests/test_html.py index 077729b069..5f0eefb99e 100644 --- a/tests/utils_tests/test_html.py +++ b/tests/utils_tests/test_html.py @@ -219,8 +219,20 @@ class TestUtilsHtml(SimpleTestCase): def test_urlize_unchanged_inputs(self): tests = ( - ('a' + '@a' * 50000) + 'a', # simple_email_re catastrophic test - ('a' + '.' * 1000000) + 'a', # trailing_punctuation catastrophic test + ("a" + "@a" * 50000) + "a", # simple_email_re catastrophic test + ("a" + "." * 1000000) + "a", # trailing_punctuation catastrophic test + "foo@", + "@foo.com", + "foo@.example.com", + "foo@localhost", + "foo@localhost.", + # trim_punctuation catastrophic tests + "(" * 100_000 + ":" + ")" * 100_000, + "(" * 100_000 + "&:" + ")" * 100_000, + "([" * 100_000 + ":" + "])" * 100_000, + "[(" * 100_000 + ":" + ")]" * 100_000, + "([[" * 100_000 + ":" + "]])" * 100_000, + "&:" + ";" * 100_000, ) for value in tests: with self.subTest(value=value): -- 2.45.2
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor