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>
189 lines
6.2 KiB
Markdown
189 lines
6.2 KiB
Markdown
---
|
|
phase: 01-core-infrastructure-security
|
|
plan: 03
|
|
type: execute
|
|
wave: 2
|
|
depends_on: ["01-01", "01-02"]
|
|
files_modified:
|
|
- backend/app/main.py
|
|
- backend/app/core/security.py
|
|
- backend/app/api/deps.py
|
|
- backend/app/api/v1/endpoints/health.py
|
|
autonomous: true
|
|
|
|
must_haves:
|
|
truths:
|
|
- "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"
|
|
artifacts:
|
|
- path: "backend/app/core/security.py"
|
|
provides: "Rate limiting and CSRF configuration"
|
|
contains: "Limiter"
|
|
- path: "backend/app/api/deps.py"
|
|
provides: "FastAPI dependency injection"
|
|
contains: "get_db"
|
|
- path: "backend/app/main.py"
|
|
provides: "Security middleware stack"
|
|
contains: "TrustedHostMiddleware"
|
|
key_links:
|
|
- from: "backend/app/main.py"
|
|
to: "backend/app/core/security.py"
|
|
via: "limiter import"
|
|
pattern: "from app\\.core\\.security import"
|
|
- from: "backend/app/api/v1/endpoints/health.py"
|
|
to: "backend/app/api/deps.py"
|
|
via: "Depends(get_db)"
|
|
pattern: "Depends\\(get_db\\)"
|
|
---
|
|
|
|
<objective>
|
|
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.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@/home/mikkel/.claude/get-shit-done/workflows/execute-plan.md
|
|
@/home/mikkel/.claude/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<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
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: Configure rate limiting and CSRF protection</name>
|
|
<files>
|
|
backend/app/core/security.py
|
|
backend/app/api/deps.py
|
|
</files>
|
|
<action>
|
|
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:
|
|
```python
|
|
async def validate_csrf(csrf_protect: CsrfProtect = Depends()):
|
|
await csrf_protect.validate_csrf_in_cookies()
|
|
```
|
|
</action>
|
|
<verify>
|
|
Run: `cd /home/mikkel/repos/debate && ruff check backend/app/core/security.py backend/app/api/deps.py`
|
|
Expected: No errors.
|
|
</verify>
|
|
<done>
|
|
Rate limiter configured at 100/minute, CSRF protection configured with secure cookie settings.
|
|
</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 2: Apply security middleware to FastAPI app and update health endpoint</name>
|
|
<files>
|
|
backend/app/main.py
|
|
backend/app/api/v1/endpoints/health.py
|
|
</files>
|
|
<action>
|
|
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)
|
|
</action>
|
|
<verify>
|
|
Start the server and test:
|
|
```bash
|
|
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.
|
|
</verify>
|
|
<done>
|
|
Security middleware applied, health endpoints check database, rate limiting blocks excess requests.
|
|
</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
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
|
|
</verification>
|
|
|
|
<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>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/01-core-infrastructure-security/01-03-SUMMARY.md`
|
|
</output>
|