diff options
| author | Gyorgy Sarvari <skandigraun@gmail.com> | 2026-01-14 08:34:35 +0100 |
|---|---|---|
| committer | Gyorgy Sarvari <skandigraun@gmail.com> | 2026-01-20 18:22:07 +0100 |
| commit | 12d4f40a4a5881d2e26741fbed672fd841f557f5 (patch) | |
| tree | 4850ea52e9df531e65cda982ffb562048e518adb /meta-python | |
| parent | d29ee9b3878cbaee94b4f3b7db64adca38b67a22 (diff) | |
| download | meta-openembedded-12d4f40a4a5881d2e26741fbed672fd841f557f5.tar.gz | |
python3-twisted: patch CVE-2022-24801
Details: https://nvd.nist.gov/vuln/detail/CVE-2022-24801
Pick the commits from the pull request that is referenced by the NVD report.
(The full set is consisting of 13 patches, but the ones that only updated
news/readme/typo fixes in comments were not backported)
Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
Diffstat (limited to 'meta-python')
10 files changed, 618 insertions, 3 deletions
diff --git a/meta-python/recipes-devtools/python/python3-twisted/CVE-2022-24801-1.patch b/meta-python/recipes-devtools/python/python3-twisted/CVE-2022-24801-1.patch new file mode 100644 index 0000000000..f7b6824612 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-twisted/CVE-2022-24801-1.patch | |||
| @@ -0,0 +1,132 @@ | |||
| 1 | From 832a878c9c324ad23dde6cf16520b7768c1a8c5c Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Tom Most <twm@freecog.net> | ||
| 3 | Date: Sat, 5 Mar 2022 23:26:55 -0800 | ||
| 4 | Subject: [PATCH] Some tests for GHSA-c2jg-hw38-jrqq | ||
| 5 | |||
| 6 | Upstream-Status: Backport [https://github.com/twisted/twisted/commit/22b067793cbcd0fb5dee04cfd9115fa85a7ca110] | ||
| 7 | CVE: CVE-2022-24801 | ||
| 8 | Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com> | ||
| 9 | --- | ||
| 10 | src/twisted/web/test/test_http.py | 102 ++++++++++++++++++++++++++++++ | ||
| 11 | 1 file changed, 102 insertions(+) | ||
| 12 | |||
| 13 | diff --git a/src/twisted/web/test/test_http.py b/src/twisted/web/test/test_http.py | ||
| 14 | index 86c85d2..0549ed0 100644 | ||
| 15 | --- a/src/twisted/web/test/test_http.py | ||
| 16 | +++ b/src/twisted/web/test/test_http.py | ||
| 17 | @@ -1843,6 +1843,56 @@ class ParsingTests(unittest.TestCase): | ||
| 18 | [b"t \ta \tb"], | ||
| 19 | ) | ||
| 20 | |||
| 21 | + def test_headerStripWhitespace(self): | ||
| 22 | + """ | ||
| 23 | + Leading and trailing space and tab characters are stripped from | ||
| 24 | + headers. Other forms of whitespace are preserved. | ||
| 25 | + | ||
| 26 | + See RFC 7230 section 3.2.3 and 3.2.4. | ||
| 27 | + """ | ||
| 28 | + processed = [] | ||
| 29 | + | ||
| 30 | + class MyRequest(http.Request): | ||
| 31 | + def process(self): | ||
| 32 | + processed.append(self) | ||
| 33 | + self.finish() | ||
| 34 | + | ||
| 35 | + requestLines = [ | ||
| 36 | + b"GET / HTTP/1.0", | ||
| 37 | + b"spaces: spaces were stripped ", | ||
| 38 | + b"tabs: \t\ttabs were stripped\t\t", | ||
| 39 | + b"spaces-and-tabs: \t \t spaces and tabs were stripped\t \t", | ||
| 40 | + b"line-tab: \v vertical tab was preserved\v\t", | ||
| 41 | + b"form-feed: \f form feed was preserved \f ", | ||
| 42 | + b"", | ||
| 43 | + b"", | ||
| 44 | + ] | ||
| 45 | + | ||
| 46 | + self.runRequest(b"\n".join(requestLines), MyRequest, 0) | ||
| 47 | + [request] = processed | ||
| 48 | + # All leading and trailing whitespace is stripped from the | ||
| 49 | + # header-value. | ||
| 50 | + self.assertEqual( | ||
| 51 | + request.requestHeaders.getRawHeaders(b"spaces"), | ||
| 52 | + [b"spaces were stripped"], | ||
| 53 | + ) | ||
| 54 | + self.assertEqual( | ||
| 55 | + request.requestHeaders.getRawHeaders(b"tabs"), | ||
| 56 | + [b"tabs were stripped"], | ||
| 57 | + ) | ||
| 58 | + self.assertEqual( | ||
| 59 | + request.requestHeaders.getRawHeaders(b"spaces-and-tabs"), | ||
| 60 | + [b"spaces and tabs were stripped"], | ||
| 61 | + ) | ||
| 62 | + self.assertEqual( | ||
| 63 | + request.requestHeaders.getRawHeaders(b"line-tab"), | ||
| 64 | + [b"\v vertical tab was preserved\v"], | ||
| 65 | + ) | ||
| 66 | + self.assertEqual( | ||
| 67 | + request.requestHeaders.getRawHeaders(b"form-feed"), | ||
| 68 | + [b"\f form feed was preserved \f"], | ||
| 69 | + ) | ||
| 70 | + | ||
| 71 | def test_tooManyHeaders(self): | ||
| 72 | """ | ||
| 73 | C{HTTPChannel} enforces a limit of C{HTTPChannel.maxHeaders} on the | ||
| 74 | @@ -2407,6 +2457,58 @@ Hello, | ||
| 75 | ] | ||
| 76 | ) | ||
| 77 | |||
| 78 | + def test_contentLengthMalformed(self): | ||
| 79 | + """ | ||
| 80 | + A request with a non-integer C{Content-Length} header fails with a 400 | ||
| 81 | + response without calling L{Request.process}. | ||
| 82 | + """ | ||
| 83 | + self.assertRequestRejected( | ||
| 84 | + [ | ||
| 85 | + b"GET /a HTTP/1.1", | ||
| 86 | + b"Content-Length: MORE THAN NINE THOUSAND!", | ||
| 87 | + b"Host: host.invalid", | ||
| 88 | + b"", | ||
| 89 | + b"", | ||
| 90 | + b"x" * 9001, | ||
| 91 | + ] | ||
| 92 | + ) | ||
| 93 | + | ||
| 94 | + def test_contentLengthTooPositive(self): | ||
| 95 | + """ | ||
| 96 | + A request with a C{Content-Length} header that begins with a L{+} fails | ||
| 97 | + with a 400 response without calling L{Request.process}. | ||
| 98 | + | ||
| 99 | + This is a potential request smuggling vector: see GHSA-c2jg-hw38-jrqq. | ||
| 100 | + """ | ||
| 101 | + self.assertRequestRejected( | ||
| 102 | + [ | ||
| 103 | + b"GET /a HTTP/1.1", | ||
| 104 | + b"Content-Length: +100", | ||
| 105 | + b"Host: host.invalid", | ||
| 106 | + b"", | ||
| 107 | + b"", | ||
| 108 | + b"x" * 100, | ||
| 109 | + ] | ||
| 110 | + ) | ||
| 111 | + | ||
| 112 | + def test_contentLengthNegative(self): | ||
| 113 | + """ | ||
| 114 | + A request with a C{Content-Length} header that is negative fails with | ||
| 115 | + a 400 response without calling L{Request.process}. | ||
| 116 | + | ||
| 117 | + This is a potential request smuggling vector: see GHSA-c2jg-hw38-jrqq. | ||
| 118 | + """ | ||
| 119 | + self.assertRequestRejected( | ||
| 120 | + [ | ||
| 121 | + b"GET /a HTTP/1.1", | ||
| 122 | + b"Content-Length: -100", | ||
| 123 | + b"Host: host.invalid", | ||
| 124 | + b"", | ||
| 125 | + b"", | ||
| 126 | + b"x" * 200, | ||
| 127 | + ] | ||
| 128 | + ) | ||
| 129 | + | ||
| 130 | def test_duplicateContentLengthsWithPipelinedRequests(self): | ||
| 131 | """ | ||
| 132 | Two pipelined requests, the first of which includes multiple | ||
diff --git a/meta-python/recipes-devtools/python/python3-twisted/CVE-2022-24801-2.patch b/meta-python/recipes-devtools/python/python3-twisted/CVE-2022-24801-2.patch new file mode 100644 index 0000000000..c9164b54f0 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-twisted/CVE-2022-24801-2.patch | |||
| @@ -0,0 +1,63 @@ | |||
| 1 | From 232c32ca0ecc3f9d263e2184253a839ce99b4f31 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Tom Most <twm@freecog.net> | ||
| 3 | Date: Mon, 7 Mar 2022 00:02:55 -0800 | ||
| 4 | Subject: [PATCH] Replace obs-fold with a single space | ||
| 5 | |||
| 6 | Upstream-Status: Backport [https://github.com/twisted/twisted/commit/79ee8c564ca0d4c2910c8859e0a6014d2dc40005] | ||
| 7 | CVE: CVE-2022-24801 | ||
| 8 | Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com> | ||
| 9 | --- | ||
| 10 | src/twisted/web/http.py | 2 +- | ||
| 11 | src/twisted/web/test/test_http.py | 13 +++++++++---- | ||
| 12 | 2 files changed, 10 insertions(+), 5 deletions(-) | ||
| 13 | |||
| 14 | diff --git a/src/twisted/web/http.py b/src/twisted/web/http.py | ||
| 15 | index b99480f..5491953 100644 | ||
| 16 | --- a/src/twisted/web/http.py | ||
| 17 | +++ b/src/twisted/web/http.py | ||
| 18 | @@ -2246,7 +2246,7 @@ class HTTPChannel(basic.LineReceiver, policies.TimeoutMixin): | ||
| 19 | self.setRawMode() | ||
| 20 | elif line[0] in b" \t": | ||
| 21 | # Continuation of a multi line header. | ||
| 22 | - self.__header = self.__header + b"\n" + line | ||
| 23 | + self.__header += b" " + line.lstrip(b" \t") | ||
| 24 | # Regular header line. | ||
| 25 | # Processing of header line is delayed to allow accumulating multi | ||
| 26 | # line headers. | ||
| 27 | diff --git a/src/twisted/web/test/test_http.py b/src/twisted/web/test/test_http.py | ||
| 28 | index 0549ed0..8a7adc0 100644 | ||
| 29 | --- a/src/twisted/web/test/test_http.py | ||
| 30 | +++ b/src/twisted/web/test/test_http.py | ||
| 31 | @@ -1795,7 +1795,12 @@ class ParsingTests(unittest.TestCase): | ||
| 32 | Line folded headers are handled by L{HTTPChannel} by replacing each | ||
| 33 | fold with a single space by the time they are made available to the | ||
| 34 | L{Request}. Any leading whitespace in the folded lines of the header | ||
| 35 | - value is preserved. | ||
| 36 | + value is replaced with a single space, per: | ||
| 37 | + | ||
| 38 | + A server that receives an obs-fold in a request message ... MUST | ||
| 39 | + ... replace each received obs-fold with one or more SP octets prior | ||
| 40 | + to interpreting the field value or forwarding the message | ||
| 41 | + downstream. | ||
| 42 | |||
| 43 | See RFC 7230 section 3.2.4. | ||
| 44 | """ | ||
| 45 | @@ -1832,15 +1837,15 @@ class ParsingTests(unittest.TestCase): | ||
| 46 | ) | ||
| 47 | self.assertEqual( | ||
| 48 | request.requestHeaders.getRawHeaders(b"space"), | ||
| 49 | - [b"space space"], | ||
| 50 | + [b"space space"], | ||
| 51 | ) | ||
| 52 | self.assertEqual( | ||
| 53 | request.requestHeaders.getRawHeaders(b"spaces"), | ||
| 54 | - [b"spaces spaces spaces"], | ||
| 55 | + [b"spaces spaces spaces"], | ||
| 56 | ) | ||
| 57 | self.assertEqual( | ||
| 58 | request.requestHeaders.getRawHeaders(b"tab"), | ||
| 59 | - [b"t \ta \tb"], | ||
| 60 | + [b"t a b"], | ||
| 61 | ) | ||
| 62 | |||
| 63 | def test_headerStripWhitespace(self): | ||
diff --git a/meta-python/recipes-devtools/python/python3-twisted/CVE-2022-24801-3.patch b/meta-python/recipes-devtools/python/python3-twisted/CVE-2022-24801-3.patch new file mode 100644 index 0000000000..5a9a287986 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-twisted/CVE-2022-24801-3.patch | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | From 5897923d523b357f93eb844e386bcc52c5490cdc Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Tom Most <twm@freecog.net> | ||
| 3 | Date: Mon, 7 Mar 2022 00:03:50 -0800 | ||
| 4 | Subject: [PATCH] Strip only spaces and tabs from header values | ||
| 5 | |||
| 6 | Upstream-Status: Backport [https://github.com/twisted/twisted/commit/c3a4e1d015740c1d87a3ec7d57570257e75b0062] | ||
| 7 | CVE: CVE-2022-24801 | ||
| 8 | Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com> | ||
| 9 | --- | ||
| 10 | src/twisted/web/http.py | 2 +- | ||
| 11 | 1 file changed, 1 insertion(+), 1 deletion(-) | ||
| 12 | |||
| 13 | diff --git a/src/twisted/web/http.py b/src/twisted/web/http.py | ||
| 14 | index 5491953..262da0b 100644 | ||
| 15 | --- a/src/twisted/web/http.py | ||
| 16 | +++ b/src/twisted/web/http.py | ||
| 17 | @@ -2327,7 +2327,7 @@ class HTTPChannel(basic.LineReceiver, policies.TimeoutMixin): | ||
| 18 | return False | ||
| 19 | |||
| 20 | header = header.lower() | ||
| 21 | - data = data.strip() | ||
| 22 | + data = data.strip(b" \t") | ||
| 23 | |||
| 24 | if not self._maybeChooseTransferDecoder(header, data): | ||
| 25 | return False | ||
diff --git a/meta-python/recipes-devtools/python/python3-twisted/CVE-2022-24801-4.patch b/meta-python/recipes-devtools/python/python3-twisted/CVE-2022-24801-4.patch new file mode 100644 index 0000000000..94c4b9c550 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-twisted/CVE-2022-24801-4.patch | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | From c6838374d0e323b78877ae546e1471c400e4652d Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Tom Most <twm@freecog.net> | ||
| 3 | Date: Mon, 7 Mar 2022 00:32:14 -0800 | ||
| 4 | Subject: [PATCH] Reject non-digit Content-Length | ||
| 5 | |||
| 6 | Upstream-Status: Backport [https://github.com/twisted/twisted/commit/8ebfa8f6577431226e109ff98ba48f5152a2c416] | ||
| 7 | CVE: CVE-2022-24801 | ||
| 8 | Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com> | ||
| 9 | --- | ||
| 10 | src/twisted/web/http.py | 2 ++ | ||
| 11 | 1 file changed, 2 insertions(+) | ||
| 12 | |||
| 13 | diff --git a/src/twisted/web/http.py b/src/twisted/web/http.py | ||
| 14 | index 262da0b..5316d81 100644 | ||
| 15 | --- a/src/twisted/web/http.py | ||
| 16 | +++ b/src/twisted/web/http.py | ||
| 17 | @@ -2274,6 +2274,8 @@ class HTTPChannel(basic.LineReceiver, policies.TimeoutMixin): | ||
| 18 | |||
| 19 | # Can this header determine the length? | ||
| 20 | if header == b"content-length": | ||
| 21 | + if not data.isdigit(): | ||
| 22 | + return fail() | ||
| 23 | try: | ||
| 24 | length = int(data) | ||
| 25 | except ValueError: | ||
diff --git a/meta-python/recipes-devtools/python/python3-twisted/CVE-2022-24801-5.patch b/meta-python/recipes-devtools/python/python3-twisted/CVE-2022-24801-5.patch new file mode 100644 index 0000000000..4c014bf669 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-twisted/CVE-2022-24801-5.patch | |||
| @@ -0,0 +1,71 @@ | |||
| 1 | From 8859df3b77eabf99a9b40c5e595bccaae4539ae0 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Tom Most <twm@freecog.net> | ||
| 3 | Date: Sun, 13 Mar 2022 23:19:39 -0700 | ||
| 4 | Subject: [PATCH] Test for malformed chunk size and extensions | ||
| 5 | |||
| 6 | Upstream-Status: Backport [https://github.com/twisted/twisted/commit/f22d0d9c889822adb7eaf84b42a20ff5f7c4d421] | ||
| 7 | CVE: CVE-2022-24801 | ||
| 8 | Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com> | ||
| 9 | --- | ||
| 10 | src/twisted/web/test/test_http.py | 34 +++++++++++++++++++++++++++++++ | ||
| 11 | 1 file changed, 34 insertions(+) | ||
| 12 | |||
| 13 | diff --git a/src/twisted/web/test/test_http.py b/src/twisted/web/test/test_http.py | ||
| 14 | index 8a7adc0..e686aeb 100644 | ||
| 15 | --- a/src/twisted/web/test/test_http.py | ||
| 16 | +++ b/src/twisted/web/test/test_http.py | ||
| 17 | @@ -1371,6 +1371,22 @@ class ChunkedTransferEncodingTests(unittest.TestCase): | ||
| 18 | p.dataReceived(b"3; x-foo=bar\r\nabc\r\n") | ||
| 19 | self.assertEqual(L, [b"abc"]) | ||
| 20 | |||
| 21 | + def test_extensionsMalformed(self): | ||
| 22 | + """ | ||
| 23 | + L{_ChunkedTransferDecoder.dataReceived} raises | ||
| 24 | + L{_MalformedChunkedDataError} when the chunk extension fields contain | ||
| 25 | + invalid characters. | ||
| 26 | + | ||
| 27 | + This is a potential request smuggling vector: see GHSA-c2jg-hw38-jrqq. | ||
| 28 | + """ | ||
| 29 | + for b in [*range(0, 0x09), *range(0x10, 0x21), *range(0x74, 0x80)]: | ||
| 30 | + data = b"3; " + bytes((b,)) + b"\r\nabc\r\n" | ||
| 31 | + p = http._ChunkedTransferDecoder( | ||
| 32 | + lambda b: None, # pragma: nocov | ||
| 33 | + lambda b: None, # pragma: nocov | ||
| 34 | + ) | ||
| 35 | + self.assertRaises(http._MalformedChunkedDataError, p.dataReceived, data) | ||
| 36 | + | ||
| 37 | def test_oversizedChunkSizeLine(self): | ||
| 38 | """ | ||
| 39 | L{_ChunkedTransferDecoder.dataReceived} raises | ||
| 40 | @@ -1426,6 +1442,22 @@ class ChunkedTransferEncodingTests(unittest.TestCase): | ||
| 41 | http._MalformedChunkedDataError, p.dataReceived, b"-3\r\nabc\r\n" | ||
| 42 | ) | ||
| 43 | |||
| 44 | + def test_malformedChunkSizeHex(self): | ||
| 45 | + """ | ||
| 46 | + L{_ChunkedTransferDecoder.dataReceived} raises | ||
| 47 | + L{_MalformedChunkedDataError} when the chunk size is prefixed with | ||
| 48 | + "0x", as if it were a Python integer literal. | ||
| 49 | + | ||
| 50 | + This is a potential request smuggling vector: see GHSA-c2jg-hw38-jrqq. | ||
| 51 | + """ | ||
| 52 | + p = http._ChunkedTransferDecoder( | ||
| 53 | + lambda b: None, # pragma: nocov | ||
| 54 | + lambda b: None, # pragma: nocov | ||
| 55 | + ) | ||
| 56 | + self.assertRaises( | ||
| 57 | + http._MalformedChunkedDataError, p.dataReceived, b"0x3\r\nabc\r\n" | ||
| 58 | + ) | ||
| 59 | + | ||
| 60 | def test_malformedChunkEnd(self): | ||
| 61 | r""" | ||
| 62 | L{_ChunkedTransferDecoder.dataReceived} raises | ||
| 63 | @@ -1538,6 +1570,8 @@ class ChunkingTests(unittest.TestCase, ResponseTestMixin): | ||
| 64 | chunked = b"".join(http.toChunk(s)) | ||
| 65 | self.assertEqual((s, b""), http.fromChunk(chunked)) | ||
| 66 | self.assertRaises(ValueError, http.fromChunk, b"-5\r\nmalformed!\r\n") | ||
| 67 | + self.assertRaises(ValueError, http.fromChunk, b"0xa\r\nmalformed!\r\n") | ||
| 68 | + self.assertRaises(ValueError, http.fromChunk, b"0XA\r\nmalformed!\r\n") | ||
| 69 | |||
| 70 | def testConcatenatedChunks(self): | ||
| 71 | chunked = b"".join([b"".join(http.toChunk(t)) for t in self.strings]) | ||
diff --git a/meta-python/recipes-devtools/python/python3-twisted/CVE-2022-24801-6.patch b/meta-python/recipes-devtools/python/python3-twisted/CVE-2022-24801-6.patch new file mode 100644 index 0000000000..26007d9e04 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-twisted/CVE-2022-24801-6.patch | |||
| @@ -0,0 +1,129 @@ | |||
| 1 | From e33f7fc231845487f969a9c0fbf7956226ac8dfa Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Tom Most <twm@freecog.net> | ||
| 3 | Date: Sun, 13 Mar 2022 23:51:52 -0700 | ||
| 4 | Subject: [PATCH] Reject malformed chunk sizes | ||
| 5 | |||
| 6 | Upstream-Status: Backport [https://github.com/twisted/twisted/commit/0275152f147506c82868ff1dabd9bf655ab67946] | ||
| 7 | CVE: CVE-2022-24801 | ||
| 8 | Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com> | ||
| 9 | --- | ||
| 10 | src/twisted/web/http.py | 35 +++++++++++++++++++++++---- | ||
| 11 | src/twisted/web/test/test_http.py | 40 +++++++++++++++++++++++++++++++ | ||
| 12 | 2 files changed, 71 insertions(+), 4 deletions(-) | ||
| 13 | |||
| 14 | diff --git a/src/twisted/web/http.py b/src/twisted/web/http.py | ||
| 15 | index 5316d81..940ff9f 100644 | ||
| 16 | --- a/src/twisted/web/http.py | ||
| 17 | +++ b/src/twisted/web/http.py | ||
| 18 | @@ -108,7 +108,7 @@ import tempfile | ||
| 19 | import time | ||
| 20 | import warnings | ||
| 21 | from io import BytesIO | ||
| 22 | -from typing import AnyStr, Callable, Optional | ||
| 23 | +from typing import AnyStr, Callable, Optional, Tuple | ||
| 24 | from urllib.parse import ( | ||
| 25 | ParseResultBytes, | ||
| 26 | unquote_to_bytes as unquote, | ||
| 27 | @@ -410,7 +410,33 @@ def toChunk(data): | ||
| 28 | return (networkString(f"{len(data):x}"), b"\r\n", data, b"\r\n") | ||
| 29 | |||
| 30 | |||
| 31 | -def fromChunk(data): | ||
| 32 | +def _ishexdigits(b: bytes) -> bool: | ||
| 33 | + """ | ||
| 34 | + Is the string case-insensitively hexidecimal? | ||
| 35 | + | ||
| 36 | + It must be composed of one or more characters in the ranges a-f, A-F | ||
| 37 | + and 0-9. | ||
| 38 | + """ | ||
| 39 | + for c in b: | ||
| 40 | + if c not in b'0123456789abcdefABCDEF': | ||
| 41 | + return False | ||
| 42 | + return bool(b) | ||
| 43 | + | ||
| 44 | + | ||
| 45 | +def _hexint(b: bytes) -> int: | ||
| 46 | + """ | ||
| 47 | + Decode a hexadecimal integer. | ||
| 48 | + | ||
| 49 | + Unlike L{int(b, 16)}, this raises L{ValueError} when the integer has | ||
| 50 | + a prefix like C{b'0x'}, C{b'+'}, or C{b'-'}, which is desirable when | ||
| 51 | + parsing network protocols. | ||
| 52 | + """ | ||
| 53 | + if not _ishexdigits(b): | ||
| 54 | + raise ValueError(b) | ||
| 55 | + return int(b, 16) | ||
| 56 | + | ||
| 57 | + | ||
| 58 | +def fromChunk(data: bytes) -> Tuple[bytes, bytes]: | ||
| 59 | """ | ||
| 60 | Convert chunk to string. | ||
| 61 | |||
| 62 | @@ -422,7 +448,7 @@ def fromChunk(data): | ||
| 63 | byte string. | ||
| 64 | """ | ||
| 65 | prefix, rest = data.split(b"\r\n", 1) | ||
| 66 | - length = int(prefix, 16) | ||
| 67 | + length = _hexint(prefix) | ||
| 68 | if length < 0: | ||
| 69 | raise ValueError("Chunk length must be >= 0, not %d" % (length,)) | ||
| 70 | if rest[length : length + 2] != b"\r\n": | ||
| 71 | @@ -1883,8 +1909,9 @@ class _ChunkedTransferDecoder: | ||
| 72 | endOfLengthIndex = self._buffer.find(b";", 0, eolIndex) | ||
| 73 | if endOfLengthIndex == -1: | ||
| 74 | endOfLengthIndex = eolIndex | ||
| 75 | + rawLength = self._buffer[0:endOfLengthIndex] | ||
| 76 | try: | ||
| 77 | - length = int(self._buffer[0:endOfLengthIndex], 16) | ||
| 78 | + length = _hexint(rawLength) | ||
| 79 | except ValueError: | ||
| 80 | raise _MalformedChunkedDataError("Chunk-size must be an integer.") | ||
| 81 | |||
| 82 | diff --git a/src/twisted/web/test/test_http.py b/src/twisted/web/test/test_http.py | ||
| 83 | index e686aeb..201991f 100644 | ||
| 84 | --- a/src/twisted/web/test/test_http.py | ||
| 85 | +++ b/src/twisted/web/test/test_http.py | ||
| 86 | @@ -4472,3 +4472,43 @@ class HTTPClientSanitizationTests(unittest.SynchronousTestCase): | ||
| 87 | transport.value().splitlines(), | ||
| 88 | [b": ".join([sanitizedBytes, sanitizedBytes])], | ||
| 89 | ) | ||
| 90 | + | ||
| 91 | + | ||
| 92 | +class HexHelperTests(unittest.SynchronousTestCase): | ||
| 93 | + """ | ||
| 94 | + Test the L{http._hexint} and L{http._ishexdigits} helper functions. | ||
| 95 | + """ | ||
| 96 | + | ||
| 97 | + badStrings = (b"", b"0x1234", b"feds", b"-123" b"+123") | ||
| 98 | + | ||
| 99 | + def test_isHex(self): | ||
| 100 | + """ | ||
| 101 | + L{_ishexdigits()} returns L{True} for nonempy bytestrings containing | ||
| 102 | + hexadecimal digits. | ||
| 103 | + """ | ||
| 104 | + for s in (b"10", b"abcdef", b"AB1234", b"fed", b"123467890"): | ||
| 105 | + self.assertIs(True, http._ishexdigits(s)) | ||
| 106 | + | ||
| 107 | + def test_decodes(self): | ||
| 108 | + """ | ||
| 109 | + L{_hexint()} returns the integer equivalent of the input. | ||
| 110 | + """ | ||
| 111 | + self.assertEqual(10, http._hexint(b"a")) | ||
| 112 | + self.assertEqual(0x10, http._hexint(b"10")) | ||
| 113 | + self.assertEqual(0xABCD123, http._hexint(b"abCD123")) | ||
| 114 | + | ||
| 115 | + def test_isNotHex(self): | ||
| 116 | + """ | ||
| 117 | + L{_ishexdigits()} returns L{False} for bytestrings that don't contain | ||
| 118 | + hexadecimal digits, including the empty string. | ||
| 119 | + """ | ||
| 120 | + for s in self.badStrings: | ||
| 121 | + self.assertIs(False, http._ishexdigits(s)) | ||
| 122 | + | ||
| 123 | + def test_decodeNotHex(self): | ||
| 124 | + """ | ||
| 125 | + L{_hexint()} raises L{ValueError} for bytestrings that can't | ||
| 126 | + be decoded. | ||
| 127 | + """ | ||
| 128 | + for s in self.badStrings: | ||
| 129 | + self.assertRaises(ValueError, http._hexint, s) | ||
diff --git a/meta-python/recipes-devtools/python/python3-twisted/CVE-2022-24801-7.patch b/meta-python/recipes-devtools/python/python3-twisted/CVE-2022-24801-7.patch new file mode 100644 index 0000000000..3fb4f432b4 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-twisted/CVE-2022-24801-7.patch | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | From a2fd35cf03bd3a847fd47a6f1a812e359c2dafda Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Tom Most <twm@freecog.net> | ||
| 3 | Date: Sun, 13 Mar 2022 23:57:23 -0700 | ||
| 4 | Subject: [PATCH] Remove unreachable branch | ||
| 5 | |||
| 6 | Upstream-Status: Backport [https://github.com/twisted/twisted/commit/696bfeaf5a1fa7ff952f860c89e2bdcfacef7d7a] | ||
| 7 | CVE: CVE-2022-24801 | ||
| 8 | Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com> | ||
| 9 | --- | ||
| 10 | src/twisted/web/http.py | 4 +--- | ||
| 11 | 1 file changed, 1 insertion(+), 3 deletions(-) | ||
| 12 | |||
| 13 | diff --git a/src/twisted/web/http.py b/src/twisted/web/http.py | ||
| 14 | index 940ff9f..ea77f57 100644 | ||
| 15 | --- a/src/twisted/web/http.py | ||
| 16 | +++ b/src/twisted/web/http.py | ||
| 17 | @@ -1915,9 +1915,7 @@ class _ChunkedTransferDecoder: | ||
| 18 | except ValueError: | ||
| 19 | raise _MalformedChunkedDataError("Chunk-size must be an integer.") | ||
| 20 | |||
| 21 | - if length < 0: | ||
| 22 | - raise _MalformedChunkedDataError("Chunk-size must not be negative.") | ||
| 23 | - elif length == 0: | ||
| 24 | + if length == 0: | ||
| 25 | self.state = "TRAILER" | ||
| 26 | else: | ||
| 27 | self.state = "BODY" | ||
diff --git a/meta-python/recipes-devtools/python/python3-twisted/CVE-2022-24801-8.patch b/meta-python/recipes-devtools/python/python3-twisted/CVE-2022-24801-8.patch new file mode 100644 index 0000000000..e426d4d8f2 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-twisted/CVE-2022-24801-8.patch | |||
| @@ -0,0 +1,108 @@ | |||
| 1 | From 29b4c6ab9a917200a37d6fca1243a7f57caba922 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Tom Most <twm@freecog.net> | ||
| 3 | Date: Sun, 27 Mar 2022 22:17:30 -0700 | ||
| 4 | Subject: [PATCH] Correct chunk extension byte validation | ||
| 5 | |||
| 6 | Go back to the RFC to figure out the correct allowed ranges. | ||
| 7 | |||
| 8 | Upstream-Status: Backport [https://github.com/twisted/twisted/commit/fa9caa54d63399b4ccdfbf0429ba1b504ccc7c89] | ||
| 9 | CVE: CVE-2022-24801 | ||
| 10 | Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com> | ||
| 11 | --- | ||
| 12 | src/twisted/web/http.py | 49 ++++++++++++++++++++++++++++++- | ||
| 13 | src/twisted/web/test/test_http.py | 8 ++++- | ||
| 14 | 2 files changed, 55 insertions(+), 2 deletions(-) | ||
| 15 | |||
| 16 | diff --git a/src/twisted/web/http.py b/src/twisted/web/http.py | ||
| 17 | index ea77f57..81df437 100644 | ||
| 18 | --- a/src/twisted/web/http.py | ||
| 19 | +++ b/src/twisted/web/http.py | ||
| 20 | @@ -418,7 +418,7 @@ def _ishexdigits(b: bytes) -> bool: | ||
| 21 | and 0-9. | ||
| 22 | """ | ||
| 23 | for c in b: | ||
| 24 | - if c not in b'0123456789abcdefABCDEF': | ||
| 25 | + if c not in b"0123456789abcdefABCDEF": | ||
| 26 | return False | ||
| 27 | return bool(b) | ||
| 28 | |||
| 29 | @@ -1816,6 +1816,47 @@ class _IdentityTransferDecoder: | ||
| 30 | maxChunkSizeLineLength = 1024 | ||
| 31 | |||
| 32 | |||
| 33 | +_chunkExtChars = ( | ||
| 34 | + b"\t !\"#$%&'()*+,-./0123456789:;<=>?@" | ||
| 35 | + b"ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`" | ||
| 36 | + b"abcdefghijklmnopqrstuvwxyz{|}~" | ||
| 37 | + b"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" | ||
| 38 | + b"\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" | ||
| 39 | + b"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf" | ||
| 40 | + b"\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" | ||
| 41 | + b"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" | ||
| 42 | + b"\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" | ||
| 43 | + b"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef" | ||
| 44 | + b"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" | ||
| 45 | +) | ||
| 46 | +""" | ||
| 47 | +Characters that are valid in a chunk extension. | ||
| 48 | + | ||
| 49 | +See RFC 7230 section 4.1.1: | ||
| 50 | + | ||
| 51 | + chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) | ||
| 52 | + | ||
| 53 | + chunk-ext-name = token | ||
| 54 | + chunk-ext-val = token / quoted-string | ||
| 55 | + | ||
| 56 | +Section 3.2.6: | ||
| 57 | + | ||
| 58 | + token = 1*tchar | ||
| 59 | + | ||
| 60 | + tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" | ||
| 61 | + / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" | ||
| 62 | + / DIGIT / ALPHA | ||
| 63 | + ; any VCHAR, except delimiters | ||
| 64 | + | ||
| 65 | + quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE | ||
| 66 | + qdtext = HTAB / SP /%x21 / %x23-5B / %x5D-7E / obs-text | ||
| 67 | + obs-text = %x80-FF | ||
| 68 | + | ||
| 69 | +We don't check if chunk extensions are well-formed beyond validating that they | ||
| 70 | +don't contain characters outside this range. | ||
| 71 | +""" | ||
| 72 | + | ||
| 73 | + | ||
| 74 | class _ChunkedTransferDecoder: | ||
| 75 | """ | ||
| 76 | Protocol for decoding I{chunked} Transfer-Encoding, as defined by RFC 7230, | ||
| 77 | @@ -1915,6 +1956,12 @@ class _ChunkedTransferDecoder: | ||
| 78 | except ValueError: | ||
| 79 | raise _MalformedChunkedDataError("Chunk-size must be an integer.") | ||
| 80 | |||
| 81 | + ext = self._buffer[endOfLengthIndex + 1 : eolIndex] | ||
| 82 | + if ext and ext.translate(None, _chunkExtChars) != b"": | ||
| 83 | + raise _MalformedChunkedDataError( | ||
| 84 | + f"Invalid characters in chunk extensions: {ext!r}." | ||
| 85 | + ) | ||
| 86 | + | ||
| 87 | if length == 0: | ||
| 88 | self.state = "TRAILER" | ||
| 89 | else: | ||
| 90 | diff --git a/src/twisted/web/test/test_http.py b/src/twisted/web/test/test_http.py | ||
| 91 | index 201991f..eccb9b0 100644 | ||
| 92 | --- a/src/twisted/web/test/test_http.py | ||
| 93 | +++ b/src/twisted/web/test/test_http.py | ||
| 94 | @@ -1379,7 +1379,13 @@ class ChunkedTransferEncodingTests(unittest.TestCase): | ||
| 95 | |||
| 96 | This is a potential request smuggling vector: see GHSA-c2jg-hw38-jrqq. | ||
| 97 | """ | ||
| 98 | - for b in [*range(0, 0x09), *range(0x10, 0x21), *range(0x74, 0x80)]: | ||
| 99 | + invalidControl = ( | ||
| 100 | + b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\n\x0b\x0c\r\x0e\x0f" | ||
| 101 | + b"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" | ||
| 102 | + ) | ||
| 103 | + invalidDelimiter = b"\\" | ||
| 104 | + invalidDel = b"\x7f" | ||
| 105 | + for b in invalidControl + invalidDelimiter + invalidDel: | ||
| 106 | data = b"3; " + bytes((b,)) + b"\r\nabc\r\n" | ||
| 107 | p = http._ChunkedTransferDecoder( | ||
| 108 | lambda b: None, # pragma: nocov | ||
diff --git a/meta-python/recipes-devtools/python/python3-twisted/CVE-2022-24801-9.patch b/meta-python/recipes-devtools/python/python3-twisted/CVE-2022-24801-9.patch new file mode 100644 index 0000000000..e49afde74b --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-twisted/CVE-2022-24801-9.patch | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | From 349771026e1eb80cab7a19ceb8b80aa6d1bb3184 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Tom Most <twm@freecog.net> | ||
| 3 | Date: Fri, 1 Apr 2022 20:47:59 -0700 | ||
| 4 | Subject: [PATCH] Address review feedback | ||
| 5 | |||
| 6 | Upstream-Status: Backport [https://github.com/twisted/twisted/commit/2bbd6c89110f0d44d2bb109c14d787f65bca9df8] | ||
| 7 | CVE: CVE-2022-24801 | ||
| 8 | Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com> | ||
| 9 | --- | ||
| 10 | src/twisted/web/http.py | 6 +++--- | ||
| 11 | 1 file changed, 3 insertions(+), 3 deletions(-) | ||
| 12 | |||
| 13 | diff --git a/src/twisted/web/http.py b/src/twisted/web/http.py | ||
| 14 | index 81df437..4a2ce1d 100644 | ||
| 15 | --- a/src/twisted/web/http.py | ||
| 16 | +++ b/src/twisted/web/http.py | ||
| 17 | @@ -420,7 +420,7 @@ def _ishexdigits(b: bytes) -> bool: | ||
| 18 | for c in b: | ||
| 19 | if c not in b"0123456789abcdefABCDEF": | ||
| 20 | return False | ||
| 21 | - return bool(b) | ||
| 22 | + return b != b"" | ||
| 23 | |||
| 24 | |||
| 25 | def _hexint(b: bytes) -> int: | ||
diff --git a/meta-python/recipes-devtools/python/python3-twisted_22.2.0.bb b/meta-python/recipes-devtools/python/python3-twisted_22.2.0.bb index 5b23ceeb91..8e140f387e 100644 --- a/meta-python/recipes-devtools/python/python3-twisted_22.2.0.bb +++ b/meta-python/recipes-devtools/python/python3-twisted_22.2.0.bb | |||
| @@ -12,9 +12,19 @@ SRC_URI[sha256sum] = "57f32b1f6838facb8c004c89467840367ad38e9e535f8252091345dba5 | |||
| 12 | PYPI_PACKAGE = "Twisted" | 12 | PYPI_PACKAGE = "Twisted" |
| 13 | 13 | ||
| 14 | SRC_URI += "file://CVE-2024-41671-0001.patch \ | 14 | SRC_URI += "file://CVE-2024-41671-0001.patch \ |
| 15 | file://CVE-2024-41671-0002.patch \ | 15 | file://CVE-2024-41671-0002.patch \ |
| 16 | file://CVE-2024-41810.patch \ | 16 | file://CVE-2024-41810.patch \ |
| 17 | file://CVE-2023-46137.patch" | 17 | file://CVE-2023-46137.patch \ |
| 18 | file://CVE-2022-24801-1.patch \ | ||
| 19 | file://CVE-2022-24801-2.patch \ | ||
| 20 | file://CVE-2022-24801-3.patch \ | ||
| 21 | file://CVE-2022-24801-4.patch \ | ||
| 22 | file://CVE-2022-24801-5.patch \ | ||
| 23 | file://CVE-2022-24801-6.patch \ | ||
| 24 | file://CVE-2022-24801-7.patch \ | ||
| 25 | file://CVE-2022-24801-8.patch \ | ||
| 26 | file://CVE-2022-24801-9.patch \ | ||
| 27 | " | ||
| 18 | 28 | ||
| 19 | inherit pypi python_setuptools_build_meta | 29 | inherit pypi python_setuptools_build_meta |
| 20 | 30 | ||
