Skip to content

Commit

Permalink
Merge branch 'master' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
reivilibre committed Jul 30, 2024
2 parents be72672 + e4868f8 commit 53db8a9
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 57 deletions.
20 changes: 20 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,26 @@
* Bump ulid from 1.1.2 to 1.1.3. ([\#17442](https://github.com/element-hq/synapse/issues/17442))
* Bump zipp from 3.15.0 to 3.19.1. ([\#17427](https://github.com/element-hq/synapse/issues/17427))

# Synapse 1.111.1 (2024-07-30)

This security release is to update our locked dependency on Twisted to 24.7.0rc1, which includes a security fix for [CVE-2024-41671 / GHSA-c8m8-j448-xjx7: Disordered HTTP pipeline response in twisted.web, again](https://github.com/twisted/twisted/security/advisories/GHSA-c8m8-j448-xjx7).

This issue means that, if multiple HTTP requests are pipelined in the same TCP connection, Synapse can send responses to the wrong HTTP request.
If a reverse proxy was configured to use HTTP pipelining, this could result in responses being sent to the wrong user, severely harming confidentiality.

With that said, despite being a high severity issue, **we consider it unlikely that Synapse installations will be affected**.
The use of HTTP pipelining in this fashion would cause worse performance for clients (request-response latencies would be increased as users' responses would be artificially blocked behind other users' slow requests). Further, Nginx and Haproxy, two common reverse proxies, do not appear to support configuring their upstreams to use HTTP pipelining and thus would not be affected. For both of these reasons, we consider it unlikely that a Synapse deployment would be set up in such a configuration.

Despite that, we cannot rule out that some installations may exist with this unusual setup and so we are releasing this security update today.

**pip users:** Note that by default, upgrading Synapse using pip will not automatically upgrade Twisted. **Please manually install the new version of Twisted** using `pip install Twisted==24.7.0rc1`. Note also that even the `--upgrade-strategy=eager` flag to `pip install -U matrix-synapse` will not upgrade Twisted to a patched version because it is only a release candidate at this time.


### Internal Changes

- Upgrade locked dependency on Twisted to 24.7.0rc1. ([\#17502](https://github.com/element-hq/synapse/issues/17502))


# Synapse 1.111.0 (2024-07-16)

No significant changes since 1.111.0rc2.
Expand Down
6 changes: 6 additions & 0 deletions debian/changelog
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ matrix-synapse-py3 (1.112.0~rc1) stable; urgency=medium

-- Synapse Packaging team <packages@matrix.org> Tue, 23 Jul 2024 08:58:55 -0600

matrix-synapse-py3 (1.111.1) stable; urgency=medium

* New Synapse release 1.111.1.

-- Synapse Packaging team <packages@matrix.org> Tue, 30 Jul 2024 16:13:52 +0100

matrix-synapse-py3 (1.111.0) stable; urgency=medium

* New Synapse release 1.111.0.
Expand Down
63 changes: 22 additions & 41 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 10 additions & 2 deletions synapse/http/proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@
"Upgrade",
}

if hasattr(Headers, "_canonicalNameCaps"):
# Twisted < 24.7.0rc1
_canonicalHeaderName = Headers()._canonicalNameCaps # type: ignore[attr-defined]
else:
# Twisted >= 24.7.0rc1
# But note that `_encodeName` still exists on prior versions,
# it just encodes differently
_canonicalHeaderName = Headers()._encodeName


def parse_connection_header_value(
connection_header_value: Optional[bytes],
Expand All @@ -85,11 +94,10 @@ def parse_connection_header_value(
The set of header names that should not be copied over from the remote response.
The keys are capitalized in canonical capitalization.
"""
headers = Headers()
extra_headers_to_remove: Set[str] = set()
if connection_header_value:
extra_headers_to_remove = {
headers._canonicalNameCaps(connection_option.strip()).decode("ascii")
_canonicalHeaderName(connection_option.strip()).decode("ascii")
for connection_option in connection_header_value.split(b",")
}

Expand Down
4 changes: 3 additions & 1 deletion synapse/http/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
from synapse.config.homeserver import HomeServerConfig
from synapse.logging.context import defer_to_thread, preserve_fn, run_in_background
from synapse.logging.opentracing import active_span, start_active_span, trace_servlet
from synapse.types import ISynapseReactor
from synapse.util import json_encoder
from synapse.util.caches import intern_dict
from synapse.util.cancellation import is_function_cancellable
Expand Down Expand Up @@ -868,7 +869,8 @@ def encode(opentracing_span: "Optional[opentracing.Span]") -> bytes:

with start_active_span("encode_json_response"):
span = active_span()
json_str = await defer_to_thread(request.reactor, encode, span)
reactor: ISynapseReactor = request.reactor # type: ignore
json_str = await defer_to_thread(reactor, encode, span)

_write_bytes_to_request(request, json_str)

Expand Down
2 changes: 1 addition & 1 deletion synapse/http/site.py
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,7 @@ def request_factory(channel: HTTPChannel, queued: bool) -> Request:
self.access_logger = logging.getLogger(logger_name)
self.server_version_string = server_version_string.encode("ascii")

def log(self, request: SynapseRequest) -> None:
def log(self, request: SynapseRequest) -> None: # type: ignore[override]
pass


Expand Down
5 changes: 2 additions & 3 deletions tests/rest/client/test_login.py
Original file line number Diff line number Diff line change
Expand Up @@ -969,9 +969,8 @@ def test_cas_redirect_confirm(self) -> None:
# Test that the response is HTML.
self.assertEqual(channel.code, 200, channel.result)
content_type_header_value = ""
for header in channel.result.get("headers", []):
if header[0] == b"Content-Type":
content_type_header_value = header[1].decode("utf8")
for header in channel.headers.getRawHeaders("Content-Type", []):
content_type_header_value = header

self.assertTrue(content_type_header_value.startswith("text/html"))

Expand Down
26 changes: 22 additions & 4 deletions tests/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,17 +198,35 @@ def code(self) -> int:
def headers(self) -> Headers:
if not self.result:
raise Exception("No result yet.")
h = Headers()
for i in self.result["headers"]:
h.addRawHeader(*i)

h = self.result["headers"]
assert isinstance(h, Headers)
return h

def writeHeaders(
self, version: bytes, code: bytes, reason: bytes, headers: Headers
self,
version: bytes,
code: bytes,
reason: bytes,
headers: Union[Headers, List[Tuple[bytes, bytes]]],
) -> None:
self.result["version"] = version
self.result["code"] = code
self.result["reason"] = reason

if isinstance(headers, list):
# Support prior to Twisted 24.7.0rc1
new_headers = Headers()
for k, v in headers:
assert isinstance(k, bytes), f"key is not of type bytes: {k!r}"
assert isinstance(v, bytes), f"value is not of type bytes: {v!r}"
new_headers.addRawHeader(k, v)
headers = new_headers

assert isinstance(
headers, Headers
), f"headers are of the wrong type: {headers!r}"

self.result["headers"] = headers

def write(self, data: bytes) -> None:
Expand Down
9 changes: 4 additions & 5 deletions tests/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -392,8 +392,7 @@ async def callback(request: SynapseRequest, **kwargs: object) -> None:
)

self.assertEqual(channel.code, 301)
headers = channel.result["headers"]
location_headers = [v for k, v in headers if k == b"Location"]
location_headers = channel.headers.getRawHeaders(b"Location", [])
self.assertEqual(location_headers, [b"/look/an/eagle"])

def test_redirect_exception_with_cookie(self) -> None:
Expand All @@ -415,10 +414,10 @@ async def callback(request: SynapseRequest, **kwargs: object) -> NoReturn:
)

self.assertEqual(channel.code, 304)
headers = channel.result["headers"]
location_headers = [v for k, v in headers if k == b"Location"]
headers = channel.headers
location_headers = headers.getRawHeaders(b"Location", [])
self.assertEqual(location_headers, [b"/no/over/there"])
cookies_headers = [v for k, v in headers if k == b"Set-Cookie"]
cookies_headers = headers.getRawHeaders(b"Set-Cookie", [])
self.assertEqual(cookies_headers, [b"session=yespls"])

def test_head_request(self) -> None:
Expand Down

0 comments on commit 53db8a9

Please sign in to comment.