From a22866244736345239909eaca7be2eb8da791997 Mon Sep 17 00:00:00 2001 From: Viktor Chuchurski Date: Thu, 25 Jul 2024 19:34:35 +0200 Subject: [PATCH 1/6] - added output encoding in redirect HTML --- src/twisted/web/_template_util.py | 2 +- src/twisted/web/newsfragments/12263.bugfix | 1 + src/twisted/web/newsfragments/9839.bugfix | 1 + src/twisted/web/test/test_util.py | 39 +++++++++++++++++++++- 4 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 src/twisted/web/newsfragments/12263.bugfix create mode 100644 src/twisted/web/newsfragments/9839.bugfix diff --git a/src/twisted/web/_template_util.py b/src/twisted/web/_template_util.py index 38ebbed..c6f7e9d 100644 --- a/src/twisted/web/_template_util.py +++ b/src/twisted/web/_template_util.py @@ -92,7 +92,7 @@ def redirectTo(URL: bytes, request: IRequest) -> bytes: """ % { - b"url": URL + b"url": escape(URL.decode("utf-8")).encode("utf-8") } return content diff --git a/src/twisted/web/newsfragments/12263.bugfix b/src/twisted/web/newsfragments/12263.bugfix new file mode 100644 index 0000000..b3982ca --- /dev/null +++ b/src/twisted/web/newsfragments/12263.bugfix @@ -0,0 +1 @@ +twisted.web.util.redirectTo now HTML-escapes the provided URL in the fallback response body it returns (GHSA-cf56-g6w6-pqq2). The issue is being tracked with CVE-2024-41810. \ No newline at end of file diff --git a/src/twisted/web/newsfragments/9839.bugfix b/src/twisted/web/newsfragments/9839.bugfix new file mode 100644 index 0000000..1e2e7f7 --- /dev/null +++ b/src/twisted/web/newsfragments/9839.bugfix @@ -0,0 +1 @@ +twisted.web.util.redirectTo now HTML-escapes the provided URL in the fallback response body it returns (GHSA-cf56-g6w6-pqq2, CVE-2024-41810). diff --git a/src/twisted/web/test/test_util.py b/src/twisted/web/test/test_util.py index 996b0d0..87282ce 100644 --- a/src/twisted/web/test/test_util.py +++ b/src/twisted/web/test/test_util.py @@ -5,7 +5,6 @@ Tests for L{twisted.web.util}. """ - import gc from twisted.internet import defer @@ -64,6 +63,44 @@ class RedirectToTests(TestCase): targetURL = "http://target.example.com/4321" self.assertRaises(TypeError, redirectTo, targetURL, request) + def test_legitimateRedirect(self): + """ + Legitimate URLs are fully interpolated in the `redirectTo` response body without transformation + """ + request = DummyRequest([b""]) + html = redirectTo(b"https://twisted.org/", request) + expected = b""" + + + + + + click here + + +""" + self.assertEqual(html, expected) + + def test_maliciousRedirect(self): + """ + Malicious URLs are HTML-escaped before interpolating them in the `redirectTo` response body + """ + request = DummyRequest([b""]) + html = redirectTo( + b'https://twisted.org/">', request + ) + expected = b""" + + + + + + click here + + +""" + self.assertEqual(html, expected) + class ParentRedirectTests(SynchronousTestCase): """ -- 2.41.0