Only redirect to sanitized path under /web
This commit is contained in:
		@@ -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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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."""
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user