diff --git a/docs/source/changelog.md b/docs/source/changelog.md index d669a1f0..3a37ce9f 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -2,7 +2,10 @@ ## 4.2 -### v4.2.0 - 2024-06-DD +### v4.2.0 - 2024-06-11 + +This release includes an important security patch for +[CVE-2024-35225 ](https://github.com/jupyterhub/jupyter-server-proxy/security/advisories/GHSA-fvcq-4x64-hqxr). ([full changelog](https://github.com/jupyterhub/jupyter-server-proxy/compare/v4.1.2...v4.2.0)) diff --git a/jupyter_server_proxy/handlers.py b/jupyter_server_proxy/handlers.py index 272e7c9c..2d65150d 100644 --- a/jupyter_server_proxy/handlers.py +++ b/jupyter_server_proxy/handlers.py @@ -323,14 +323,11 @@ async def proxy(self, host, port, proxied_path): """ if not self._check_host_allowlist(host): - self.set_status(403) - self.write( - "Host '{host}' is not allowed. " - "See https://jupyter-server-proxy.readthedocs.io/en/latest/arbitrary-ports-hosts.html for info.".format( - host=host - ) + raise web.HTTPError( + 403, + f"Host '{host}' is not allowed. " + "See https://jupyter-server-proxy.readthedocs.io/en/latest/arbitrary-ports-hosts.html for info.", ) - return # Remove hop-by-hop headers that don't necessarily apply to the request we are making # to the backend. See https://github.com/jupyterhub/jupyter-server-proxy/pull/328 @@ -391,9 +388,7 @@ async def proxy(self, host, port, proxied_path): # Ref: https://www.tornadoweb.org/en/stable/httpclient.html#tornado.httpclient.AsyncHTTPClient.fetch if err.code == 599: self._record_activity() - self.set_status(599) - self.write(str(err)) - return + raise web.HTTPError(599, str(err)) else: raise @@ -402,8 +397,7 @@ async def proxy(self, host, port, proxied_path): # For all non http errors... if response.error and type(response.error) is not httpclient.HTTPError: - self.set_status(500) - self.write(str(response.error)) + raise web.HTTPError(500, str(response.error)) else: # Represent the original response as a RewritableResponse object. original_response = RewritableResponse(orig_response=response) diff --git a/tests/test_proxies.py b/tests/test_proxies.py index ef320e28..8c2de5a0 100644 --- a/tests/test_proxies.py +++ b/tests/test_proxies.py @@ -255,6 +255,17 @@ def test_server_proxy_host_absolute(a_server_port_and_token: Tuple[int, str]) -> assert "X-Proxycontextpath" not in s +@pytest.mark.parametrize("absolute", ["", "/absolute"]) +def test_server_proxy_host_invalid( + a_server_port_and_token: Tuple[int, str], absolute: str +) -> None: + PORT, TOKEN = a_server_port_and_token + r = request_get(PORT, f"/proxy{absolute}/:54321/", TOKEN) + assert r.code == 403 + s = r.read().decode("ascii") + assert "Host '<invalid>' is not allowed." in s + + def test_server_proxy_port_non_service_rewrite_response( a_server_port_and_token: Tuple[int, str] ) -> None: