Open media type files in browser (#9151)

* changes

* add changeset

* changes

* add changeset

* changes

* changes

* changes

* changes

* changes

* changes

---------

Co-authored-by: Ali Abid <aliabid94@gmail.com>
Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
This commit is contained in:
aliabid94 2024-08-20 11:31:13 -07:00 committed by GitHub
parent 675b50b2e5
commit f1ef94a435
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 62 additions and 4 deletions

View File

@ -0,0 +1,5 @@
---
"gradio": patch
---
fix:Open media type files in browser

View File

@ -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(

View File

@ -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("<html>Hello, world!</html>")
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())