From 2b501c691d1f79624fc01e2cfe765361c03c3785 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 30 May 2026 21:27:29 +0000 Subject: [PATCH] Fix information leakage in ingest route Modified `src/audioformation/server/routes.py` to prevent raw exception strings from leaking in `HTTPException` details. Errors are now logged internally while clients receive generic responses. 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..9f8c3fa 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-02-27 - Information Leakage via HTTP 500 Responses +**Vulnerability:** The application was exposing the raw exception string (e.g. `f"Upload failed: {e}"`) in HTTP 500 error responses returned to the user in `routes.py`, potentially leaking internal system details. +**Learning:** Returning unhandled exception strings directly to the client is a security risk. FastAPI exception handlers should "fail closed" by logging the exact error message internally (`logger.error()`) for debugging but displaying a safe, generic message (like `Upload failed`) to the user. +**Prevention:** In API endpoints, explicitly catch specific exceptions where possible. Use structured logging to capture context, and standardize `HTTPException` details to generic, unrevealing descriptions for HTTP 500 errors. diff --git a/src/audioformation/server/routes.py b/src/audioformation/server/routes.py index 34c9bec..7429d46 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: {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" },