Skip to content

Commit 71cf736

Browse files
authored
fix: harden media path validation against path injection (CodeQL) (#86)
Add early rejection of path traversal (`..`) and absolute paths before filesystem operations. Use `resolve(strict=True)` to prevent TOCTOU race conditions with symlinks. Resolves CodeQL alerts #12, #13, #14.
1 parent 655b3dd commit 71cf736

File tree

2 files changed

+11
-1
lines changed

2 files changed

+11
-1
lines changed

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ You assist developers working on telegram-archive.
4343
- Create a descriptive branch name (e.g., `feat/add-login`, `fix/button-styling`)
4444
- Open a PR for review before merging
4545
- Do NOT commit directly to main/master branch
46+
- **After every push/merge**, check CI status with `gh run list` or `gh pr checks` and fix any test or lint failures before moving on
4647

4748
## Boundaries
4849

src/web/main.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -666,7 +666,16 @@ async def serve_media(path: str, user: UserContext = Depends(require_auth)):
666666
if not _media_root:
667667
raise HTTPException(status_code=404, detail="Media directory not configured")
668668

669-
resolved = (_media_root / path).resolve()
669+
# Reject path traversal and absolute paths before any filesystem operations
670+
if ".." in path.split("/") or path.startswith("/"):
671+
raise HTTPException(status_code=403, detail="Access denied")
672+
673+
# Construct and resolve path, then verify it stays within media root
674+
candidate = _media_root / path
675+
try:
676+
resolved = candidate.resolve(strict=True)
677+
except (OSError, ValueError):
678+
raise HTTPException(status_code=404, detail="File not found")
670679
if not resolved.is_relative_to(_media_root):
671680
raise HTTPException(status_code=403, detail="Access denied")
672681

0 commit comments

Comments
 (0)