Review source
Round 14 (Opus 4.8) — P1. Rate limiting flaws.
Problem
- Rate limiting keys on request.client.host only (app.py:124), ignoring X-Forwarded-For. Behind any load balancer, every client collapses into one bucket (the LB's IP) — rate limiting is effectively broken in production.
- X-Plan-Tier is always 'free'. _resolve_plan_tier reads request.state.plan_tier but nothing in the codebase ever SETS it. Tiered rate limiting (pro/enterprise from GL-317) is inert dead wiring.
Fix
- Rate limiter should honor X-Forwarded-For (take the first/client IP) when behind a trusted proxy; make trusted-proxy behavior configurable (GRANTLAYER_TRUST_PROXY=false by default for security)
- Set request.state.plan_tier in the auth dependency that decodes the JWT/workspace — resolve the workspace's plan_tier from DB and attach it
- Verify tiered limits actually apply (free=100, pro=1000, enterprise=unlimited)
Acceptance Criteria
- Behind a proxy with TRUST_PROXY=true, rate limiting uses the real client IP
- request.state.plan_tier is populated from the workspace record
- Test: pro-tier workspace gets higher limit than free-tier
- mypy clean, ruff clean, tests >= baseline
Review source
Round 14 (Opus 4.8) — P1. Rate limiting flaws.
Problem
Fix
Acceptance Criteria