fix: replace FileResponse with Response for index.html to prevent Content-Length mismatch and add path traversal guard
This commit is contained in:
parent
0ccf07a03b
commit
2e06a00934
1 changed files with 41 additions and 23 deletions
|
|
@ -8,7 +8,7 @@ from contextlib import asynccontextmanager
|
|||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.responses import FileResponse
|
||||
from fastapi.responses import FileResponse, HTMLResponse, Response
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
|
|
@ -156,26 +156,44 @@ async def get_hardware_info():
|
|||
|
||||
def setup_frontend(app: FastAPI, build_path: Path):
|
||||
"""Mount frontend static files (optional)"""
|
||||
if build_path.exists():
|
||||
# Mount assets
|
||||
assets_dir = build_path / "assets"
|
||||
if assets_dir.exists():
|
||||
app.mount("/assets", StaticFiles(directory=assets_dir), name="assets")
|
||||
if not build_path.exists():
|
||||
return False
|
||||
|
||||
@app.get("/")
|
||||
async def serve_root():
|
||||
return FileResponse(build_path / "index.html", headers={"Cache-Control": "no-cache, no-store, must-revalidate"})
|
||||
# Mount assets
|
||||
assets_dir = build_path / "assets"
|
||||
if assets_dir.exists():
|
||||
app.mount("/assets", StaticFiles(directory=assets_dir), name="assets")
|
||||
|
||||
@app.get("/{full_path:path}")
|
||||
async def serve_frontend(full_path: str):
|
||||
if full_path.startswith("api"):
|
||||
return {"error": "API endpoint not found"}
|
||||
@app.get("/")
|
||||
async def serve_root():
|
||||
content = (build_path / "index.html").read_bytes()
|
||||
return Response(
|
||||
content=content,
|
||||
media_type="text/html",
|
||||
headers={"Cache-Control": "no-cache, no-store, must-revalidate"},
|
||||
)
|
||||
|
||||
file_path = build_path / full_path
|
||||
if file_path.is_file():
|
||||
return FileResponse(file_path)
|
||||
@app.get("/{full_path:path}")
|
||||
async def serve_frontend(full_path: str):
|
||||
if full_path.startswith("api"):
|
||||
return {"error": "API endpoint not found"}
|
||||
|
||||
return FileResponse(build_path / "index.html", headers={"Cache-Control": "no-cache, no-store, must-revalidate"})
|
||||
file_path = (build_path / full_path).resolve()
|
||||
|
||||
# Block path traversal — ensure resolved path stays inside build_path
|
||||
if not str(file_path).startswith(str(build_path.resolve())):
|
||||
return Response(status_code=403)
|
||||
|
||||
if file_path.is_file():
|
||||
return FileResponse(file_path)
|
||||
|
||||
# Serve index.html as bytes — avoids Content-Length mismatch
|
||||
content = (build_path / "index.html").read_bytes()
|
||||
return Response(
|
||||
content=content,
|
||||
media_type="text/html",
|
||||
headers={"Cache-Control": "no-cache, no-store, must-revalidate"},
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
return True
|
||||
return False
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue