From d43e0d18e02e87ec9a66e2d302a18f0944e65866 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 25 May 2026 21:30:44 +0000 Subject: [PATCH] Fix internal exception string leakage in HTTP 500 responses Modified the /projects/{project_id}/ingest route to catch exceptions, log the raw exception internally using logger.error, and return a generic 'Upload failed' message in the HTTPException instead of exposing the raw exception string. This prevents the leakage of sensitive internal context (such as stack details, paths, or database errors) to the client. Co-authored-by: socialawy <24765060+socialawy@users.noreply.github.com> --- .jules/sentinel.md | 5 +++++ src/audioformation/server/routes.py | 3 ++- uv.lock | 4 ++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.jules/sentinel.md b/.jules/sentinel.md index c1caaeb..31ec4e6 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -6,3 +6,8 @@ **Vulnerability:** The custom `SafeStaticFiles` middleware in `src/audioformation/server/app.py` intended to block access to sensitive directories (like `00_CONFIG` and `.git`). However, it used `p = Path(path).lower()`, which raises an `AttributeError` because `pathlib.Path` objects lack a `.lower()` method. This effectively broke static file serving entirely (causing 500 errors) and represented a malformed security check. If such errors were ever 'swallowed' without raising an HTTP exception, it could result in 'failing open' and allowing access to sensitive files. **Learning:** Security checks that rely on path manipulation or normalization must be carefully tested for runtime exceptions. An unhandled exception in a security gate can either block legitimate traffic (Denial of Service) or, if caught improperly elsewhere, fail open. Always normalize the string representation of paths before converting them to `Path` objects. **Prevention:** Thoroughly test security middleware endpoints for both valid and invalid access attempts. Ensure that path string normalizations like `.lower()` are applied directly to the string before instantiating `Path(str(path).lower())`. + +## 2025-05-25 - Prevent Exception String Leakage in API Routes +**Vulnerability:** The raw string representation of caught internal exceptions (`Exception as e`) was being exposed directly to clients inside HTTP 500 responses (e.g., `raise HTTPException(status_code=500, detail=f"Upload failed: {e}")`). +**Learning:** This exposes sensitive internal application state, stack details, or potentially infrastructure specifics (like file paths or database queries) to external users, enabling reconnaissance for further attacks. +**Prevention:** Always log the exception details internally using `logger.error` or `logger.exception` and return a generic, static message to the client (e.g., `"Upload failed"` or `"Internal server error"`). diff --git a/src/audioformation/server/routes.py b/src/audioformation/server/routes.py index 34c9bec..d216eb6 100644 --- a/src/audioformation/server/routes.py +++ b/src/audioformation/server/routes.py @@ -185,7 +185,8 @@ async def ingest_files( shutil.copyfileobj(file.file, buffer) except Exception as e: shutil.rmtree(tmp_dir, ignore_errors=True) - raise HTTPException(status_code=500, detail=f"Upload failed: {e}") + logger.error(f"Upload failed for project {project_id}: {e}") + raise HTTPException(status_code=500, detail="Upload failed") background_tasks.add_task( _run_with_status, diff --git a/uv.lock b/uv.lock index e34eac4..7970295 100644 --- a/uv.lock +++ b/uv.lock @@ -270,7 +270,7 @@ requires-dist = [ { name = "jsonschema", specifier = ">=4.21,<5" }, { name = "midiutil", marker = "extra == 'midi'", specifier = ">=1.2,<2" }, { name = "mutagen", marker = "extra == 'm4b'", specifier = ">=1.47,<2" }, - { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.0,<2" }, + { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.0,<3" }, { name = "numpy", specifier = ">=1.26,<3" }, { name = "pre-commit", marker = "extra == 'dev'", specifier = ">=3.6,<5" }, { name = "pydub", specifier = ">=0.25,<1" }, @@ -280,7 +280,7 @@ requires-dist = [ { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=4.0,<8" }, { name = "python-dotenv", marker = "extra == 'cloud'", specifier = ">=1.0,<2" }, { name = "python-multipart", marker = "extra == 'server'", specifier = ">=0.0.27,<1" }, - { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.1.9" }, + { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.15.1" }, { name = "silero-vad", marker = "extra == 'vad'", specifier = ">=6.0,<7" }, { name = "soundfile", specifier = ">=0.12,<1" }, { name = "transformers", specifier = ">=4.44,<6" },