Skip to content
Closed
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 @@ -12,3 +12,8 @@
**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.

## 2026-02-10 - Missing Security Middleware
**Vulnerability:** The FastAPI application lacked `CORSMiddleware` and `TrustedHostMiddleware`, leaving it vulnerable to Cross-Origin attacks and Host Header attacks.
**Learning:** Default FastAPI applications do not include security headers or CORS protection by default.
**Prevention:** Always configure `CORSMiddleware` and `TrustedHostMiddleware` with strict, environment-configurable allowlists (e.g., `ALLOWED_ORIGINS`, `ALLOWED_HOSTS`).
19 changes: 19 additions & 0 deletions src/regression_model_template/controller/kafka_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import uvicorn
import pandas as pd
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.trustedhost import TrustedHostMiddleware
from pydantic import BaseModel

from confluent_kafka import Producer, Consumer, KafkaError, Message
Expand All @@ -29,6 +31,9 @@
DEFAULT_OUTPUT_TOPIC = os.getenv("DEFAULT_OUTPUT_TOPIC", "output_topic")
DEFAULT_FASTAPI_HOST = os.getenv("DEFAULT_FASTAPI_HOST", "127.0.0.1")
DEFAULT_FASTAPI_PORT = int(os.getenv("DEFAULT_FASTAPI_PORT", 8100))
# Security: Configure allowed origins and hosts via environment variables
ALLOWED_ORIGINS = os.getenv("ALLOWED_ORIGINS", "*").split(",")
ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS", "*").split(",")
LOGGING_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"


Expand All @@ -43,6 +48,20 @@
version="1.0.0",
)

# Security Middleware
app.add_middleware(
TrustedHostMiddleware,
allowed_hosts=ALLOWED_HOSTS,
)

app.add_middleware(
CORSMiddleware,
allow_origins=ALLOWED_ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)


# Data Models
class PredictionRequest(BaseModel):
Expand Down
26 changes: 26 additions & 0 deletions tests/controller/test_kafka_app_middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.trustedhost import TrustedHostMiddleware
from regression_model_template.controller.kafka_app import app, ALLOWED_ORIGINS, ALLOWED_HOSTS


def test_security_middleware_present():
"""Test that security middleware is added to the FastAPI app."""
middleware_cls = [m.cls for m in app.user_middleware]

assert CORSMiddleware in middleware_cls, "CORSMiddleware should be present"
assert TrustedHostMiddleware in middleware_cls, "TrustedHostMiddleware should be present"


def test_security_middleware_configuration():
"""Test that security middleware is configured correctly."""

# Check CORSMiddleware config
cors_middleware = next(m for m in app.user_middleware if m.cls == CORSMiddleware)
# Starlette Middleware stores args in kwargs
assert cors_middleware.kwargs["allow_origins"] == ALLOWED_ORIGINS
assert cors_middleware.kwargs["allow_origins"] == ["*"] # Default

# Check TrustedHostMiddleware config
trusted_host_middleware = next(m for m in app.user_middleware if m.cls == TrustedHostMiddleware)
assert trusted_host_middleware.kwargs["allowed_hosts"] == ALLOWED_HOSTS
assert trusted_host_middleware.kwargs["allowed_hosts"] == ["*"] # Default