diff --git a/radicale/app/__init__.py b/radicale/app/__init__.py index 18d9808..6896bc7 100644 --- a/radicale/app/__init__.py +++ b/radicale/app/__init__.py @@ -191,13 +191,16 @@ class Application(ApplicationPartDelete, ApplicationPartHead, base_prefix_src = ("HTTP_X_SCRIPT_NAME" if "HTTP_X_SCRIPT_NAME" in environ else "SCRIPT_NAME") base_prefix = environ.get(base_prefix_src, "") - if base_prefix and (base_prefix[0] != "/" or base_prefix[-1] == "/"): - logger.error("Base prefix (from %s) must %s with '/': %r", - base_prefix_src, "not end" if base_prefix[-1] == "/" - else "start", base_prefix) + if base_prefix and base_prefix[0] != "/": + logger.error("Base prefix (from %s) must start with '/': %r", + base_prefix_src, base_prefix) if base_prefix_src == "HTTP_X_SCRIPT_NAME": return response(*httputils.BAD_REQUEST) return response(*httputils.INTERNAL_SERVER_ERROR) + if base_prefix.endswith("/"): + logger.warning("Base prefix (from %s) must not end with '/': %r", + base_prefix_src, base_prefix) + base_prefix = base_prefix.rstrip("/") logger.debug("Base prefix (from %s): %r", base_prefix_src, base_prefix) # Sanitize request URI (a WSGI server indicates with an empty path, # that the URL targets the application root without a trailing slash) diff --git a/radicale/tests/test_base.py b/radicale/tests/test_base.py index f3d6fdb..f654f1c 100644 --- a/radicale/tests/test_base.py +++ b/radicale/tests/test_base.py @@ -67,8 +67,14 @@ permissions: RrWw""") def test_root_broken_script_name(self) -> None: """GET request at "/" with SCRIPT_NAME ending with "/".""" - for script_name in ["/", "/radicale/", "radicale"]: - self.get("/", check=500, SCRIPT_NAME=script_name) + for script_name, prefix in [ + ("/", ""), ("//", ""), ("/radicale/", "/radicale"), + ("radicale", None), ("radicale//", None)]: + _, headers, _ = self.request( + "GET", "/", check=500 if prefix is None else 302, + SCRIPT_NAME=script_name) + assert (prefix is None or + headers.get("Location") == prefix + "/.web") def test_root_http_x_script_name(self) -> None: """GET request at "/" with HTTP_X_SCRIPT_NAME.""" @@ -79,8 +85,14 @@ permissions: RrWw""") def test_root_broken_http_x_script_name(self) -> None: """GET request at "/" with HTTP_X_SCRIPT_NAME ending with "/".""" - for script_name in ["/", "/radicale/", "radicale"]: - self.get("/", check=400, HTTP_X_SCRIPT_NAME=script_name) + for script_name, prefix in [ + ("/", ""), ("//", ""), ("/radicale/", "/radicale"), + ("radicale", None), ("radicale//", None)]: + _, headers, _ = self.request( + "GET", "/", check=400 if prefix is None else 302, + HTTP_X_SCRIPT_NAME=script_name) + assert (prefix is None or + headers.get("Location") == prefix + "/.web") def test_sanitized_path(self) -> None: """GET request with unsanitized paths."""