From f1ef94a435439934610497eab34201a586e418e1 Mon Sep 17 00:00:00 2001 From: aliabid94 Date: Tue, 20 Aug 2024 11:31:13 -0700 Subject: [PATCH] Open media type files in browser (#9151) * changes * add changeset * changes * add changeset * changes * changes * changes * changes * changes * changes --------- Co-authored-by: Ali Abid Co-authored-by: gradio-pr-bot --- .changeset/legal-donkeys-accept.md | 5 +++++ gradio/routes.py | 34 ++++++++++++++++++++++++++---- test/test_routes.py | 27 ++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 .changeset/legal-donkeys-accept.md diff --git a/.changeset/legal-donkeys-accept.md b/.changeset/legal-donkeys-accept.md new file mode 100644 index 0000000000..b39da03664 --- /dev/null +++ b/.changeset/legal-donkeys-accept.md @@ -0,0 +1,5 @@ +--- +"gradio": patch +--- + +fix:Open media type files in browser diff --git a/gradio/routes.py b/gradio/routes.py index ab091341b0..04afb5438b 100644 --- a/gradio/routes.py +++ b/gradio/routes.py @@ -125,6 +125,17 @@ BUILD_PATH_LIB = cast( files("gradio").joinpath("templates", "frontend", "assets").as_posix(), # type: ignore ) VERSION = get_package_version() +XSS_VULNERABLE_EXTENSIONS = [ + ".html", + ".htm", + ".js", + ".php", + ".asp", + ".aspx", + ".jsp", + ".xml", + ".svg", +] class ORJSONResponse(JSONResponse): @@ -530,7 +541,10 @@ class App(FastAPI): except PermissionError as err: raise HTTPException(status_code=400, detail=str(err)) from err rp_resp = await client.send(rp_req, stream=True) - rp_resp.headers.update({"Content-Disposition": "attachment"}) + file_extension = os.path.splitext(url_path)[1].lower() + if file_extension in XSS_VULNERABLE_EXTENSIONS: + rp_resp.headers.update({"Content-Disposition": "attachment"}) + rp_resp.headers.update({"Content-Type": "application/octet-stream"}) return StreamingResponse( rp_resp.aiter_raw(), status_code=rp_resp.status_code, @@ -589,6 +603,16 @@ class App(FastAPI): if not abs_path.exists(): raise HTTPException(404, f"File not found: {path_or_url}.") + mime_type, _ = mimetypes.guess_type(abs_path) + file_extension = os.path.splitext(abs_path)[1].lower() + + if file_extension in XSS_VULNERABLE_EXTENSIONS: + media_type = "application/octet-stream" + content_disposition_type = "attachment" + else: + media_type = mime_type or "application/octet-stream" + content_disposition_type = "inline" + range_val = request.headers.get("Range", "").strip() if range_val.startswith("bytes=") and "-" in range_val: range_val = range_val[6:] @@ -597,7 +621,8 @@ class App(FastAPI): start = int(start) end = int(end) headers = dict(request.headers) - headers["Content-Disposition"] = "attachment" + headers["Content-Disposition"] = content_disposition_type + headers["Content-Type"] = media_type response = ranged_response.RangedFileResponse( abs_path, ranged_response.OpenRange(start, end), @@ -609,8 +634,9 @@ class App(FastAPI): return FileResponse( abs_path, headers={"Accept-Ranges": "bytes"}, - content_disposition_type="attachment", - media_type="application/octet-stream", + content_disposition_type=content_disposition_type, + media_type=media_type, + filename=abs_path.name, ) @app.get( diff --git a/test/test_routes.py b/test/test_routes.py index 0a01e37d5c..7834f7a89c 100644 --- a/test/test_routes.py +++ b/test/test_routes.py @@ -254,6 +254,33 @@ class TestRoutes: assert len(file_response.text) == len(media_data.BASE64_IMAGE) io.close() + def test_response_attachment_format(self): + image_file = tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".png") + image_file.write(media_data.BASE64_IMAGE) + image_file.flush() + + html_file = tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".html") + html_file.write("Hello, world!") + html_file.flush() + + io = gr.Interface(lambda s: s.name, gr.File(), gr.File()) + app, _, _ = io.launch( + prevent_thread_lock=True, + allowed_paths=[ + os.path.dirname(image_file.name), + os.path.dirname(html_file.name), + ], + ) + client = TestClient(app) + + file_response = client.get(f"/file={image_file.name}") + assert file_response.headers["Content-Type"] == "image/png" + assert "inline" in file_response.headers["Content-Disposition"] + + file_response = client.get(f"/file={html_file.name}") + assert file_response.headers["Content-Type"] == "application/octet-stream" + assert "attachment" in file_response.headers["Content-Disposition"] + def test_allowed_and_blocked_paths(self): with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tmp_file: io = gr.Interface(lambda s: s.name, gr.File(), gr.File())