From 45b5f1162572fe974824e82d917df378f5d3bcf7 Mon Sep 17 00:00:00 2001 From: Abubakar Abid Date: Tue, 30 May 2023 12:33:24 -0700 Subject: [PATCH 1/4] test --- gradio/routes.py | 20 +++++++++++++++----- test/test_external.py | 2 +- test/test_routes.py | 9 +++++++++ 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/gradio/routes.py b/gradio/routes.py index 753c55c7c634b..63573c95c73a8 100644 --- a/gradio/routes.py +++ b/gradio/routes.py @@ -144,6 +144,20 @@ def get_blocks(self) -> gradio.Blocks: raise ValueError("No Blocks has been configured for this app.") return self.blocks + @staticmethod + def build_proxy_request(url_path): + url = httpx.URL(url_path) + is_hf_url = ( + url.host.endswith(".huggingface.co") + or url.host.endswith(".hf.space") + or url.host.endswith(".hf.co") + ) + headers = {} + if Context.hf_token is not None and is_hf_url: + headers["Authorization"] = f"Bearer {Context.hf_token}" + rp_req = client.build_request("GET", url, headers=headers) + return rp_req + @staticmethod def create_app( blocks: gradio.Blocks, app_kwargs: Dict[str, Any] | None = None @@ -300,11 +314,7 @@ async def favicon(): @app.get("/proxy={url_path:path}", dependencies=[Depends(login_check)]) async def reverse_proxy(url_path: str): # Adapted from: https://github.com/tiangolo/fastapi/issues/1788 - url = httpx.URL(url_path) - headers = {} - if Context.hf_token is not None: - headers["Authorization"] = f"Bearer {Context.hf_token}" - rp_req = client.build_request("GET", url, headers=headers) + rp_req = build_proxy_request(url_path) rp_resp = await client.send(rp_req, stream=True) return StreamingResponse( rp_resp.aiter_raw(), diff --git a/test/test_external.py b/test/test_external.py index 9c20ee27baaf1..9095c665f3c13 100644 --- a/test/test_external.py +++ b/test/test_external.py @@ -320,7 +320,7 @@ def test_multiple_spaces_one_private(self): def test_loading_files_via_proxy_works(self): api_key = "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" # Intentionally revealing this key for testing purposes io = gr.load( - "spaces/gradio-tests/test-loading-examples-private", api_key=api_key + "spaces/gradio-tests/test-loading-examples-private", hf_token=api_key ) assert io.theme.name == "default" app, _, _ = io.launch(prevent_thread_lock=True) diff --git a/test/test_routes.py b/test/test_routes.py index 918b459d7d97a..0cb65e924e979 100644 --- a/test/test_routes.py +++ b/test/test_routes.py @@ -393,6 +393,15 @@ def test_mount_gradio_app_raises_error_if_event_queued_but_queue_disabled(self): demo.close() + def test_proxy_does_not_leak_hf_token_externally(self): + gr.context.Context.hf_token = "abcdef" + r = routes.App.build_proxy_request( + "https://gradio-tests-test-loading-examples-private.hf.space/file=Bunny.obj" + ) + assert "authorization" in dict(r.headers) + r = routes.App.build_proxy_request("https://google.com") + assert "authorization" not in dict(r.headers) + class TestApp: def test_create_app(self): From a5c5def28f121f50e93fc318e642e3b35e8ea359 Mon Sep 17 00:00:00 2001 From: Abubakar Abid Date: Tue, 30 May 2023 12:34:46 -0700 Subject: [PATCH 2/4] fix --- gradio/routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradio/routes.py b/gradio/routes.py index 63573c95c73a8..f104a485e3dde 100644 --- a/gradio/routes.py +++ b/gradio/routes.py @@ -314,7 +314,7 @@ async def favicon(): @app.get("/proxy={url_path:path}", dependencies=[Depends(login_check)]) async def reverse_proxy(url_path: str): # Adapted from: https://github.com/tiangolo/fastapi/issues/1788 - rp_req = build_proxy_request(url_path) + rp_req = app.build_proxy_request(url_path) rp_resp = await client.send(rp_req, stream=True) return StreamingResponse( rp_resp.aiter_raw(), From 4ce30693a76661f2ee2c0c71676a4f381c7f63ec Mon Sep 17 00:00:00 2001 From: Abubakar Abid Date: Tue, 30 May 2023 12:36:28 -0700 Subject: [PATCH 3/4] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff71c8e3015d0..4240becf26b37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Make `Blocks.load` behave like other event listeners (allows chaining `then` off of it) [@anentropic](https://github.com/anentropic/) in [PR 4304](https://github.com/gradio-app/gradio/pull/4304) - Respect `interactive=True` in output components of a `gr.Interface` by [@abidlabs](https://github.com/abidlabs) in [PR 4356](https://github.com/gradio-app/gradio/pull/4356). - Remove unused frontend code by [@akx](https://github.com/akx) in [PR 4275](https://github.com/gradio-app/gradio/pull/4275) +- Do not send HF token to other domains via `/proxy` route by [@abidlabs](https://github.com/abidlabs) in [PR 4368](https://github.com/gradio-app/gradio/pull/4368). ## Other Changes: From 6df296da5462f81c96b742e6bb6c27e457814699 Mon Sep 17 00:00:00 2001 From: Abubakar Abid Date: Wed, 31 May 2023 11:39:25 -0700 Subject: [PATCH 4/4] fix, add warnings --- gradio/blocks.py | 2 +- gradio/external.py | 2 +- gradio/interface.py | 2 +- gradio/routes.py | 6 +----- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/gradio/blocks.py b/gradio/blocks.py index 9731fb739ddb6..275061c2ac85a 100644 --- a/gradio/blocks.py +++ b/gradio/blocks.py @@ -1448,7 +1448,7 @@ def load( Parameters: name: Class Method - the name of the model (e.g. "gpt2" or "facebook/bart-base") or space (e.g. "flax-community/spanish-gpt2"), can include the `src` as prefix (e.g. "models/facebook/bart-base") src: Class Method - the source of the model: `models` or `spaces` (or leave empty if source is provided as a prefix in `name`) - api_key: Class Method - optional access token for loading private Hugging Face Hub models or spaces. Find your token here: https://huggingface.co/settings/tokens + api_key: Class Method - optional access token for loading private Hugging Face Hub models or spaces. Find your token here: https://huggingface.co/settings/tokens. Warning: only provide this if you are loading a trusted private Space as it can be read by the Space you are loading. alias: Class Method - optional string used as the name of the loaded model instead of the default name (only applies if loading a Space running Gradio 2.x) fn: Instance Method - the function to wrap an interface around. Often a machine learning model's prediction function. Each parameter of the function corresponds to one input component, and the function should return a single value or a tuple of values, with each element in the tuple corresponding to one output component. inputs: Instance Method - List of gradio.components to use as inputs. If the function takes no inputs, this should be an empty list. diff --git a/gradio/external.py b/gradio/external.py index 673963f451afa..e3de14d353670 100644 --- a/gradio/external.py +++ b/gradio/external.py @@ -51,7 +51,7 @@ def load( name: the name of the model (e.g. "gpt2" or "facebook/bart-base") or space (e.g. "flax-community/spanish-gpt2"), can include the `src` as prefix (e.g. "models/facebook/bart-base") src: the source of the model: `models` or `spaces` (or leave empty if source is provided as a prefix in `name`) api_key: Deprecated. Please use the `hf_token` parameter instead. - hf_token: optional access token for loading private Hugging Face Hub models or spaces. Find your token here: https://huggingface.co/settings/tokens + hf_token: optional access token for loading private Hugging Face Hub models or spaces. Find your token here: https://huggingface.co/settings/tokens. Warning: only provide this if you are loading a trusted private Space as it can be read by the Space you are loading. alias: optional string used as the name of the loaded model instead of the default name (only applies if loading a Space running Gradio 2.x) Returns: a Gradio Blocks object for the given model diff --git a/gradio/interface.py b/gradio/interface.py index fa47addc1eced..8848d47ceb714 100644 --- a/gradio/interface.py +++ b/gradio/interface.py @@ -85,7 +85,7 @@ def load( Parameters: name: the name of the model (e.g. "gpt2" or "facebook/bart-base") or space (e.g. "flax-community/spanish-gpt2"), can include the `src` as prefix (e.g. "models/facebook/bart-base") src: the source of the model: `models` or `spaces` (or leave empty if source is provided as a prefix in `name`) - api_key: optional access token for loading private Hugging Face Hub models or spaces. Find your token here: https://huggingface.co/settings/tokens + api_key: optional access token for loading private Hugging Face Hub models or spaces. Find your token here: https://huggingface.co/settings/tokens. Warning: only provide this if you are loading a trusted private Space as it can be read by the Space you are loading. alias: optional string used as the name of the loaded model instead of the default name (only applies if loading a Space running Gradio 2.x) Returns: a Gradio Interface object for the given model diff --git a/gradio/routes.py b/gradio/routes.py index f104a485e3dde..5603694809284 100644 --- a/gradio/routes.py +++ b/gradio/routes.py @@ -147,11 +147,7 @@ def get_blocks(self) -> gradio.Blocks: @staticmethod def build_proxy_request(url_path): url = httpx.URL(url_path) - is_hf_url = ( - url.host.endswith(".huggingface.co") - or url.host.endswith(".hf.space") - or url.host.endswith(".hf.co") - ) + is_hf_url = url.host.endswith(".hf.space") headers = {} if Context.hf_token is not None and is_hf_url: headers["Authorization"] = f"Bearer {Context.hf_token}"