Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@
**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.

## 2026-03-04 - Information Leakage in Application Error Fields
**Vulnerability:** The application was catching exceptions in logic callbacks and Kafka consumers, then assigning the raw exception string to a JSON `error` field in the successful response object. This leaked internal details even when the HTTP status code was 200 OK or when processing asynchronously via Kafka.
**Learning:** Checking for HTTP 500 handlers is not enough. Review application-level error handling where business logic manually constructs error objects.
**Prevention:** Ensure that any `result["error"]` or similar fields populated in catch blocks use generic messages, while the real exception is logged server-side.
11 changes: 6 additions & 5 deletions src/regression_model_template/controller/kafka_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,9 @@ def _process_message(self, msg: Message) -> None:
predictionresponse.result["error"] = error
logger.error(error)
prediction_result = predictionresponse.result
except Exception as e:
error = f"Error during prediction processing: {e}"
logger.exception(error)
except Exception:
error = "An error occurred during prediction processing."
logger.exception("Error during prediction processing")
predictionresponse.result["error"] = error
prediction_result = predictionresponse.result

Expand Down Expand Up @@ -278,10 +278,11 @@ def my_prediction_function(input_data: PredictionRequest) -> PredictionResponse:
predictionresponse.result["inference"] = outputs.to_numpy().tolist()
predictionresponse.result["quality"] = 1
predictionresponse.result["error"] = None
except Exception as e:
except Exception:
logger.exception("Prediction callback failed")
predictionresponse.result["inference"] = 0
predictionresponse.result["quality"] = 0
predictionresponse.result["error"] = str(e)
predictionresponse.result["error"] = "Prediction failed."
return predictionresponse

# Kafka Configuration
Expand Down
40 changes: 40 additions & 0 deletions tests/controller/test_kafka_app_leakage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from unittest.mock import MagicMock
import json
import pytest
from regression_model_template.controller.kafka_app import FastAPIKafkaService


def test_process_message_exception_leakage():
# Mock dependencies
mock_producer = MagicMock()
mock_consumer = MagicMock()

# Mock callback that raises an exception with sensitive info
def sensitive_callback(request):
raise ValueError("Sensitive Database Error: Connection failed with user 'admin'")

service = FastAPIKafkaService(
prediction_callback=sensitive_callback, kafka_config={}, input_topic="in", output_topic="out"
)
service.producer = mock_producer
service.consumer = mock_consumer

# Mock a Kafka message
mock_msg = MagicMock()
mock_msg.error.return_value = None
mock_msg.value.return_value = json.dumps({"input_data": {}}).encode("utf-8")

# Call _process_message
service._process_message(mock_msg)

# Verify what was produced
assert mock_producer.produce.called
args, kwargs = mock_producer.produce.call_args
value = json.loads(kwargs["value"])

# Check if error is present
assert "error" in value

# Check that sensitive info is NOT leaked and generic message is used
assert "Sensitive Database Error" not in value["error"]
assert value["error"] == "An error occurred during prediction processing."