From 69c4fde3f63323be14a241eebaae237c945250f4 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Wed, 31 May 2023 01:19:42 +0300 Subject: [PATCH] Don't allow dotfiles for /file= route (#4303) * Don't allow dotfiles or files in "dot directories" for /file= * update guide --------- Co-authored-by: Abubakar Abid --- CHANGELOG.md | 2 +- gradio/routes.py | 2 +- .../01_getting-started/03_sharing-your-app.md | 20 +++++++++++++------ test/test_routes.py | 20 +++++++++++++++++++ 4 files changed, 36 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2193cc4ad2d2..770f7448c7cfa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ ## Breaking Changes: -No changes to highlight. +- The `/file=` route no longer allows accessing dotfiles or files in "dot directories" by [@akx](https://github.com/akx) in [PR 4303](https://github.com/gradio-app/gradio/pull/4303) # 3.32.0 diff --git a/gradio/routes.py b/gradio/routes.py index 753c55c7c634b..055db04247185 100644 --- a/gradio/routes.py +++ b/gradio/routes.py @@ -326,7 +326,7 @@ async def file(path_or_url: str, request: fastapi.Request): utils.is_in_or_equal(abs_path, blocked_path) for blocked_path in blocks.blocked_paths ) - if in_blocklist: + if in_blocklist or any(part.startswith(".") for part in abs_path.parts): raise HTTPException(403, f"File not allowed: {path_or_url}.") in_app_dir = utils.abspath(app.cwd) in abs_path.parents diff --git a/guides/01_getting-started/03_sharing-your-app.md b/guides/01_getting-started/03_sharing-your-app.md index a01e39177d77d..281b5d5e7657c 100644 --- a/guides/01_getting-started/03_sharing-your-app.md +++ b/guides/01_getting-started/03_sharing-your-app.md @@ -188,14 +188,22 @@ Note that this approach also allows you run your Gradio apps on custom paths (`h ## Security and File Access -Sharing your Gradio app with others (by hosting it on Spaces, on your own server, or through temporary share links) **exposes** certain files on the host machine to users of your Gradio app. This is done so that Gradio apps are able to display output files created by Gradio or created by your prediction function. +Sharing your Gradio app with others (by hosting it on Spaces, on your own server, or through temporary share links) **exposes** certain files on the host machine to users of your Gradio app. -In particular, Gradio apps grant users access to three kinds of files: +In particular, Gradio apps ALLOW users to access to three kinds of files: -* Files in the same folder (or a subdirectory) of where the Gradio script is launched from. For example, if the path to your gradio scripts is `/home/usr/scripts/project/app.py` and you launch it from `/home/usr/scripts/project/`, then users of your shared Gradio app will be able to access any files inside `/home/usr/scripts/project/`. This is needed so that you can easily reference these files in your Gradio app. +* **Files in the same directory (or a subdirectory) of where the Gradio script is launched from.** For example, if the path to your gradio scripts is `/home/usr/scripts/project/app.py` and you launch it from `/home/usr/scripts/project/`, then users of your shared Gradio app will be able to access any files inside `/home/usr/scripts/project/`. This is done so that you can easily reference these files in your Gradio app (e.g. for your app's `examples`). -* Temporary files created by Gradio. These are files that are created by Gradio as part of running your prediction function. For example, if your prediction function returns a video file, then Gradio will save that video to a temporary file and then send the path to the temporary file to the front end. You can customize the location of temporary files created by Gradio by setting the environment variable GRADIO_TEMP_DIR to an absolute path, such as `/home/usr/scripts/project/temp/`. +* **Temporary files created by Gradio.** These are files that are created by Gradio as part of running your prediction function. For example, if your prediction function returns a video file, then Gradio will save that video to a temporary file and then send the path to the temporary file to the front end. You can customize the location of temporary files created by Gradio by setting the environment variable `GRADIO_TEMP_DIR` to an absolute path, such as `/home/usr/scripts/project/temp/`. -* Files that you explicitly allow via the `allowed_paths` parameter in `launch()`. This parameter allows you to pass in a list of additional directories or exact filepaths you'd like to allow users to have access to. (By default, this parameter is an empty list). +* **Files that you explicitly allow via the `allowed_paths` parameter in `launch()`**. This parameter allows you to pass in a list of additional directories or exact filepaths you'd like to allow users to have access to. (By default, this parameter is an empty list). -Users should NOT be able to access other arbitrary paths on the host. Furthermore, as a security measure, you can also **block** specific files or directories from being able to be accessed by users. To do this, pass in a list of additional directories or exact filepaths to the `blocked_paths` parameter in `launch()`. This parameter takes precedence over the files that Gradio exposes by default or by the `allowed_paths`. +Gradio DOES NOT ALLOW access to: + +* **Dotfiles** (any files whose name begins with `'.'`) or any files that are contained in any directory whose name begins with `'.'` + +* **Files that you explicitly allow via the `blocked_paths` parameter in `launch()`**. You can pass in a list of additional directories or exact filepaths to the `blocked_paths` parameter in `launch()`. This parameter takes precedence over the files that Gradio exposes by default or by the `allowed_paths`. + +* **Any other paths on the host machine**. Users should NOT be able to access other arbitrary paths on the host. + +Please make sure you are running the latest version of `gradio` for these security settings to apply. \ No newline at end of file diff --git a/test/test_routes.py b/test/test_routes.py index 918b459d7d97a..9729a9aa40673 100644 --- a/test/test_routes.py +++ b/test/test_routes.py @@ -3,6 +3,7 @@ import os import sys import tempfile +from contextlib import closing from pathlib import Path from unittest.mock import patch @@ -645,3 +646,22 @@ def test_orjson_serialization(): response = test_client.get("/") assert response.status_code == 200 demo.close() + + +def test_file_route_does_not_allow_dot_paths(tmp_path): + dot_file = tmp_path / ".env" + dot_file.write_text("secret=1234") + subdir = tmp_path / "subdir" + subdir.mkdir() + sub_dot_file = subdir / ".env" + sub_dot_file.write_text("secret=1234") + secret_sub_dir = tmp_path / ".versioncontrol" + secret_sub_dir.mkdir() + secret_sub_dir_regular_file = secret_sub_dir / "settings" + secret_sub_dir_regular_file.write_text("token = 8") + with closing(gr.Interface(lambda s: s.name, gr.File(), gr.File())) as io: + app, _, _ = io.launch(prevent_thread_lock=True) + client = TestClient(app) + assert client.get("/file=.env").status_code == 403 + assert client.get("/file=subdir/.env").status_code == 403 + assert client.get("/file=.versioncontrol/settings").status_code == 403