debate/.planning/phases/01-core-infrastructure-security/01-03-PLAN.md
Mikkel Georgsen 262a32673b docs(01): create phase plan
Phase 01: Core Infrastructure & Security
- 5 plans in 3 waves
- 3 parallel (Wave 1-2), 1 sequential (Wave 3)
- Ready for execution

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 19:59:49 +00:00

6.2 KiB

phase plan type wave depends_on files_modified autonomous must_haves
01-core-infrastructure-security 03 execute 2
01-01
01-02
backend/app/main.py
backend/app/core/security.py
backend/app/api/deps.py
backend/app/api/v1/endpoints/health.py
true
truths artifacts key_links
Rate limiting blocks requests exceeding 100/minute
CSRF tokens are validated on state-changing requests
Database connectivity checked in health endpoint
Security headers present in responses
path provides contains
backend/app/core/security.py Rate limiting and CSRF configuration Limiter
path provides contains
backend/app/api/deps.py FastAPI dependency injection get_db
path provides contains
backend/app/main.py Security middleware stack TrustedHostMiddleware
from to via pattern
backend/app/main.py backend/app/core/security.py limiter import from app.core.security import
from to via pattern
backend/app/api/v1/endpoints/health.py backend/app/api/deps.py Depends(get_db) Depends(get_db)
Implement security middleware stack with rate limiting, CSRF protection, and security headers.

Purpose: Protect the API from abuse and common web vulnerabilities (INFR-06, INFR-07). Output: FastAPI application with layered security: rate limiting (100/min), CSRF protection, trusted hosts, CORS, and security headers.

<execution_context> @/home/mikkel/.claude/get-shit-done/workflows/execute-plan.md @/home/mikkel/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/phases/01-core-infrastructure-security/01-RESEARCH.md (Pattern 3: FastAPI Security Middleware Stack) @.planning/phases/01-core-infrastructure-security/01-01-SUMMARY.md @.planning/phases/01-core-infrastructure-security/01-02-SUMMARY.md Task 1: Configure rate limiting and CSRF protection backend/app/core/security.py backend/app/api/deps.py Create backend/app/core/security.py: - Import and configure slowapi Limiter: - key_func=get_remote_address - default_limits=["100/minute"] - storage_uri from settings (default to memory, Redis for production) - Configure fastapi-csrf-protect CsrfProtect: - Create CsrfSettings Pydantic model with: - secret_key from settings - cookie_samesite = "lax" - cookie_secure = True (HTTPS only) - cookie_httponly = True - Implement @CsrfProtect.load_config decorator

Create backend/app/api/deps.py:

  • Import get_db from app.db.session
  • Re-export for cleaner imports in endpoints
  • Create optional dependency for CSRF validation:
    async def validate_csrf(csrf_protect: CsrfProtect = Depends()):
        await csrf_protect.validate_csrf_in_cookies()
    

Run: cd /home/mikkel/repos/debate && ruff check backend/app/core/security.py backend/app/api/deps.py Expected: No errors. Rate limiter configured at 100/minute, CSRF protection configured with secure cookie settings.

Task 2: Apply security middleware to FastAPI app and update health endpoint backend/app/main.py backend/app/api/v1/endpoints/health.py Update backend/app/main.py with middleware stack (order matters - first added = outermost):
  1. TrustedHostMiddleware:

    • allowed_hosts from settings.allowed_hosts
    • Block requests with invalid Host header
  2. CORSMiddleware:

    • allow_origins from settings.allowed_origins
    • allow_credentials=True
    • allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"]
    • allow_headers=["*"]
    • max_age=600 (cache preflight for 10 min)
  3. Rate limiting:

    • app.state.limiter = limiter
    • Add RateLimitExceeded exception handler
  4. Custom middleware for security headers:

    • Strict-Transport-Security: max-age=31536000; includeSubDomains
    • X-Content-Type-Options: nosniff
    • X-Frame-Options: DENY
    • X-XSS-Protection: 1; mode=block
    • Referrer-Policy: strict-origin-when-cross-origin

Update backend/app/api/v1/endpoints/health.py:

  • Keep GET /health as simple {"status": "healthy"}
  • Add GET /health/db that checks database connectivity:
    • Depends on get_db session
    • Execute "SELECT 1" query
    • Return {"status": "healthy", "database": "connected"} on success
    • Return {"status": "unhealthy", "database": "error", "detail": str(e)} on failure
  • Add @limiter.exempt decorator to health endpoints (don't rate limit health checks) Start the server and test:
cd /home/mikkel/repos/debate
source .venv/bin/activate
uvicorn backend.app.main:app --host 0.0.0.0 --port 8000 &
sleep 2

# Test health endpoint
curl -s http://localhost:8000/health

# Test database health
curl -s http://localhost:8000/api/v1/health/db

# Test security headers
curl -sI http://localhost:8000/health | grep -E "(X-Content-Type|X-Frame|Strict-Transport)"

# Test rate limiting (make 110 requests)
for i in {1..110}; do curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8000/health; done | sort | uniq -c

Expected: First 100+ requests return 200, some return 429 (rate limited). Kill the server after testing. Security middleware applied, health endpoints check database, rate limiting blocks excess requests.

1. `curl -sI http://localhost:8000/health` includes security headers (X-Content-Type-Options, X-Frame-Options) 2. `curl http://localhost:8000/api/v1/health/db` returns database status 3. Rapid requests (>100/min) return 429 Too Many Requests 4. Invalid Host header returns 400 Bad Request 5. `ruff check backend/` passes

<success_criteria>

  • Rate limiting enforced at 100 requests/minute per IP (INFR-06)
  • CSRF protection configured (INFR-07)
  • Security headers present in all responses
  • Health endpoints verify database connectivity
  • All middleware applied in correct order </success_criteria>
After completion, create `.planning/phases/01-core-infrastructure-security/01-03-SUMMARY.md`