From ea1c27237e42e16b158f46b0c659f2f58f26430e Mon Sep 17 00:00:00 2001 From: Simon Li Date: Wed, 22 May 2024 23:33:42 +0100 Subject: [PATCH] Escape invalid host when displayed in html Use html.escape Use web.HTTPError to safely return errors to browser --- jupyter_server_proxy/handlers.py | 16 +++++++--------- tests/test_proxies.py | 9 +++++++++ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/jupyter_server_proxy/handlers.py b/jupyter_server_proxy/handlers.py index 874e77e0..9525b662 100644 --- a/jupyter_server_proxy/handlers.py +++ b/jupyter_server_proxy/handlers.py @@ -307,10 +307,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)) - return + 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.", + ) # 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 @@ -360,9 +361,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 @@ -371,8 +370,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 c0605398..bd367117 100644 --- a/tests/test_proxies.py +++ b/tests/test_proxies.py @@ -187,6 +187,15 @@ def test_server_proxy_host_absolute(): assert 'X-Forwarded-Context' not in s assert 'X-Proxycontextpath' not in s + +@pytest.mark.parametrize("absolute", ["", "/absolute"]) +def test_server_proxy_host_invalid(absolute: str) -> None: + 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(): """Test that 'hello' is replaced by 'foo'.""" r = request_get(PORT, '/proxy/54321/hello', TOKEN)