fix: security file upload (#3923)
* fix: add check user authentication * fix: add request body multipart boundary validation
This commit is contained in:
parent
059fd9403a
commit
f873309004
4 changed files with 44 additions and 2 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -273,3 +273,5 @@ src/frontend/temp
|
|||
*-shm
|
||||
*-wal
|
||||
.history
|
||||
|
||||
.dspy_cache/
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ def get_flow_id(
|
|||
async def upload_file(
|
||||
file: UploadFile,
|
||||
flow_id: UUID = Depends(get_flow_id),
|
||||
current_user=Depends(get_current_active_user),
|
||||
session=Depends(get_session),
|
||||
storage_service: StorageService = Depends(get_storage_service),
|
||||
):
|
||||
try:
|
||||
|
|
@ -50,6 +52,10 @@ async def upload_file(
|
|||
)
|
||||
|
||||
flow_id_str = str(flow_id)
|
||||
flow = session.get(Flow, flow_id_str)
|
||||
if flow.user_id != current_user.id:
|
||||
raise HTTPException(status_code=403, detail="You don't have access to this flow")
|
||||
|
||||
file_content = await file.read()
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
||||
file_name = file.filename or hashlib.sha256(file_content).hexdigest()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import warnings
|
||||
from contextlib import asynccontextmanager
|
||||
from http import HTTPStatus
|
||||
|
|
@ -8,7 +9,7 @@ from pathlib import Path
|
|||
from urllib.parse import urlencode
|
||||
|
||||
import nest_asyncio # type: ignore
|
||||
from fastapi import FastAPI, HTTPException, Request, Response
|
||||
from fastapi import FastAPI, HTTPException, Request, Response, status
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import FileResponse, JSONResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
|
@ -127,6 +128,39 @@ def create_app():
|
|||
)
|
||||
app.add_middleware(JavaScriptMIMETypeMiddleware)
|
||||
|
||||
@app.middleware("http")
|
||||
async def check_boundary(request: Request, call_next):
|
||||
if "/api/v1/files/upload" in request.url.path:
|
||||
content_type = request.headers.get("Content-Type")
|
||||
|
||||
if not content_type or "multipart/form-data" not in content_type or "boundary=" not in content_type:
|
||||
return JSONResponse(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
content={"detail": "Content-Type header must be 'multipart/form-data' with a boundary parameter."},
|
||||
)
|
||||
|
||||
boundary = content_type.split("boundary=")[-1].strip()
|
||||
|
||||
if not re.match(r"^[\w\-]{1,70}$", boundary):
|
||||
return JSONResponse(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
content={"detail": "Invalid boundary format"},
|
||||
)
|
||||
|
||||
body = await request.body()
|
||||
|
||||
boundary_start = f"--{boundary}".encode()
|
||||
boundary_end = f"--{boundary}--\r\n".encode()
|
||||
|
||||
if not body.startswith(boundary_start) or not body.endswith(boundary_end):
|
||||
return JSONResponse(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
content={"detail": "Invalid multipart formatting"},
|
||||
)
|
||||
|
||||
response = await call_next(request)
|
||||
return response
|
||||
|
||||
@app.middleware("http")
|
||||
async def flatten_query_string_lists(request: Request, call_next):
|
||||
flattened: list[tuple[str, str]] = []
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ class CustomComponent(Component):
|
|||
];
|
||||
|
||||
for (const text of allVisibleTexts) {
|
||||
await expect(page.getByText(text)).toBeVisible();
|
||||
await expect(page.getByText(text).last()).toBeVisible();
|
||||
}
|
||||
|
||||
await page.locator(".ag-cell-value").first().click();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue