Redirect GET and HEAD requests to sanitized path

This commit is contained in:
Unrud 2022-01-15 22:32:37 +01:00
parent 6dee974b74
commit b93842b10c
4 changed files with 29 additions and 17 deletions

View File

@ -161,6 +161,9 @@ class Application(ApplicationPartDelete, ApplicationPartHead,
# Return response content # Return response content
return status_text, list(headers.items()), answers return status_text, list(headers.items()), answers
time_begin = datetime.datetime.now()
request_method = environ["REQUEST_METHOD"].upper()
unsafe_path = environ.get("PATH_INFO", "")
remote_host = "unknown" remote_host = "unknown"
if environ.get("REMOTE_HOST"): if environ.get("REMOTE_HOST"):
remote_host = repr(environ["REMOTE_HOST"]) remote_host = repr(environ["REMOTE_HOST"])
@ -175,10 +178,8 @@ class Application(ApplicationPartDelete, ApplicationPartHead,
depthinfo = "" depthinfo = ""
if environ.get("HTTP_DEPTH"): if environ.get("HTTP_DEPTH"):
depthinfo = " with depth %r" % environ["HTTP_DEPTH"] depthinfo = " with depth %r" % environ["HTTP_DEPTH"]
time_begin = datetime.datetime.now() logger.info("%s request for %r%s received from %s%s",
logger.info( request_method, unsafe_path, depthinfo,
"%s request for %r%s received from %s%s",
environ["REQUEST_METHOD"], environ.get("PATH_INFO", ""), depthinfo,
remote_host, remote_useragent) remote_host, remote_useragent)
logger.debug("Request headers:\n%s", logger.debug("Request headers:\n%s",
pprint.pformat(self._scrub_headers(environ))) pprint.pformat(self._scrub_headers(environ)))
@ -191,12 +192,19 @@ class Application(ApplicationPartDelete, ApplicationPartHead,
logger.debug("Base prefix: %r", base_prefix) logger.debug("Base prefix: %r", base_prefix)
# 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(environ.get("PATH_INFO", "")) 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(
client.MOVED_PERMANENTLY,
{"Location": location, "Content-Type": "text/plain"},
"Redirected to %s" % location)
logger.debug("Sanitized path: %r", path) logger.debug("Sanitized path: %r", path)
# Get function corresponding to method # Get function corresponding to method
function = getattr( function = getattr(self, "do_%s" % request_method, None)
self, "do_%s" % environ["REQUEST_METHOD"].upper(), None)
if not function: if not function:
return response(*httputils.METHOD_NOT_ALLOWED) return response(*httputils.METHOD_NOT_ALLOWED)

View File

@ -62,13 +62,10 @@ class ApplicationPartGet(ApplicationBase):
"""Manage GET request.""" """Manage GET request."""
# Redirect to .web if the root URL is requested # Redirect to .web if the root URL is requested
if not pathutils.strip_path(path): if not pathutils.strip_path(path):
web_path = ".web" location = ".web"
if not environ.get("PATH_INFO"):
web_path = posixpath.join(posixpath.basename(base_prefix),
web_path)
return (client.FOUND, return (client.FOUND,
{"Location": web_path, "Content-Type": "text/plain"}, {"Location": location, "Content-Type": "text/plain"},
"Redirected to %s" % web_path) "Redirected to %s" % location)
# Dispatch .web URL to web module # Dispatch .web URL to web module
if path == "/.web" or path.startswith("/.web/"): if path == "/.web" or path.startswith("/.web/"):
return self._web.get(environ, base_prefix, path, user) return self._web.get(environ, base_prefix, path, user)

View File

@ -60,8 +60,15 @@ permissions: RrWw""")
"""GET request at "/" with SCRIPT_NAME.""" """GET request at "/" with SCRIPT_NAME."""
_, answer = self.get("/", check=302, SCRIPT_NAME="/radicale") _, answer = self.get("/", check=302, SCRIPT_NAME="/radicale")
assert answer == "Redirected to .web" assert answer == "Redirected to .web"
_, answer = self.get("", check=302, SCRIPT_NAME="/radicale")
assert answer == "Redirected to radicale/.web" def test_sanitized_path(self) -> None:
"""GET request with unsanitized paths."""
for path, sane_path in [("//", "/"), ("", "/"), ("/a//b", "/a/b"),
("/a//b/", "/a/b/")]:
_, answer = self.get(path, check=301)
assert answer == "Redirected to %s" % sane_path
_, answer = self.get(path, check=301, SCRIPT_NAME="/radicale")
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."""

View File

@ -28,7 +28,7 @@ const SERVER = location.origin;
* @const * @const
* @type {string} * @type {string}
*/ */
const ROOT_PATH = location.pathname.replace(new RegExp("/+[^/]+/*(/index\\.html?)?$"), "") + '/'; const ROOT_PATH = (new URL("..", location.href)).pathname;
/** /**
* Regex to match and normalize color * Regex to match and normalize color