diff --git a/datasette/utils/__init__.py b/datasette/utils/__init__.py index d92d0cd5..87147baf 100644 --- a/datasette/utils/__init__.py +++ b/datasette/utils/__init__.py @@ -735,7 +735,8 @@ class StaticMount(click.ParamType): param, ctx, ) - path, dirpath = value.split(":") + path, dirpath = value.split(":", 1) + dirpath = os.path.abspath(dirpath) if not os.path.exists(dirpath) or not os.path.isdir(dirpath): self.fail("%s is not a valid directory path" % value, param, ctx) return path, dirpath diff --git a/datasette/utils/asgi.py b/datasette/utils/asgi.py index 38ffc072..db7f9004 100644 --- a/datasette/utils/asgi.py +++ b/datasette/utils/asgi.py @@ -300,7 +300,11 @@ async def asgi_send_file( def asgi_static(root_path, chunk_size=4096, headers=None, content_type=None): async def inner_static(scope, receive, send): path = scope["url_route"]["kwargs"]["path"] - full_path = (Path(root_path) / path).absolute() + try: + full_path = (Path(root_path) / path).resolve().absolute() + except FileNotFoundError: + await asgi_send_html(send, "404", 404) + return # Ensure full_path is within root_path to avoid weird "../" tricks try: full_path.relative_to(root_path) diff --git a/tests/test_html.py b/tests/test_html.py index f76f98b9..0a6df984 100644 --- a/tests/test_html.py +++ b/tests/test_html.py @@ -67,6 +67,8 @@ def test_static_mounts(): assert response.status == 200 response = client.get("/custom-static/not_exists.py") assert response.status == 404 + response = client.get("/custom-static/../LICENSE") + assert response.status == 404 def test_memory_database_page():