mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	[5.0.x] Fixed CVE-2024-41991 -- Prevented potential ReDoS in django.utils.html.urlize() and AdminURLFieldWidget.
Thanks Seokchan Yoon for the report. Co-authored-by: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
This commit is contained in:
		
				
					committed by
					
						 Sarah Boyce
						Sarah Boyce
					
				
			
			
				
	
			
			
			
						parent
						
							7b7b909579
						
					
				
				
					commit
					523da8771b
				
			| @@ -390,7 +390,7 @@ class AdminURLFieldWidget(forms.URLInput): | |||||||
|         context["current_label"] = _("Currently:") |         context["current_label"] = _("Currently:") | ||||||
|         context["change_label"] = _("Change:") |         context["change_label"] = _("Change:") | ||||||
|         context["widget"]["href"] = ( |         context["widget"]["href"] = ( | ||||||
|             smart_urlquote(context["widget"]["value"]) if value else "" |             smart_urlquote(context["widget"]["value"]) if url_valid else "" | ||||||
|         ) |         ) | ||||||
|         context["url_valid"] = url_valid |         context["url_valid"] = url_valid | ||||||
|         return context |         return context | ||||||
|   | |||||||
| @@ -36,6 +36,8 @@ VOID_ELEMENTS = { | |||||||
|     "spacer", |     "spacer", | ||||||
| } | } | ||||||
|  |  | ||||||
|  | MAX_URL_LENGTH = 2048 | ||||||
|  |  | ||||||
|  |  | ||||||
| @keep_lazy(SafeString) | @keep_lazy(SafeString) | ||||||
| def escape(text): | def escape(text): | ||||||
| @@ -330,9 +332,9 @@ class Urlizer: | |||||||
|             # Make URL we want to point to. |             # Make URL we want to point to. | ||||||
|             url = None |             url = None | ||||||
|             nofollow_attr = ' rel="nofollow"' if nofollow else "" |             nofollow_attr = ' rel="nofollow"' if nofollow else "" | ||||||
|             if self.simple_url_re.match(middle): |             if len(middle) <= MAX_URL_LENGTH and self.simple_url_re.match(middle): | ||||||
|                 url = smart_urlquote(html.unescape(middle)) |                 url = smart_urlquote(html.unescape(middle)) | ||||||
|             elif self.simple_url_2_re.match(middle): |             elif len(middle) <= MAX_URL_LENGTH and self.simple_url_2_re.match(middle): | ||||||
|                 url = smart_urlquote("http://%s" % html.unescape(middle)) |                 url = smart_urlquote("http://%s" % html.unescape(middle)) | ||||||
|             elif ":" not in middle and self.is_email_simple(middle): |             elif ":" not in middle and self.is_email_simple(middle): | ||||||
|                 local, domain = middle.rsplit("@", 1) |                 local, domain = middle.rsplit("@", 1) | ||||||
| @@ -447,6 +449,10 @@ class Urlizer: | |||||||
|         except ValueError: |         except ValueError: | ||||||
|             # value contains more than one @. |             # value contains more than one @. | ||||||
|             return False |             return False | ||||||
|  |         # Max length for domain name labels is 63 characters per RFC 1034. | ||||||
|  |         # Helps to avoid ReDoS vectors in the domain part. | ||||||
|  |         if len(p2) > 63: | ||||||
|  |             return False | ||||||
|         # Dot must be in p2 (e.g. example.com) |         # Dot must be in p2 (e.g. example.com) | ||||||
|         if "." not in p2 or p2.startswith("."): |         if "." not in p2 or p2.startswith("."): | ||||||
|             return False |             return False | ||||||
|   | |||||||
| @@ -23,6 +23,13 @@ CVE-2024-41990: Potential denial-of-service vulnerability in ``django.utils.html | |||||||
| denial-of-service attack via very large inputs with a specific sequence of | denial-of-service attack via very large inputs with a specific sequence of | ||||||
| characters. | characters. | ||||||
|  |  | ||||||
|  | CVE-2024-41991: Potential denial-of-service vulnerability in ``django.utils.html.urlize()`` and ``AdminURLFieldWidget`` | ||||||
|  | ======================================================================================================================= | ||||||
|  |  | ||||||
|  | :tfilter:`urlize`, :tfilter:`urlizetrunc`, and ``AdminURLFieldWidget`` were | ||||||
|  | subject to a potential denial-of-service attack via certain inputs with a very | ||||||
|  | large number of Unicode characters. | ||||||
|  |  | ||||||
| Bugfixes | Bugfixes | ||||||
| ======== | ======== | ||||||
|  |  | ||||||
|   | |||||||
| @@ -23,6 +23,13 @@ CVE-2024-41990: Potential denial-of-service vulnerability in ``django.utils.html | |||||||
| denial-of-service attack via very large inputs with a specific sequence of | denial-of-service attack via very large inputs with a specific sequence of | ||||||
| characters. | characters. | ||||||
|  |  | ||||||
|  | CVE-2024-41991: Potential denial-of-service vulnerability in ``django.utils.html.urlize()`` and ``AdminURLFieldWidget`` | ||||||
|  | ======================================================================================================================= | ||||||
|  |  | ||||||
|  | :tfilter:`urlize`, :tfilter:`urlizetrunc`, and ``AdminURLFieldWidget`` were | ||||||
|  | subject to a potential denial-of-service attack via certain inputs with a very | ||||||
|  | large number of Unicode characters. | ||||||
|  |  | ||||||
| Bugfixes | Bugfixes | ||||||
| ======== | ======== | ||||||
|  |  | ||||||
|   | |||||||
| @@ -462,7 +462,12 @@ class AdminSplitDateTimeWidgetTest(SimpleTestCase): | |||||||
| class AdminURLWidgetTest(SimpleTestCase): | class AdminURLWidgetTest(SimpleTestCase): | ||||||
|     def test_get_context_validates_url(self): |     def test_get_context_validates_url(self): | ||||||
|         w = widgets.AdminURLFieldWidget() |         w = widgets.AdminURLFieldWidget() | ||||||
|         for invalid in ["", "/not/a/full/url/", 'javascript:alert("Danger XSS!")']: |         for invalid in [ | ||||||
|  |             "", | ||||||
|  |             "/not/a/full/url/", | ||||||
|  |             'javascript:alert("Danger XSS!")', | ||||||
|  |             "http://" + "한.글." * 1_000_000 + "com", | ||||||
|  |         ]: | ||||||
|             with self.subTest(url=invalid): |             with self.subTest(url=invalid): | ||||||
|                 self.assertFalse(w.get_context("name", invalid, {})["url_valid"]) |                 self.assertFalse(w.get_context("name", invalid, {})["url_valid"]) | ||||||
|         self.assertTrue(w.get_context("name", "http://example.com", {})["url_valid"]) |         self.assertTrue(w.get_context("name", "http://example.com", {})["url_valid"]) | ||||||
|   | |||||||
| @@ -338,6 +338,15 @@ class TestUtilsHtml(SimpleTestCase): | |||||||
|                 'Search for <a href="http://google.com/?q=">google.com/?q=</a>!', |                 'Search for <a href="http://google.com/?q=">google.com/?q=</a>!', | ||||||
|             ), |             ), | ||||||
|             ("foo@example.com", '<a href="mailto:foo@example.com">foo@example.com</a>'), |             ("foo@example.com", '<a href="mailto:foo@example.com">foo@example.com</a>'), | ||||||
|  |             ( | ||||||
|  |                 "test@" + "한.글." * 15 + "aaa", | ||||||
|  |                 '<a href="mailto:test@' | ||||||
|  |                 + "xn--6q8b.xn--bj0b." * 15 | ||||||
|  |                 + 'aaa">' | ||||||
|  |                 + "test@" | ||||||
|  |                 + "한.글." * 15 | ||||||
|  |                 + "aaa</a>", | ||||||
|  |             ), | ||||||
|         ) |         ) | ||||||
|         for value, output in tests: |         for value, output in tests: | ||||||
|             with self.subTest(value=value): |             with self.subTest(value=value): | ||||||
| @@ -346,6 +355,10 @@ class TestUtilsHtml(SimpleTestCase): | |||||||
|     def test_urlize_unchanged_inputs(self): |     def test_urlize_unchanged_inputs(self): | ||||||
|         tests = ( |         tests = ( | ||||||
|             ("a" + "@a" * 50000) + "a",  # simple_email_re catastrophic test |             ("a" + "@a" * 50000) + "a",  # simple_email_re catastrophic test | ||||||
|  |             # Unicode domain catastrophic tests. | ||||||
|  |             "a@" + "한.글." * 1_000_000 + "a", | ||||||
|  |             "http://" + "한.글." * 1_000_000 + "com", | ||||||
|  |             "www." + "한.글." * 1_000_000 + "com", | ||||||
|             ("a" + "." * 1000000) + "a",  # trailing_punctuation catastrophic test |             ("a" + "." * 1000000) + "a",  # trailing_punctuation catastrophic test | ||||||
|             "foo@", |             "foo@", | ||||||
|             "@foo.com", |             "@foo.com", | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user