Skip to content

Commit 821e942

Browse files
committed
File API: sanitize paths
1 parent 9fe1dfe commit 821e942

File tree

1 file changed

+12
-6
lines changed

1 file changed

+12
-6
lines changed

covalent_dispatcher/_service/files.py

+12-6
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import aiofiles
2222
import aiofiles.os
2323
from fastapi import APIRouter, Request
24+
from fastapi.exceptions import RequestValidationError
2425
from fastapi.responses import FileResponse
2526

2627
from covalent._shared_files import logger
@@ -46,18 +47,23 @@ async def _transfer_data(req: Request, dest_path: str):
4647
await aiofiles.os.replace(tmp_path, dest_path)
4748

4849

50+
# Resolve path to an absolute path and check that it
51+
# doesn't escape the data directory root
52+
def _sanitize_path(path: str) -> str:
53+
abs_path = os.path.realpath(path)
54+
if not abs_path.startswith(BASE_PATH) or len(abs_path) <= len(BASE_PATH):
55+
raise RequestValidationError(f"Invalid object key {path}")
56+
return abs_path
57+
58+
4959
@router.get("/files/{object_key:path}")
5060
async def download_file(object_key: str):
51-
# TODO: reject relative path components
52-
53-
path = os.path.join(BASE_PATH, object_key)
61+
path = _sanitize_path(os.path.join(BASE_PATH, object_key))
5462
return FileResponse(path)
5563

5664

5765
@router.put("/files/{object_key:path}")
5866
async def upload_file(req: Request, object_key: str):
59-
# TODO: reject relative path components
60-
61-
path = os.path.join(BASE_PATH, object_key)
67+
path = _sanitize_path(os.path.join(BASE_PATH, object_key))
6268
os.makedirs(os.path.dirname(path), exist_ok=True)
6369
await _transfer_data(req, path)

0 commit comments

Comments
 (0)