diff --git a/CVE-2022-24801-http-1.1-leniency.patch b/CVE-2022-24801-http-1.1-leniency.patch deleted file mode 100644 index 35d3b74..0000000 --- a/CVE-2022-24801-http-1.1-leniency.patch +++ /dev/null @@ -1,678 +0,0 @@ -From 22b067793cbcd0fb5dee04cfd9115fa85a7ca110 Mon Sep 17 00:00:00 2001 -From: Tom Most -Date: Sat, 5 Mar 2022 23:26:55 -0800 -Subject: [PATCH 01/13] Some tests for GHSA-c2jg-hw38-jrqq - ---- - src/twisted/web/test/test_http.py | 102 ++++++++++++++++++++++++++++++ - 1 file changed, 102 insertions(+) - -diff --git a/src/twisted/web/test/test_http.py b/src/twisted/web/test/test_http.py -index 7ffea4e0bc0..b4139558724 100644 ---- a/src/twisted/web/test/test_http.py -+++ b/src/twisted/web/test/test_http.py -@@ -1751,6 +1751,56 @@ def process(self): - [b"t \ta \tb"], - ) - -+ def test_headerStripWhitespace(self): -+ """ -+ Leading and trailing space and tab characters are stripped from -+ headers. Other forms of whitespace are preserved. -+ -+ See RFC 7230 section 3.2.3 and 3.2.4. -+ """ -+ processed = [] -+ -+ class MyRequest(http.Request): -+ def process(self): -+ processed.append(self) -+ self.finish() -+ -+ requestLines = [ -+ b"GET / HTTP/1.0", -+ b"spaces: spaces were stripped ", -+ b"tabs: \t\ttabs were stripped\t\t", -+ b"spaces-and-tabs: \t \t spaces and tabs were stripped\t \t", -+ b"line-tab: \v vertical tab was preserved\v\t", -+ b"form-feed: \f form feed was preserved \f ", -+ b"", -+ b"", -+ ] -+ -+ self.runRequest(b"\n".join(requestLines), MyRequest, 0) -+ [request] = processed -+ # All leading and trailing whitespace is stripped from the -+ # header-value. -+ self.assertEqual( -+ request.requestHeaders.getRawHeaders(b"spaces"), -+ [b"spaces were stripped"], -+ ) -+ self.assertEqual( -+ request.requestHeaders.getRawHeaders(b"tabs"), -+ [b"tabs were stripped"], -+ ) -+ self.assertEqual( -+ request.requestHeaders.getRawHeaders(b"spaces-and-tabs"), -+ [b"spaces and tabs were stripped"], -+ ) -+ self.assertEqual( -+ request.requestHeaders.getRawHeaders(b"line-tab"), -+ [b"\v vertical tab was preserved\v"], -+ ) -+ self.assertEqual( -+ request.requestHeaders.getRawHeaders(b"form-feed"), -+ [b"\f form feed was preserved \f"], -+ ) -+ - def test_tooManyHeaders(self): - """ - C{HTTPChannel} enforces a limit of C{HTTPChannel.maxHeaders} on the -@@ -2315,6 +2365,58 @@ def test_duplicateContentLengths(self): - ] - ) - -+ def test_contentLengthMalformed(self): -+ """ -+ A request with a non-integer C{Content-Length} header fails with a 400 -+ response without calling L{Request.process}. -+ """ -+ self.assertRequestRejected( -+ [ -+ b"GET /a HTTP/1.1", -+ b"Content-Length: MORE THAN NINE THOUSAND!", -+ b"Host: host.invalid", -+ b"", -+ b"", -+ b"x" * 9001, -+ ] -+ ) -+ -+ def test_contentLengthTooPositive(self): -+ """ -+ A request with a C{Content-Length} header that begins with a L{+} fails -+ with a 400 response without calling L{Request.process}. -+ -+ This is a potential request smuggling vector: see GHSA-c2jg-hw38-jrqq. -+ """ -+ self.assertRequestRejected( -+ [ -+ b"GET /a HTTP/1.1", -+ b"Content-Length: +100", -+ b"Host: host.invalid", -+ b"", -+ b"", -+ b"x" * 100, -+ ] -+ ) -+ -+ def test_contentLengthNegative(self): -+ """ -+ A request with a C{Content-Length} header that is negative fails with -+ a 400 response without calling L{Request.process}. -+ -+ This is a potential request smuggling vector: see GHSA-c2jg-hw38-jrqq. -+ """ -+ self.assertRequestRejected( -+ [ -+ b"GET /a HTTP/1.1", -+ b"Content-Length: -100", -+ b"Host: host.invalid", -+ b"", -+ b"", -+ b"x" * 200, -+ ] -+ ) -+ - def test_duplicateContentLengthsWithPipelinedRequests(self): - """ - Two pipelined requests, the first of which includes multiple - -From 79ee8c564ca0d4c2910c8859e0a6014d2dc40005 Mon Sep 17 00:00:00 2001 -From: Tom Most -Date: Mon, 7 Mar 2022 00:02:55 -0800 -Subject: [PATCH 02/13] Replace obs-fold with a single space - ---- - src/twisted/web/http.py | 2 +- - src/twisted/web/test/test_http.py | 13 +++++++++---- - 2 files changed, 10 insertions(+), 5 deletions(-) - -diff --git a/src/twisted/web/http.py b/src/twisted/web/http.py -index a53ebc27c28..ce9b796404c 100644 ---- a/src/twisted/web/http.py -+++ b/src/twisted/web/http.py -@@ -2246,7 +2246,7 @@ def lineReceived(self, line): - self.setRawMode() - elif line[0] in b" \t": - # Continuation of a multi line header. -- self.__header = self.__header + b"\n" + line -+ self.__header += b" " + line.lstrip(b" \t") - # Regular header line. - # Processing of header line is delayed to allow accumulating multi - # line headers. -diff --git a/src/twisted/web/test/test_http.py b/src/twisted/web/test/test_http.py -index b4139558724..91258f8f51b 100644 ---- a/src/twisted/web/test/test_http.py -+++ b/src/twisted/web/test/test_http.py -@@ -1703,7 +1703,12 @@ def test_headersMultiline(self): - Line folded headers are handled by L{HTTPChannel} by replacing each - fold with a single space by the time they are made available to the - L{Request}. Any leading whitespace in the folded lines of the header -- value is preserved. -+ value is replaced with a single space, per: -+ -+ A server that receives an obs-fold in a request message ... MUST -+ ... replace each received obs-fold with one or more SP octets prior -+ to interpreting the field value or forwarding the message -+ downstream. - - See RFC 7230 section 3.2.4. - """ -@@ -1740,15 +1745,15 @@ def process(self): - ) - self.assertEqual( - request.requestHeaders.getRawHeaders(b"space"), -- [b"space space"], -+ [b"space space"], - ) - self.assertEqual( - request.requestHeaders.getRawHeaders(b"spaces"), -- [b"spaces spaces spaces"], -+ [b"spaces spaces spaces"], - ) - self.assertEqual( - request.requestHeaders.getRawHeaders(b"tab"), -- [b"t \ta \tb"], -+ [b"t a b"], - ) - - def test_headerStripWhitespace(self): - -From c3a4e1d015740c1d87a3ec7d57570257e75b0062 Mon Sep 17 00:00:00 2001 -From: Tom Most -Date: Mon, 7 Mar 2022 00:03:50 -0800 -Subject: [PATCH 03/13] Strip only spaces and tabs from header values - ---- - src/twisted/web/http.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/twisted/web/http.py b/src/twisted/web/http.py -index ce9b796404c..b599b7c543c 100644 ---- a/src/twisted/web/http.py -+++ b/src/twisted/web/http.py -@@ -2327,7 +2327,7 @@ def headerReceived(self, line): - return False - - header = header.lower() -- data = data.strip() -+ data = data.strip(b" \t") - - if not self._maybeChooseTransferDecoder(header, data): - return False - -From 8ebfa8f6577431226e109ff98ba48f5152a2c416 Mon Sep 17 00:00:00 2001 -From: Tom Most -Date: Mon, 7 Mar 2022 00:32:14 -0800 -Subject: [PATCH 04/13] Reject non-digit Content-Length - ---- - src/twisted/web/http.py | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/src/twisted/web/http.py b/src/twisted/web/http.py -index b599b7c543c..a551e33daec 100644 ---- a/src/twisted/web/http.py -+++ b/src/twisted/web/http.py -@@ -2274,6 +2274,8 @@ def fail(): - - # Can this header determine the length? - if header == b"content-length": -+ if not data.isdigit(): -+ return fail() - try: - length = int(data) - except ValueError: - -From f22d0d9c889822adb7eaf84b42a20ff5f7c4d421 Mon Sep 17 00:00:00 2001 -From: Tom Most -Date: Sun, 13 Mar 2022 23:19:39 -0700 -Subject: [PATCH 05/13] Test for malformed chunk size and extensions - ---- - src/twisted/web/test/test_http.py | 34 +++++++++++++++++++++++++++++++ - 1 file changed, 34 insertions(+) - -diff --git a/src/twisted/web/test/test_http.py b/src/twisted/web/test/test_http.py -index 91258f8f51b..d6362d2dd29 100644 ---- a/src/twisted/web/test/test_http.py -+++ b/src/twisted/web/test/test_http.py -@@ -1279,6 +1279,22 @@ def test_extensions(self): - p.dataReceived(b"3; x-foo=bar\r\nabc\r\n") - self.assertEqual(L, [b"abc"]) - -+ def test_extensionsMalformed(self): -+ """ -+ L{_ChunkedTransferDecoder.dataReceived} raises -+ L{_MalformedChunkedDataError} when the chunk extension fields contain -+ invalid characters. -+ -+ This is a potential request smuggling vector: see GHSA-c2jg-hw38-jrqq. -+ """ -+ for b in [*range(0, 0x09), *range(0x10, 0x21), *range(0x74, 0x80)]: -+ data = b"3; " + bytes((b,)) + b"\r\nabc\r\n" -+ p = http._ChunkedTransferDecoder( -+ lambda b: None, # pragma: nocov -+ lambda b: None, # pragma: nocov -+ ) -+ self.assertRaises(http._MalformedChunkedDataError, p.dataReceived, data) -+ - def test_oversizedChunkSizeLine(self): - """ - L{_ChunkedTransferDecoder.dataReceived} raises -@@ -1334,6 +1350,22 @@ def test_malformedChunkSizeNegative(self): - http._MalformedChunkedDataError, p.dataReceived, b"-3\r\nabc\r\n" - ) - -+ def test_malformedChunkSizeHex(self): -+ """ -+ L{_ChunkedTransferDecoder.dataReceived} raises -+ L{_MalformedChunkedDataError} when the chunk size is prefixed with -+ "0x", as if it were a Python integer literal. -+ -+ This is a potential request smuggling vector: see GHSA-c2jg-hw38-jrqq. -+ """ -+ p = http._ChunkedTransferDecoder( -+ lambda b: None, # pragma: nocov -+ lambda b: None, # pragma: nocov -+ ) -+ self.assertRaises( -+ http._MalformedChunkedDataError, p.dataReceived, b"0x3\r\nabc\r\n" -+ ) -+ - def test_malformedChunkEnd(self): - r""" - L{_ChunkedTransferDecoder.dataReceived} raises -@@ -1446,6 +1478,8 @@ def testChunks(self): - chunked = b"".join(http.toChunk(s)) - self.assertEqual((s, b""), http.fromChunk(chunked)) - self.assertRaises(ValueError, http.fromChunk, b"-5\r\nmalformed!\r\n") -+ self.assertRaises(ValueError, http.fromChunk, b"0xa\r\nmalformed!\r\n") -+ self.assertRaises(ValueError, http.fromChunk, b"0XA\r\nmalformed!\r\n") - - def testConcatenatedChunks(self): - chunked = b"".join([b"".join(http.toChunk(t)) for t in self.strings]) - -From 0275152f147506c82868ff1dabd9bf655ab67946 Mon Sep 17 00:00:00 2001 -From: Tom Most -Date: Sun, 13 Mar 2022 23:51:52 -0700 -Subject: [PATCH 06/13] Reject malformed chunk sizes - ---- - src/twisted/web/http.py | 35 +++++++++++++++++++++++---- - src/twisted/web/test/test_http.py | 40 +++++++++++++++++++++++++++++++ - 2 files changed, 71 insertions(+), 4 deletions(-) - -diff --git a/src/twisted/web/http.py b/src/twisted/web/http.py -index a551e33daec..b089fa7c5ed 100644 ---- a/src/twisted/web/http.py -+++ b/src/twisted/web/http.py -@@ -108,7 +108,7 @@ - import time - import warnings - from io import BytesIO --from typing import AnyStr, Callable, Optional -+from typing import AnyStr, Callable, Optional, Tuple - from urllib.parse import ( - ParseResultBytes, - unquote_to_bytes as unquote, -@@ -410,7 +410,33 @@ def toChunk(data): - return (networkString(f"{len(data):x}"), b"\r\n", data, b"\r\n") - - --def fromChunk(data): -+def _ishexdigits(b: bytes) -> bool: -+ """ -+ Is the string case-insensitively hexidecimal? -+ -+ It must be composed of one or more characters in the ranges a-f, A-F -+ and 0-9. -+ """ -+ for c in b: -+ if c not in b'0123456789abcdefABCDEF': -+ return False -+ return bool(b) -+ -+ -+def _hexint(b: bytes) -> int: -+ """ -+ Decode a hexadecimal integer. -+ -+ Unlike L{int(b, 16)}, this raises L{ValueError} when the integer has -+ a prefix like C{b'0x'}, C{b'+'}, or C{b'-'}, which is desirable when -+ parsing network protocols. -+ """ -+ if not _ishexdigits(b): -+ raise ValueError(b) -+ return int(b, 16) -+ -+ -+def fromChunk(data: bytes) -> Tuple[bytes, bytes]: - """ - Convert chunk to string. - -@@ -422,7 +448,7 @@ def fromChunk(data): - byte string. - """ - prefix, rest = data.split(b"\r\n", 1) -- length = int(prefix, 16) -+ length = _hexint(prefix) - if length < 0: - raise ValueError("Chunk length must be >= 0, not %d" % (length,)) - if rest[length : length + 2] != b"\r\n": -@@ -1883,8 +1909,9 @@ def _dataReceived_CHUNK_LENGTH(self) -> bool: - endOfLengthIndex = self._buffer.find(b";", 0, eolIndex) - if endOfLengthIndex == -1: - endOfLengthIndex = eolIndex -+ rawLength = self._buffer[0:endOfLengthIndex] - try: -- length = int(self._buffer[0:endOfLengthIndex], 16) -+ length = _hexint(rawLength) - except ValueError: - raise _MalformedChunkedDataError("Chunk-size must be an integer.") - -diff --git a/src/twisted/web/test/test_http.py b/src/twisted/web/test/test_http.py -index d6362d2dd29..6948dcadc0c 100644 ---- a/src/twisted/web/test/test_http.py -+++ b/src/twisted/web/test/test_http.py -@@ -4380,3 +4380,43 @@ def test_sendHeaderSanitizesLinearWhitespace(self): - transport.value().splitlines(), - [b": ".join([sanitizedBytes, sanitizedBytes])], - ) -+ -+ -+class HexHelperTests(unittest.SynchronousTestCase): -+ """ -+ Test the L{http._hexint} and L{http._ishexdigits} helper functions. -+ """ -+ -+ badStrings = (b"", b"0x1234", b"feds", b"-123" b"+123") -+ -+ def test_isHex(self): -+ """ -+ L{_ishexdigits()} returns L{True} for nonempy bytestrings containing -+ hexadecimal digits. -+ """ -+ for s in (b"10", b"abcdef", b"AB1234", b"fed", b"123467890"): -+ self.assertIs(True, http._ishexdigits(s)) -+ -+ def test_decodes(self): -+ """ -+ L{_hexint()} returns the integer equivalent of the input. -+ """ -+ self.assertEqual(10, http._hexint(b"a")) -+ self.assertEqual(0x10, http._hexint(b"10")) -+ self.assertEqual(0xABCD123, http._hexint(b"abCD123")) -+ -+ def test_isNotHex(self): -+ """ -+ L{_ishexdigits()} returns L{False} for bytestrings that don't contain -+ hexadecimal digits, including the empty string. -+ """ -+ for s in self.badStrings: -+ self.assertIs(False, http._ishexdigits(s)) -+ -+ def test_decodeNotHex(self): -+ """ -+ L{_hexint()} raises L{ValueError} for bytestrings that can't -+ be decoded. -+ """ -+ for s in self.badStrings: -+ self.assertRaises(ValueError, http._hexint, s) - -From 2a5763d5b168372abb591c0eb6323ed4dfe8a4fc Mon Sep 17 00:00:00 2001 -From: Tom Most -Date: Sun, 13 Mar 2022 23:55:26 -0700 -Subject: [PATCH 07/13] We should deprecate http.fromChunk - ---- - src/twisted/web/http.py | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/src/twisted/web/http.py b/src/twisted/web/http.py -index b089fa7c5ed..46058f61518 100644 ---- a/src/twisted/web/http.py -+++ b/src/twisted/web/http.py -@@ -440,6 +440,9 @@ def fromChunk(data: bytes) -> Tuple[bytes, bytes]: - """ - Convert chunk to string. - -+ Note that this function is not specification compliant: it doesn't handle -+ chunk extensions. -+ - @type data: C{bytes} - - @return: tuple of (result, remaining) - both C{bytes}. - -From 696bfeaf5a1fa7ff952f860c89e2bdcfacef7d7a Mon Sep 17 00:00:00 2001 -From: Tom Most -Date: Sun, 13 Mar 2022 23:57:23 -0700 -Subject: [PATCH 08/13] Remove unreachable branch - ---- - src/twisted/web/http.py | 4 +--- - 1 file changed, 1 insertion(+), 3 deletions(-) - -diff --git a/src/twisted/web/http.py b/src/twisted/web/http.py -index 46058f61518..32040fb8fd3 100644 ---- a/src/twisted/web/http.py -+++ b/src/twisted/web/http.py -@@ -1918,9 +1918,7 @@ def _dataReceived_CHUNK_LENGTH(self) -> bool: - except ValueError: - raise _MalformedChunkedDataError("Chunk-size must be an integer.") - -- if length < 0: -- raise _MalformedChunkedDataError("Chunk-size must not be negative.") -- elif length == 0: -+ if length == 0: - self.state = "TRAILER" - else: - self.state = "BODY" - -From fa9caa54d63399b4ccdfbf0429ba1b504ccc7c89 Mon Sep 17 00:00:00 2001 -From: Tom Most -Date: Sun, 27 Mar 2022 22:17:30 -0700 -Subject: [PATCH 09/13] Correct chunk extension byte validation - -Go back to the RFC to figure out the correct allowed ranges. ---- - src/twisted/web/http.py | 49 ++++++++++++++++++++++++++++++- - src/twisted/web/test/test_http.py | 8 ++++- - 2 files changed, 55 insertions(+), 2 deletions(-) - -diff --git a/src/twisted/web/http.py b/src/twisted/web/http.py -index 32040fb8fd3..e8327f03f2c 100644 ---- a/src/twisted/web/http.py -+++ b/src/twisted/web/http.py -@@ -418,7 +418,7 @@ def _ishexdigits(b: bytes) -> bool: - and 0-9. - """ - for c in b: -- if c not in b'0123456789abcdefABCDEF': -+ if c not in b"0123456789abcdefABCDEF": - return False - return bool(b) - -@@ -1819,6 +1819,47 @@ def noMoreData(self): - maxChunkSizeLineLength = 1024 - - -+_chunkExtChars = ( -+ b"\t !\"#$%&'()*+,-./0123456789:;<=>?@" -+ b"ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`" -+ b"abcdefghijklmnopqrstuvwxyz{|}~" -+ b"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" -+ b"\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" -+ b"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf" -+ b"\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" -+ b"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" -+ b"\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" -+ b"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef" -+ b"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" -+) -+""" -+Characters that are valid in a chunk extension. -+ -+See RFC 7230 section 4.1.1: -+ -+ chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) -+ -+ chunk-ext-name = token -+ chunk-ext-val = token / quoted-string -+ -+Section 3.2.6: -+ -+ token = 1*tchar -+ -+ tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" -+ / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" -+ / DIGIT / ALPHA -+ ; any VCHAR, except delimiters -+ -+ quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE -+ qdtext = HTAB / SP /%x21 / %x23-5B / %x5D-7E / obs-text -+ obs-text = %x80-FF -+ -+We don't check if chunk extensions are well-formed beyond validating that they -+don't contain characters outside this range. -+""" -+ -+ - class _ChunkedTransferDecoder: - """ - Protocol for decoding I{chunked} Transfer-Encoding, as defined by RFC 7230, -@@ -1918,6 +1959,12 @@ def _dataReceived_CHUNK_LENGTH(self) -> bool: - except ValueError: - raise _MalformedChunkedDataError("Chunk-size must be an integer.") - -+ ext = self._buffer[endOfLengthIndex + 1 : eolIndex] -+ if ext and ext.translate(None, _chunkExtChars) != b"": -+ raise _MalformedChunkedDataError( -+ f"Invalid characters in chunk extensions: {ext!r}." -+ ) -+ - if length == 0: - self.state = "TRAILER" - else: -diff --git a/src/twisted/web/test/test_http.py b/src/twisted/web/test/test_http.py -index 6948dcadc0c..f304991ca48 100644 ---- a/src/twisted/web/test/test_http.py -+++ b/src/twisted/web/test/test_http.py -@@ -1287,7 +1287,13 @@ def test_extensionsMalformed(self): - - This is a potential request smuggling vector: see GHSA-c2jg-hw38-jrqq. - """ -- for b in [*range(0, 0x09), *range(0x10, 0x21), *range(0x74, 0x80)]: -+ invalidControl = ( -+ b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\n\x0b\x0c\r\x0e\x0f" -+ b"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" -+ ) -+ invalidDelimiter = b"\\" -+ invalidDel = b"\x7f" -+ for b in invalidControl + invalidDelimiter + invalidDel: - data = b"3; " + bytes((b,)) + b"\r\nabc\r\n" - p = http._ChunkedTransferDecoder( - lambda b: None, # pragma: nocov - -From c6d6a3ea2a819a99f17b54b08b7fd2c75587a8a7 Mon Sep 17 00:00:00 2001 -From: Tom Most -Date: Wed, 30 Mar 2022 23:16:20 -0700 -Subject: [PATCH 10/13] Add newsfragment - ---- - src/twisted/web/newsfragments/10323.bugfix | 1 + - 1 file changed, 1 insertion(+) - create mode 100644 src/twisted/web/newsfragments/10323.bugfix - -diff --git a/src/twisted/web/newsfragments/10323.bugfix b/src/twisted/web/newsfragments/10323.bugfix -new file mode 100644 -index 00000000000..6aad08919b4 ---- /dev/null -+++ b/src/twisted/web/newsfragments/10323.bugfix -@@ -0,0 +1 @@ -+Correct several defects in HTTP request parsing that could permit HTTP request smugling: disallow signed Content-Length headers, forbid illegal characters in chunked extensions, forbid 0x prefix to chunk lengths, and only strip space and horizontal tab from header values. GHSA-c2jg-hw38-jrqq - -From fbb001e9a66bdc97507e930388a7999fca323706 Mon Sep 17 00:00:00 2001 -From: Tom Most -Date: Thu, 31 Mar 2022 21:47:23 -0700 -Subject: [PATCH 11/13] Add CVE to the newsfragment - ---- - src/twisted/web/newsfragments/10323.bugfix | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/twisted/web/newsfragments/10323.bugfix b/src/twisted/web/newsfragments/10323.bugfix -index 6aad08919b4..a123aa89862 100644 ---- a/src/twisted/web/newsfragments/10323.bugfix -+++ b/src/twisted/web/newsfragments/10323.bugfix -@@ -1 +1 @@ --Correct several defects in HTTP request parsing that could permit HTTP request smugling: disallow signed Content-Length headers, forbid illegal characters in chunked extensions, forbid 0x prefix to chunk lengths, and only strip space and horizontal tab from header values. GHSA-c2jg-hw38-jrqq -+Correct several defects in HTTP request parsing that could permit HTTP request smugling: disallow signed Content-Length headers, forbid illegal characters in chunked extensions, forbid 0x prefix to chunk lengths, and only strip space and horizontal tab from header values. Addresses CVE-2022-24801 and GHSA-c2jg-hw38-jrqq. - -From 2bbd6c89110f0d44d2bb109c14d787f65bca9df8 Mon Sep 17 00:00:00 2001 -From: Tom Most -Date: Fri, 1 Apr 2022 20:47:59 -0700 -Subject: [PATCH 12/13] Address review feedback - ---- - src/twisted/web/http.py | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/src/twisted/web/http.py b/src/twisted/web/http.py -index e8327f03f2c..b80a55a2b00 100644 ---- a/src/twisted/web/http.py -+++ b/src/twisted/web/http.py -@@ -420,7 +420,7 @@ def _ishexdigits(b: bytes) -> bool: - for c in b: - if c not in b"0123456789abcdefABCDEF": - return False -- return bool(b) -+ return b != b"" - - - def _hexint(b: bytes) -> int: -@@ -1835,14 +1835,14 @@ def noMoreData(self): - """ - Characters that are valid in a chunk extension. - --See RFC 7230 section 4.1.1: -+See RFC 7230 section 4.1.1:: - - chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) - - chunk-ext-name = token - chunk-ext-val = token / quoted-string - --Section 3.2.6: -+And section 3.2.6:: - - token = 1*tchar - - -From a5138047c6bccb23eae2c1ecf4fa2fe17890872c Mon Sep 17 00:00:00 2001 -From: Tom Most -Date: Fri, 1 Apr 2022 21:38:48 -0700 -Subject: [PATCH 13/13] Revise newsfragment - ---- - src/twisted/web/newsfragments/10323.bugfix | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/twisted/web/newsfragments/10323.bugfix b/src/twisted/web/newsfragments/10323.bugfix -index a123aa89862..1890b1b58e1 100644 ---- a/src/twisted/web/newsfragments/10323.bugfix -+++ b/src/twisted/web/newsfragments/10323.bugfix -@@ -1 +1 @@ --Correct several defects in HTTP request parsing that could permit HTTP request smugling: disallow signed Content-Length headers, forbid illegal characters in chunked extensions, forbid 0x prefix to chunk lengths, and only strip space and horizontal tab from header values. Addresses CVE-2022-24801 and GHSA-c2jg-hw38-jrqq. -+twisted.web.http had several several defects in HTTP request parsing that could permit HTTP request smuggling. It now disallows signed Content-Length headers, forbids illegal characters in chunked extensions, forbids 0x prefix to chunk lengths, and only strips spaces and horizontal tab characters from header values. These changes address CVE-2022-24801 and GHSA-c2jg-hw38-jrqq. diff --git a/Twisted-22.2.0.tar.gz b/Twisted-22.2.0.tar.gz deleted file mode 100644 index 83d4a0b..0000000 --- a/Twisted-22.2.0.tar.gz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:57f32b1f6838facb8c004c89467840367ad38e9e535f8252091345dba500b4f2 -size 3733470 diff --git a/Twisted-22.4.0.tar.gz b/Twisted-22.4.0.tar.gz new file mode 100644 index 0000000..db1f851 --- /dev/null +++ b/Twisted-22.4.0.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a047990f57dfae1e0bd2b7df2526d4f16dcdc843774dc108b78c52f2a5f13680 +size 3744412 diff --git a/_multibuild b/_multibuild new file mode 100644 index 0000000..fcc7b97 --- /dev/null +++ b/_multibuild @@ -0,0 +1,3 @@ + + test + diff --git a/python-Twisted-rpmlintrc b/python-Twisted-rpmlintrc deleted file mode 100644 index 1307eaf..0000000 --- a/python-Twisted-rpmlintrc +++ /dev/null @@ -1,2 +0,0 @@ -addFilter("pem-certificate .*test/fake_CAs/.*.pem") -addFilter("pem-certificate .*doc/core/examples/.*.pem") \ No newline at end of file diff --git a/python-Twisted.changes b/python-Twisted.changes index bd294f1..ac4cd1f 100644 --- a/python-Twisted.changes +++ b/python-Twisted.changes @@ -1,3 +1,53 @@ +------------------------------------------------------------------- +Tue Jun 21 18:08:20 UTC 2022 - Ben Greiner + +- Update to 22.4.0 + * twisted.python.failure.Failure tracebacks now capture module + information, improving compatibility with the Raven Sentry + client. (#7796) + * twisted.python.failure.Failure objects are now compatible with + dis.distb, improving compatibility with post-mortem debuggers. + (#9599) + * twisted.internet.interfaces.IReactorSSL.listenSSL now has + correct type annotations. (#10274) + * twisted.internet.test.test_glibbase.GlibReactorBaseTests now + passes. (#10317) + * Conch + - twisted.conch.ssh now supports using RSA keys with SHA-2 + signatures (RFC 8332) when acting as a server. The + rsa-sha2-512 and rsa-sha2-256 public key signature algorithms + are automatically preferred over ssh-rsa if the client + advertises support for them; the actual public keys do not + need to change. (#9765) + - twisted.conch.ssh now has an alternative Ed25519 + implementation using PyNaCl, in order to support platforms + that lack OpenSSL >= 1.1.1b. The new "conch_nacl" extra has + the necessary dependency. (#10208) + * Web + - Twisted is now compatible with h2 4.x.x. (#10182) + - twisted.web.http had several several defects in HTTP request + parsing that could permit HTTP request smuggling. It now + disallows signed Content-Length headers, forbids illegal + characters in chunked extensions, forbids a ``0x`` prefix to + chunk lengths, and only strips spaces and horizontal tab + characters from header values. These changes address + CVE-2022-24801 and GHSA-c2jg-hw38-jrqq. (#10323) + * Mail + - twisted.mail.pop3.APOPCredentials is now correctly marked as + implementing twisted.cred.credentials.IUsernamHashedPassword, + rather than IUsernamePassword. (#10305) + * Trial + - `trial --until-failure --jobs=N` now reports the number of + each test pass as it begins. (#10312) + - twisted.trial.unittest.TestCase now discards cleanup + functions after running them. Notably, this prevents them + from being run an ever growing number of times with trial -u + .... (#10320) +- Drop CVE-2022-24801-http-1.1-leniency.patch fixed upstream +- Refresh remove-dependency-version-upper-bounds.patch +- Move extra_requires optional dependencies to meta sub packages +- Create :test multibuild flavor + ------------------------------------------------------------------- Wed May 4 06:39:10 UTC 2022 - Steve Kowalik diff --git a/python-Twisted.spec b/python-Twisted.spec index 349b114..ccab0b2 100644 --- a/python-Twisted.spec +++ b/python-Twisted.spec @@ -1,5 +1,5 @@ # -# spec file for package python-Twisted +# spec file # # Copyright (c) 2022 SUSE LLC # @@ -16,16 +16,24 @@ # +%global flavor @BUILD_FLAVOR@%{nil} +%if "%{flavor}" == "test" +%bcond_without test +%define psuffix -test +%else +%bcond_with test +%define psuffix %{nil} +%endif + %{?!python_module:%define python_module() python3-%{**}} %define skip_python2 1 -Name: python-Twisted -Version: 22.2.0 +Name: python-Twisted%{psuffix} +Version: 22.4.0 Release: 0 Summary: An asynchronous networking framework written in Python License: MIT URL: https://twistedmatrix.com/ Source0: https://files.pythonhosted.org/packages/source/T/Twisted/Twisted-%{version}.tar.gz -Source99: python-Twisted-rpmlintrc Patch0: skip_MultiCast.patch # PATCH-FIX-UPSTREAM no-test_successResultOfWithFailureHasTraceback.patch https://twistedmatrix.com/trac/ticket/9665 mcepl@suse.com # skip over the test test_successResultOfWithFailureHasTraceback @@ -41,53 +49,31 @@ Patch5: no-pygtkcompat.patch Patch6: remove-dependency-version-upper-bounds.patch # PATCH-FIX-OPENSUSE Skip test that is broken with Expat >= 2.4.5 Patch7: skip-namespacewithwhitespace.patch -# PATCH-FIX-UPSTREAM Address CVE-2022-24801, can be dropped next upstream release -Patch8: CVE-2022-24801-http-1.1-leniency.patch -BuildRequires: %{python_module Automat >= 0.8.0} -BuildRequires: %{python_module PyHamcrest >= 1.9.0} -BuildRequires: %{python_module appdirs >= 1.4.0} -BuildRequires: %{python_module attrs >= 19.2.0} -BuildRequires: %{python_module bcrypt >= 3.0.0} -BuildRequires: %{python_module constantly >= 15.1} -BuildRequires: %{python_module cryptography >= 2.6} -BuildRequires: %{python_module devel} -BuildRequires: %{python_module h2 >= 3.0} -BuildRequires: %{python_module hyperlink >= 17.1.1} -BuildRequires: %{python_module idna >= 2.4} BuildRequires: %{python_module incremental >= 21.3.0} -BuildRequires: %{python_module pyOpenSSL >= 16.0.0} -BuildRequires: %{python_module pyasn1} -BuildRequires: %{python_module pyserial >= 3.0} -BuildRequires: %{python_module pyserial} -BuildRequires: %{python_module pytest} -BuildRequires: %{python_module python-subunit} -BuildRequires: %{python_module pytz} -BuildRequires: %{python_module service_identity >= 18.1.0} BuildRequires: %{python_module setuptools} -BuildRequires: %{python_module typing_extensions >= 3.6.5} -BuildRequires: %{python_module zope.interface >= 4.4.2} BuildRequires: fdupes BuildRequires: git-core BuildRequires: python-rpm-macros -Requires: python-Automat >= 0.8.0 -Requires: python-PyHamcrest >= 1.9.0 -Requires: python-appdirs >= 1.4.0 -Requires: python-attrs >= 19.2.0 -Requires: python-bcrypt >= 3.0.0 -Requires: python-constantly >= 15.1 -Requires: python-cryptography >= 2.6 -Requires: python-h2 >= 3.0 -Requires: python-hyperlink >= 17.1.1 -Requires: python-idna >= 2.4 -Requires: python-incremental >= 21.3.0 -Requires: python-pyOpenSSL >= 16.0.0 -Requires: python-pyasn1 -Requires: python-pyserial >= 3.0 -Requires: python-service_identity >= 18.1.0 -Requires: python-typing_extensions >= 3.6.5 -Requires: python-zope.interface >= 4.4.2 Requires(post): update-alternatives Requires(postun):update-alternatives +# SECTION install requires +Requires: python-Automat >= 0.8.0 +Requires: python-attrs >= 19.2.0 +Requires: python-constantly >= 15.1 +Requires: python-hyperlink >= 17.1.1 +Requires: python-incremental >= 21.3.0 +Requires: python-typing_extensions >= 3.6.5 +Requires: python-zope.interface >= 4.4.2 +# /SECTION +# twisted[tls] is so common, let's keep it tied to the main package for the time being. +Requires: python-Twisted-tls = %{version} +%if %{with test} +BuildRequires: %{python_module Twisted-all_non_platform = %{version}} +BuildRequires: %{python_module Twisted-conch_nacl = %{version}} +# declared nowhere but required to pass 8 tests with timezone checks +BuildRequires: %{python_module pytz} +%endif +BuildArch: noarch %python_subpackages %description @@ -103,13 +89,105 @@ on event-based network programming and multiprotocol integration. This package contains the documentation for python-Twisted +%package tls +Summary: TLS support for Twisted +Requires: python-Twisted = %{version} +Requires: python-idna >= 2.4 +Requires: python-pyOpenSSL >= 16.0.0 +Requires: python-service_identity >= 18.1.0 + +%description tls +Twisted is an extensible framework for Python programming, with special focus +on event-based network programming and multiprotocol integration. + +This metapackage is for the optional feature tls + +%package conch +Summary: Conch for Twisted +Requires: python-Twisted = %{version} +Requires: python-appdirs >= 1.4.0 +Requires: python-bcrypt >= 3.0.0 +Requires: python-cryptography >= 2.6 +Requires: python-pyasn1 + +%description conch +Twisted is an extensible framework for Python programming, with special focus +on event-based network programming and multiprotocol integration. + +Twisted Conch: The Twisted Shell. Terminal emulation, SSHv2 and telnet. + +%package conch_nacl +Summary: Conch w/ NaCl for Twisted +Requires: python-PyNaCl +Requires: python-Twisted-conch = %{version} + +%description conch_nacl +Twisted is an extensible framework for Python programming, with special focus +on event-based network programming and multiprotocol integration. + +This metapackage is for the optional feature Conch with NaCl + +%package serial +Summary: Serial support for Twisted +Requires: python-Twisted = %{version} +Requires: python-pyserial >= 3.0 + +%description serial +Twisted is an extensible framework for Python programming, with special focus +on event-based network programming and multiprotocol integration. + +This metapackage is for the optional feature serial + +%package http2 +Summary: HTTP/2 support for Twisted +Requires: python-Twisted = %{version} +Requires: python-h2 >= 3.0 +Requires: python-priority >= 1.1.0 + +%description http2 +Twisted is an extensible framework for Python programming, with special focus +on event-based network programming and multiprotocol integration. + +This metapackage is for the optional feature http2 + +%package contextvars +Summary: Contextvars extra for Twisted +Requires: python-Twisted = %{version} +%if 0%{?python_version_nodots} < 37 +Requires: python-contextvars >= 2.4 +%endif + +%description contextvars +Twisted is an extensible framework for Python programming, with special focus +on event-based network programming and multiprotocol integration. + +This metapackage is for the optional dependency contextvars + +%package all_non_platform +Summary: The all_non_platform dependency extra for Twisted +Requires: python-PyHamcrest >= 1.9.0 +Requires: python-Twisted-conch = %{version} +Requires: python-Twisted-contextvars = %{version} +Requires: python-Twisted-http2 = %{version} +Requires: python-Twisted-serial = %{version} +Requires: python-Twisted-tls = %{version} + +%description all_non_platform +Twisted is an extensible framework for Python programming, with special focus +on event-based network programming and multiprotocol integration. + +This metapackage is for the optional dependency all_non_platform + %prep %autosetup -p1 -n Twisted-%{version} sed -i '1{/env python/d}' src/twisted/mail/test/pop3testserver.py src/twisted/trial/test/scripttest.py +%if ! %{with test} %build %python_build +%endif +%if ! %{with test} %install %python_install find %{buildroot} -regex '.*\.[ch]' -exec rm {} ";" # Remove leftover C sources @@ -136,12 +214,15 @@ cp -r docs/* %{buildroot}%{_docdir}/%{name}-doc/ # empty files rm %{buildroot}%{_docdir}/%{name}-doc/{fun/Twisted.Quotes,_static/.placeholder,_templates/.placeholder} %fdupes %{buildroot}%{_docdir}/%{name}-doc +%endif +%if %{with test} %check export LANG=en_US.UTF-8 export PYTHONDONTWRITEBYTECODE=1 -%{python_expand # provide flavored commands for testing (=not yet available python_flavored_alternatives from gh#openSUSE/python-rpm-macros#120) +%{python_expand # provide flavored commands for testing +# (= python_flavored_alternatives from gh#openSUSE/python-rpm-macros#120, but sadly not available for non-TW) mkdir -p build/bin/ for f in %{buildroot}%{_bindir}/*-%{$python_bin_suffix}; do ln -s $f build/bin/$(basename ${f%%%%-%{$python_bin_suffix}}) @@ -154,6 +235,7 @@ export OPENSSL_SYSTEM_CIPHERS_OVERRIDE=xyz_nonexistent_file export OPENSSL_CONF='' %python_expand PYTHONPATH=%{buildroot}%{$python_sitelib} $python -m twisted.trial twisted +%endif %post # these were master alternatives until Dec 2020. Remove before the install as slave links @@ -166,28 +248,51 @@ done %postun %python_uninstall_alternative twistd +%if ! %{with test} %files -n %{name}-doc %doc %{_docdir}/%{name}-doc +%files %{python_files tls} +%license LICENSE + +%files %{python_files conch} +%license LICENSE + +%files %{python_files conch_nacl} +%license LICENSE + +%files %{python_files serial} +%license LICENSE + +%files %{python_files http2} +%license LICENSE + +%files %{python_files contextvars} +%license LICENSE + +%files %{python_files all_non_platform} +%license LICENSE + %files %{python_files} %license LICENSE %doc NEWS.rst README.rst +%python_alternative %{_bindir}/conch +%python_alternative %{_bindir}/tkconch +%python_alternative %{_mandir}/man1/conch.1%{?ext_man} +%python_alternative %{_mandir}/man1/tkconch.1%{?ext_man} %python_alternative %{_bindir}/twistd %python_alternative %{_bindir}/cftp %python_alternative %{_bindir}/ckeygen -%python_alternative %{_bindir}/conch %python_alternative %{_bindir}/pyhtmlizer -%python_alternative %{_bindir}/tkconch %python_alternative %{_bindir}/trial %python_alternative %{_bindir}/twist %python_alternative %{_mandir}/man1/twistd.1%{?ext_man} %python_alternative %{_mandir}/man1/cftp.1%{?ext_man} %python_alternative %{_mandir}/man1/ckeygen.1%{?ext_man} -%python_alternative %{_mandir}/man1/conch.1%{?ext_man} %python_alternative %{_mandir}/man1/pyhtmlizer.1%{?ext_man} -%python_alternative %{_mandir}/man1/tkconch.1%{?ext_man} %python_alternative %{_mandir}/man1/trial.1%{?ext_man} %{python_sitelib}/twisted %{python_sitelib}/Twisted-%{version}*-info +%endif %changelog diff --git a/remove-dependency-version-upper-bounds.patch b/remove-dependency-version-upper-bounds.patch index c3794bb..734f162 100644 --- a/remove-dependency-version-upper-bounds.patch +++ b/remove-dependency-version-upper-bounds.patch @@ -1,15 +1,18 @@ -Index: Twisted-22.1.0/setup.cfg +Index: Twisted-22.4.0/setup.cfg =================================================================== ---- Twisted-22.1.0.orig/setup.cfg -+++ Twisted-22.1.0/setup.cfg -@@ -69,8 +69,8 @@ serial = +--- Twisted-22.4.0.orig/setup.cfg ++++ Twisted-22.4.0/setup.cfg +@@ -72,10 +72,10 @@ serial = pyserial >= 3.0 pywin32 != 226; platform_system == "Windows" http2 = -- h2 >= 3.0, < 4.0 +- h2 >= 3.0, < 5.0 - priority >= 1.1.0, < 2.0 + h2 >= 3.0 + priority >= 1.1.0 contextvars = - contextvars >= 2.4, < 3; python_version < "3.7" +- contextvars >= 2.4, < 3; python_version < "3.7" ++ contextvars >= 2.4; python_version < "3.7" all_non_platform = + %(test)s + %(tls)s