fix: security file upload (#3923)

* fix: add check user authentication
* fix: add request body multipart boundary validation
This commit is contained in:
Ítalo Johnny 2024-09-30 16:14:03 -03:00 • committed by GitHub
commit f873309004
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 44 additions and 2 deletions

2
.gitignore vendored
View file

@ -273,3 +273,5 @@ src/frontend/temp
*-shm *-shm
*-wal *-wal
.history .history
.dspy_cache/

View file

@ -40,6 +40,8 @@ def get_flow_id(
async def upload_file( async def upload_file(
file: UploadFile, file: UploadFile,
flow_id: UUID = Depends(get_flow_id), 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), storage_service: StorageService = Depends(get_storage_service),
): ):
try: try:
@ -50,6 +52,10 @@ async def upload_file(
) )
flow_id_str = str(flow_id) 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() file_content = await file.read()
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
file_name = file.filename or hashlib.sha256(file_content).hexdigest() file_name = file.filename or hashlib.sha256(file_content).hexdigest()

View file

@ -1,6 +1,7 @@
import asyncio import asyncio
import json import json
import os import os
import re
import warnings import warnings
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from http import HTTPStatus from http import HTTPStatus
@ -8,7 +9,7 @@ from pathlib import Path
from urllib.parse import urlencode from urllib.parse import urlencode
import nest_asyncio # type: ignore 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.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse, JSONResponse from fastapi.responses import FileResponse, JSONResponse
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
@ -127,6 +128,39 @@ def create_app():
) )
app.add_middleware(JavaScriptMIMETypeMiddleware) 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") @app.middleware("http")
async def flatten_query_string_lists(request: Request, call_next): async def flatten_query_string_lists(request: Request, call_next):
flattened: list[tuple[str, str]] = [] flattened: list[tuple[str, str]] = []

View file

@ -123,7 +123,7 @@ class CustomComponent(Component):
]; ];
for (const text of allVisibleTexts) { 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(); await page.locator(".ag-cell-value").first().click();