Only redirect to sanitized path under /web

This commit is contained in:
Unrud 2022-01-18 18:20:15 +01:00
parent d1532aa466
commit 4ed77cabc6
3 changed files with 24 additions and 21 deletions

View File

@ -193,12 +193,6 @@ class Application(ApplicationPartDelete, ApplicationPartHead,
# Sanitize request URI (a WSGI server indicates with an empty path, # Sanitize request URI (a WSGI server indicates with an empty path,
# that the URL targets the application root without a trailing slash) # that the URL targets the application root without a trailing slash)
path = pathutils.sanitize_path(unsafe_path) path = pathutils.sanitize_path(unsafe_path)
if unsafe_path != path and request_method in ["GET", "HEAD"]:
location = base_prefix + path
logger.info("Redirecting to sanitized path: %r ==> %r",
base_prefix + unsafe_path, location)
return response(*httputils.redirect(
location, client.MOVED_PERMANENTLY))
logger.debug("Sanitized path: %r", path) logger.debug("Sanitized path: %r", path)
# Get function corresponding to method # Get function corresponding to method

View File

@ -60,11 +60,18 @@ class ApplicationPartGet(ApplicationBase):
def do_GET(self, environ: types.WSGIEnviron, base_prefix: str, path: str, def do_GET(self, environ: types.WSGIEnviron, base_prefix: str, path: str,
user: str) -> types.WSGIResponse: user: str) -> types.WSGIResponse:
"""Manage GET request.""" """Manage GET request."""
# Redirect to .web if the root URL is requested # Redirect to /.web if the root path is requested
if not pathutils.strip_path(path): if not pathutils.strip_path(path):
return httputils.redirect(".web") return httputils.redirect(base_prefix + "/.web")
# Dispatch .web URL to web module
if path == "/.web" or path.startswith("/.web/"): if path == "/.web" or path.startswith("/.web/"):
# Redirect to sanitized path for all subpaths of /.web
unsafe_path = environ.get("PATH_INFO", "")
if unsafe_path != path:
location = base_prefix + path
logger.info("Redirecting to sanitized path: %r ==> %r",
base_prefix + unsafe_path, location)
return httputils.redirect(location, client.MOVED_PERMANENTLY)
# Dispatch /.web path to web module
return self._web.get(environ, base_prefix, path, user) return self._web.get(environ, base_prefix, path, user)
access = Access(self._rights, user, path) access = Access(self._rights, user, path)
if not access.check("r") and "i" not in access.permissions: if not access.check("r") and "i" not in access.permissions:

View File

@ -53,26 +53,28 @@ permissions: RrWw""")
def test_root(self) -> None: def test_root(self) -> None:
"""GET request at "/".""" """GET request at "/"."""
status, headers, answer = self.request("GET", "/", check=302) for path in ["", "/", "//"]:
assert headers.get("Location") == ".web" _, headers, answer = self.request("GET", path, check=302)
assert answer == "Redirected to .web" assert headers.get("Location") == "/.web"
assert answer == "Redirected to /.web"
def test_root_script_name(self) -> None: def test_root_script_name(self) -> None:
"""GET request at "/" with SCRIPT_NAME.""" """GET request at "/" with SCRIPT_NAME."""
_, answer = self.get("/", check=302, SCRIPT_NAME="/radicale") for path in ["", "/", "//"]:
assert answer == "Redirected to .web" _, headers, _ = self.request("GET", path, check=302,
SCRIPT_NAME="/radicale")
assert headers.get("Location") == "/radicale/.web"
def test_sanitized_path(self) -> None: def test_sanitized_path(self) -> None:
"""GET request with unsanitized paths.""" """GET request with unsanitized paths."""
for path, sane_path in [("//", "/"), ("", "/"), ("/a//b", "/a/b"), for path, sane_path in [
("/a//b/", "/a/b/")]: ("//.web", "/.web"), ("//.web/", "/.web/"),
_, headers, answer = self.request("GET", path, check=301) ("/.web//", "/.web/"), ("/.web/a//b", "/.web/a/b")]:
_, headers, _ = self.request("GET", path, check=301)
assert headers.get("Location") == sane_path assert headers.get("Location") == sane_path
assert answer == "Redirected to %s" % sane_path _, headers, _ = self.request("GET", path, check=301,
_, headers, answer = self.request("GET", path, check=301,
SCRIPT_NAME="/radicale") SCRIPT_NAME="/radicale")
assert headers.get("Location") == "/radicale%s" % sane_path assert headers.get("Location") == "/radicale%s" % sane_path
assert answer == "Redirected to /radicale%s" % sane_path
def test_add_event(self) -> None: def test_add_event(self) -> None:
"""Add an event.""" """Add an event."""