Skip to content

/api/chat has no rate limiting, auth, or input validation — public endpoint can drain the OpenAI budget #68

Description

@Muawiya-contact

Summary

The public POST /api/chat endpoint has no authentication, no rate limiting, and no input validation. Every request triggers three billed OpenAI calls on the project's key, so anyone can run up unbounded cost by hitting the endpoint in a loop. Malformed requests also crash with an unhandled 500.

Impact

  • Cost / abuse: each request fans out to (1) the detectQueryTypes router call (gpt-4o-mini), (2) at least one text-embedding-ada-002 embedding, and (3) the streaming gpt-4o-mini completion — all billed to OPENAI_API_KEY. A simple loop against the endpoint can exhaust the budget and rate limits. For a public, nonprofit civic project this is a real exposure.
  • Robustness: the body is never validated. If messages is missing or empty, messages[messages.length - 1] throws, and with no try/catch the handler returns a raw 500 that can leak internal error details.

Steps to reproduce

  1. POST /api/chat with {"messages":[{"role":"user","content":"hi"}]} repeatedly in a loop → unbounded OpenAI usage, nothing throttles it.
  2. POST /api/chat with {} → 500, from the unhandled messages[...] access.

Root cause

app/api/chat/route.tsx:

export async function POST(req: Request) {
  const { messages } = await req.json()             // not validated
  const lastMessage = messages[messages.length - 1] // throws if messages missing/empty
  // ...no auth check, no rate limit, no try/catch
}

Suggested fix (reuses the existing stack)

  • Rate limit by IP with @upstash/ratelimit + Upstash Redis — Upstash is already a dependency (via QStash), so the account is in place. Sliding window (e.g. N requests/min per IP); return 429 when exceeded.
  • Validate the body with Zod (already a dependency): assert messages is a non-empty array of { role, content } before use; return 400 on failure.
  • Wrap the handler in try/catch → graceful JSON error, no leaked internals.
  • Optional: gate to authenticated users via the existing Pehchan OAuth, or add a per-session cap.

Small, self-contained; no schema or data migration.

Scope

Endpoint hardening only (rate limiting, validation, error handling). The retrieval-context fix is tracked separately in #<your Issue 1 number>.

Happy to open a PR for this.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions