AOSSIE-Org / AOSSIE-Org/DebateAI
[BUG]: No rate limiting on AI debate endpoints — allows abuse of Gemini/LLM API quota
- Dominant language
- TypeScript
- Stars
- 84
- Forks
- 198
- Avg merge
- 2d 19h
- Merged PRs (30d)
- 30
Description
### Bug Description
The backend exposes AI-powered debate endpoints — primarily `/debate-vs-bot` and any other routes that proxy requests to the Gemini LLM API (e.g. AI opponent response generation, fallacy detection, or post-debate analysis endpoints) — without any request throttling in front of them.
Each hit to these endpoints triggers at least one outbound call to the Gemini API, which is billed and quota-limited by Google. Since there is currently no middleware enforcing per-user, per-session, or per-IP request limits, the endpoint's effective rate is bounded only by network/browser speed — meaning a single client can fire dozens or hundreds of requests per minute with a simple script or load-testing tool.
This creates two concrete risks:
**1. Cost/Quota exhaustion (availability risk):**
The Gemini API key configured in `backend/config/config.prod.yml` is shared across all users of the deployed instance. A single abusive client can exhaust the daily/per-minute quota, which would cause the LLM to stop responding for *every other user* on the platform — not just the abuser. This turns a single bad actor into a platform-wide outage.
**2. Unbounded billing exposure (cost risk):**
Since Gemini API usage is metered, an attacker (or even an accidental infinite-loop bug in the frontend) could generate significant unexpected cost with no circuit breaker in place to stop it.
**Why this is more than a "nice to have":**
Unlike a generic REST endpoint, LLM-backed endpoints are inherently expensive per-call (both in latency and $), which is exactly the class of endpoint that *should* have stricter throttling than the rest of the API — but currently there's no differentiation; all routes appear to be treated the same, if any rate limiting exists at all.
## Steps to Reproduce
1. Authenticate as any valid user and obtain a session/auth token
2. Using `curl`, Postman, or a small script, send 20–30 requests in rapid succession to the debate-vs-bot endpoint (or whichever route triggers an LLM call), e.g.:
```bash
for i in {1..30}; do
curl -X POST https:///api/debate-vs-bot \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{"message": "test"}' &
done
```
3. Observe that all (or nearly all) requests return `200 OK` and are forwarded to Gemini, with no `429` responses or delay introduced by the server
## Expected Behavior
- LLM-backed routes should have a dedicated rate-limiting middleware, separate from (and stricter than) general API routes
- Limiting should be scoped per authenticated user (not just per-IP, since users may share IPs on mobile networks/NAT)
- Requests over the limit should receive a `429 Too Many Requests` with a `Retry-After` header and a clear JSON error body
- Limits should be configurable via environment variables (e.g. `LLM_RATE_LIMIT_MAX`, `LLM_RATE_LIMIT_WINDOW_MS`) rather than hardcoded, so different deployments can tune based on their quota tier
## Impact
**High** — this is a direct, low-effort path to:
- Full-platform LLM feature outage for all users (shared quota exhaustion)
- Uncontrolled billing cost on the project maintainers' Gemini API key
- No audit trail currently exists to identify which user/IP caused the exhaustion, making incident response harder
## Suggested Fix (high level)
Introduce an `express-rate-limit` (or equivalent) middleware scoped specifically to LLM-calling routes, keyed by authenticated user ID where available, falling back to IP. This should sit *before* the controller that calls the Gemini API, so blocked requests never reach the LLM call itself.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start at the /debate-vs-bot route and other endpoints that proxy Gemini calls, then inspect backend/config/config.prod.yml and the authentication flow. Reproduce the current behavior with the curl sequence in the issue and identify each LLM-calling entry point. Done means over-limit requests receive 429, Retry-After, and a clear JSON body, limits are configurable, and blocked requests do not reach Gemini.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- express, google-cloud, typescript
- Domain
- api, backend, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100