Skip to content

Commit 371a94c

Browse files
committed
fix: harden /files/upload path handling for multi-user isolation
- Resolve directory parameter through fs.resolve_path() - Sanitize uploaded filename with os.path.basename() - Normalize composed path with os.path.normpath() - Catch PermissionError and return 403 for cross-user access attempts
1 parent f4277c3 commit 371a94c

3 files changed

Lines changed: 13 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
66

7+
## [0.11.11] - 2026-03-11
8+
9+
### Fixed
10+
11+
- 🔒 **Upload path traversal**`/files/upload` now resolves the `directory` parameter through `fs.resolve_path()` and sanitizes the uploaded filename with `os.path.basename()`, preventing path traversal attacks (e.g. `../../etc/passwd`) that could escape the user's home directory in multi-user mode. The composed path is normalized with `os.path.normpath()` and validated by `_check_path` before writing. All other file endpoints already had these protections.
12+
713
## [0.11.10] - 2026-03-11
814

915
### Fixed

open_terminal/main.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -878,16 +878,20 @@ async def upload_file(
878878
filename = os.path.basename(urlparse(url).path) or "download"
879879
elif file:
880880
content = await file.read()
881-
filename = file.filename or "upload"
881+
filename = os.path.basename(file.filename or "upload")
882882
else:
883883
raise HTTPException(
884884
status_code=400, detail="Provide either 'url' or a file upload."
885885
)
886886

887+
directory = fs.resolve_path(directory)
888+
path = os.path.normpath(os.path.join(directory, filename))
889+
887890
try:
888891
await fs.mkdir(directory)
889-
path = os.path.join(directory, filename)
890892
await fs.write_bytes(path, content)
893+
except PermissionError as e:
894+
raise HTTPException(status_code=403, detail=str(e))
891895
except OSError as e:
892896
raise HTTPException(status_code=400, detail=str(e))
893897
return {"path": path, "size": len(content)}

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "open-terminal"
3-
version = "0.11.10"
3+
version = "0.11.11"
44
description = "A remote terminal API."
55
readme = "README.md"
66
authors = [

0 commit comments

Comments
 (0)