POST /calls/{id}/reprocess is unmetered spend for any authenticated user #8

Closed
opened 2026-08-19 22:06:54 -04:00 by logan · 1 comment
Owner

routers/calls.py:42 is gated only by the router-level "any valid Firebase token" dependency. Any authenticated viewer can trigger the full Whisper + Gemini pipeline on any call, repeatedly, with no rate limit and no quota.

A stranger with a free account can loop this and burn the owner's OpenAI and Gemini credit. The only thing currently preventing it is that accounts were handed out by hand — and that mitigation disappeared the moment self-serve signup shipped.

Scheduled as item B2c of SAAS_PLAN.md; filed separately because it is exploitable today, independent of the rest of the SaaS work.

`routers/calls.py:42` is gated only by the router-level "any valid Firebase token" dependency. Any authenticated viewer can trigger the full Whisper + Gemini pipeline on any call, repeatedly, with no rate limit and no quota. A stranger with a free account can loop this and burn the owner's OpenAI and Gemini credit. The only thing currently preventing it is that accounts were handed out by hand — and that mitigation disappeared the moment self-serve signup shipped. Scheduled as item B2c of `SAAS_PLAN.md`; filed separately because it is exploitable today, independent of the rest of the SaaS work.
Author
Owner

Already fixed before this issue was filed — my mistake for filing it.

routers/calls.py now gates POST /calls/{id}/reprocess on require_admin_token, not the router-level "any valid Firebase token", and calls reprocess_limiter.check(call_id) before queueing the pipeline. The limiter is _RateLimiter(max_calls=3, window_seconds=600) in internal/auth.py:260.

Both landed in a3681ea ("Stamp org_id everywhere and gate every route that leaked across tenants"). The tally that produced this issue was written from the DEFERRED.md note rather than from the code, which is exactly the drift CLAUDE.md warns about.

The separate backlog reprocess (server-26#7) is still open and still needs its own spend ceiling.

Already fixed before this issue was filed — my mistake for filing it. `routers/calls.py` now gates `POST /calls/{id}/reprocess` on `require_admin_token`, not the router-level "any valid Firebase token", and calls `reprocess_limiter.check(call_id)` before queueing the pipeline. The limiter is `_RateLimiter(max_calls=3, window_seconds=600)` in `internal/auth.py:260`. Both landed in `a3681ea` ("Stamp org_id everywhere and gate every route that leaked across tenants"). The tally that produced this issue was written from the DEFERRED.md note rather than from the code, which is exactly the drift CLAUDE.md warns about. The separate backlog reprocess (server-26#7) is still open and still needs its own spend ceiling.
logan closed this issue 2026-08-20 03:02:55 -04:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: logan/server-26#8