diff --git a/.jules/sentinel.md b/.jules/sentinel.md new file mode 100644 index 0000000..f7fa645 --- /dev/null +++ b/.jules/sentinel.md @@ -0,0 +1,4 @@ +## 2026-02-03 - Information Leakage in Exception Handling +**Vulnerability:** The application was catching all exceptions and returning their string representation (`str(e)`) directly to the client in the HTTP 500 response. This could expose sensitive internal details (stack traces, database info, file paths). +**Learning:** Developers often pass `str(e)` to `HTTPException` for convenience during debugging, but this practice frequently makes it into production code, leading to information leakage. +**Prevention:** In production, catch `Exception` and raise `HTTPException` with a generic message (e.g., "Internal Server Error"). Ensure full exception details are logged using `logger.exception()` for server-side debugging. diff --git a/src/regression_model_template/controller/kafka_app.py b/src/regression_model_template/controller/kafka_app.py index 0f17f58..0bcd83a 100644 --- a/src/regression_model_template/controller/kafka_app.py +++ b/src/regression_model_template/controller/kafka_app.py @@ -247,7 +247,7 @@ async def predict(request: PredictionRequest) -> PredictionResponse: # Use glob return prediction_result # Use the global class except Exception as e: logger.exception("Error processing HTTP prediction request:") - raise HTTPException(status_code=500, detail=str(e)) + raise HTTPException(status_code=500, detail="Internal Server Error") @app.get("/health", summary="Health Check", tags=["System"]) diff --git a/tests/controller/test_kafka_app_security.py b/tests/controller/test_kafka_app_security.py new file mode 100644 index 0000000..93857a7 --- /dev/null +++ b/tests/controller/test_kafka_app_security.py @@ -0,0 +1,36 @@ +import pytest +from unittest.mock import patch, MagicMock +from fastapi import HTTPException +from regression_model_template.controller.kafka_app import ( + PredictionRequest, + predict, +) +import asyncio +import regression_model_template.controller.kafka_app as kafka_app + + +def test_predict_endpoint_exception_leak(): + """Test that the predict endpoint does NOT leak exception details.""" + + async def run_async_test(): + # Initialize the global variable if it's missing, or patch it. + # Since it might not be initialized, patch with create=True might work, + # or we can manually set it. + + with patch( + "regression_model_template.controller.kafka_app.fastapi_kafka_service", create=True + ) as mock_fastapi_kafka_service: + # Simulate a sensitive internal error + sensitive_error_message = "Database connection failed at 192.168.1.100:5432" + mock_fastapi_kafka_service.prediction_callback.side_effect = Exception(sensitive_error_message) + + # Expect an HTTPException + with pytest.raises(HTTPException) as excinfo: + await predict(PredictionRequest()) + + # Verify that the sensitive message is leaked in the detail + assert excinfo.value.status_code == 500 + assert excinfo.value.detail == "Internal Server Error" + assert excinfo.value.detail != sensitive_error_message + + asyncio.run(run_async_test())