From 4eb2d2b3f7421aed3dcdeff71b66473c4e189ef8 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 7 May 2026 21:32:00 +0000 Subject: [PATCH] Fix Path attribute error and fail securely in SafeStaticFiles. Co-authored-by: socialawy <24765060+socialawy@users.noreply.github.com> --- .jules/sentinel.md | 6 +++++- src/audioformation/server/app.py | 15 ++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 9bd8528..ae1cadd 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -1,4 +1,8 @@ ## 2025-02-21 - Path Traversal in Mix Endpoint API Parameter **Vulnerability:** The `/projects/{project_id}/mix` API endpoint in `src/audioformation/server/routes.py` accepted a `music` parameter (meant to specify a filename within the `05_MUSIC/generated` directory) but directly passed it to `mix_project` without sanitization. This allowed directory traversal payloads like `../../../etc/passwd` to be used for background music resolution. **Learning:** Even internal API inputs that map strictly to filenames inside an expected directory must be sanitized. A simple check for file existence (`if not bg_music_path.exists():`) is insufficient as it confirms existence but allows looking outside the bounded directory. -**Prevention:** Always use established sanitization helpers (like `sanitize_filename`) or bound checks (like `validate_path_within`) for any user-supplied string that forms part of a filesystem path. Ensure bypass parameters like `FORCE_NO_MUSIC` are handled before and mutually exclusively from sanitization. \ No newline at end of file +**Prevention:** Always use established sanitization helpers (like `sanitize_filename`) or bound checks (like `validate_path_within`) for any user-supplied string that forms part of a filesystem path. Ensure bypass parameters like `FORCE_NO_MUSIC` are handled before and mutually exclusively from sanitization. +## 2024-05-24 - Path AttributeError in SafeStaticFiles +**Vulnerability:** Unhandled AttributeError when calling `.lower()` on a `Path` object in `SafeStaticFiles`, causing unhandled 500 errors on paths and potentially leaking information or allowing bypasses. +**Learning:** `Path` objects in Python do not have a `.lower()` method. When dealing with path validation, case normalization must occur on the string representation before `Path` instantiation. Unhandled exceptions in security checks can cause a 'fail open' or 500 error leaking internals. +**Prevention:** Always use `Path(str(path).lower())` and wrap path-based security validations in `try/except` blocks that fail securely with a generic 400 or 403 HTTP response. diff --git a/src/audioformation/server/app.py b/src/audioformation/server/app.py index 9334beb..2b2cf51 100644 --- a/src/audioformation/server/app.py +++ b/src/audioformation/server/app.py @@ -24,11 +24,16 @@ class SafeStaticFiles(StaticFiles): async def get_response(self, path: str, scope) -> Response: # Normalize path for check - p = Path(path).lower() - if "00_config" in p.parts or p.name.startswith(".env") or ".git" in p.parts: - raise HTTPException( - status_code=403, detail="Access denied to sensitive resource" - ) + try: + p = Path(str(path).lower()) + if "00_config" in p.parts or p.name.startswith(".env") or ".git" in p.parts: + raise HTTPException( + status_code=403, detail="Access denied to sensitive resource" + ) + except HTTPException: + raise + except Exception: + raise HTTPException(status_code=400, detail="Invalid path format") return await super().get_response(path, scope)