diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 2ceab100..0e992fa8 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -2,3 +2,8 @@ **Vulnerability:** Raw error objects from FFmpeg and browser APIs (`error.message` and `console.error`) were being rendered directly in the user interface and browser console. **Learning:** Returning unhandled exception messages to the client risks exposing internal stack traces, system paths, and unexpected framework vulnerabilities to potential attackers. **Prevention:** Catch error blocks should log a sanitized version of the error or omit sensitive details, while the state presented to the user should be a generic, friendly, and secure fallback message (e.g. "Processing failed securely"). + +## 2024-03-25 - Prevent Path Traversal in Server File Uploads +**Vulnerability:** In `src/app/api/video/[tool]/route.ts`, the file upload endpoint used the unsanitized `file.name` to construct the `inputPath` where the server would save the user's file (`${tmpDir}/${crypto.randomUUID()}_${file.name}`). This permitted a Path Traversal vulnerability where an attacker could upload a file with a name containing `../` to write arbitrary files outside the intended `/tmp/omni/video` directory, such as `../../../../../etc/passwd`. +**Learning:** Even if randomizing the start of a file path (using `crypto.randomUUID()`), appending user-controlled input to the end of a path without sanitization still allows the directory segments to traverse up the file tree. +**Prevention:** Always sanitize filenames from user uploads using `path.basename(file.name)` to strip out any directory paths and safely construct paths using `path.join()`. diff --git a/src/app/api/video/[tool]/route.ts b/src/app/api/video/[tool]/route.ts index a7307430..9c552d93 100644 --- a/src/app/api/video/[tool]/route.ts +++ b/src/app/api/video/[tool]/route.ts @@ -126,7 +126,10 @@ export async function POST( const tmpDir = `/tmp/omni/video`; await fs.mkdir(tmpDir, { recursive: true }); - const inputPath = `${tmpDir}/${crypto.randomUUID()}_${file.name}`; + + // Prevent path traversal by extracting just the filename + const safeFileName = path.basename(file.name); + const inputPath = path.join(tmpDir, `${crypto.randomUUID()}_${safeFileName}`); // Write file to disk const arrayBuffer = await file.arrayBuffer();