Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions python/composio/server/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from functools import update_wrapper
from hashlib import md5
from pathlib import Path
import unicodedata

import typing_extensions as te
from fastapi import FastAPI, HTTPException, Request, Response
Expand All @@ -30,7 +31,6 @@
from composio.tools.local import load_local_tools
from composio.utils.logging import get as get_logger


ResponseType = t.TypeVar("ResponseType")

R = t.TypeVar("R")
Expand Down Expand Up @@ -281,7 +281,11 @@ def _download_file_or_dir(file: t.Optional[str] = None):
raise HTTPException(
status_code=400, detail="File path is required as query parameter"
)
path = Path(file)
# Apply filename sanitization to prevent path injection
safe_file = secure_filename(file)
if not safe_file:
raise HTTPException(status_code=400, detail="Invalid file path after sanitization")
path = Path(safe_file)
if not path.exists():
return Response(
content=APIResponse[None](
Expand Down Expand Up @@ -313,3 +317,14 @@ def _archive(directory: Path, output: Path) -> Path:
),
)
return output


def secure_filename(filename: str) -> str:
filename = unicodedata.normalize("NFKD", filename)
filename = filename.encode("ascii", "ignore").decode("ascii")
for sep in (os.sep, os.path.altsep):
if sep:
Comment on lines +325 to +326

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The os module is used in secure_filename() but not imported at the top of the file, which will cause a NameError when the function is called.

filename = filename.replace(sep, " ")
filename = "_".join(filename.split())
filename = filename.strip("._")
return filename
Loading