CVE-2024-1561 (Python, published 2024-04-16T00:15Z)
Description: value: An issue was discovered in gradio-app/gradio, where the `/component_server` endpoint improperly allows the invocation of any method on a `Component` class with attacker-controlled arguments. Specifically, by exploiting the `move_resource_to_block_cache()` method of the `Block` class, an attacker can copy any file on the filesystem to a temporary directory and subsequently retrieve it. This vulnerability enables unauthorized local file read access, posing a significant risk especially when the application is exposed to the internet via `launch(share=True)`, thereby allowing remote attackers to read files on the host machine. Furthermore, gradio apps hosted on `huggingface.co` are also affected, potentially leading to the exposure of sensitive information such as API keys and credentials stored in environment variables.
Severity: nan (CVSS v3 base score: )
Weakness: CWE-29 \u2014 Path Traversal: '\..\filename'
The product uses external input to construct a pathname that should be within a restricted directory, but it does not properly neutralize '\..\filename' (leading backslash dot dot) sequences that can resolve to a location that is outside of that directory.
Repository: https://github.com/gradio-app/gradio
Files changed:
.changeset/ten-chefs-argue.md
gradio/components/file_explorer.py
gradio/routes.py
test/test_routes.py
Commit message: Component Server fix (#6884)

* changes

* add changeset

* Update gradio/routes.py

Co-authored-by: Aarni Koskela <akx@iki.fi>

* changes

* changes

---------

Co-authored-by: Ali Abid <ubuntu@ip-172-31-25-241.us-west-2.compute.internal>
Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Co-authored-by: Aarni Koskela <akx@iki.fi>

Vulnerable code:
    def ls(self, value=None) -> list[dict[str, str]] | None:
        Parameters:
            value: file path as a list of strings for each directory level relative to the root.
            fn = getattr(block, body.fn_name)

Fixed code:
---
"gradio": minor
---

feat:Component Server fix
    def ls(self, _=None) -> list[dict[str, str]] | None:
            fn = getattr(block, body.fn_name, None)
            if fn is None or not getattr(fn, "_is_server_fn", False):
                raise HTTPException(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail="Function not found.",
                )


def test_component_server_endpoints(connect):
    here = os.path.dirname(os.path.abspath(__file__))
    with gr.Blocks() as demo:
        file_explorer = gr.FileExplorer(root=here)

    with closing(demo) as io:
        app, _, _ = io.launch(prevent_thread_lock=True)
        client = TestClient(app)
        success_req = client.post(
            "/component_server/",
            json={
                "session_hash": "123",
                "component_id": file_explorer._id,
                "fn_name": "ls",
                "data": None,
            },
        )
        assert success_req.status_code == 200
        assert len(success_req.json()) > 0
        fail_req = client.post(
            "/component_server/",
            json={
                "session_hash": "123",
                "component_id": file_explorer._id,
                "fn_name": "preprocess",
                "data": None,
            },
        )
        assert fail_req.status_code == 404