Skip to content

fix: GET /api/admin/challenges performs self-heal DB writes with no rate limiting #271

Description

@nazarli-shabnam

Problem

Every mutating admin route under `app/api/admin/challenges/` calls `rateLimitAdminMutation(auth)` before writing (see `PATCH`/`DELETE` in `[id]/route.ts`, and `finalize/route.ts`). But `GET /api/admin/challenges` self-heals ended/expired challenges as a side effect of a plain read — issuing real `UPDATE`s via `resolveEndedChallenges`/`resolveExpiredPending` — with only `requireAdmin`, no `rateLimitAdminMutation`:

```ts
export async function GET(req: NextRequest) {
const auth = await requireAdmin(req);
if (auth instanceof NextResponse) return auth;
// ... no rateLimitAdminMutation call ...
const ended = await db.select().from(challenges).where(and(eq(challenges.status, "active"), lt(challenges.endsAt, now)));
await resolveEndedChallenges(ended, now);
const expiredPending = await db.select().from(challenges).where(and(eq(challenges.status, "pending"), lt(challenges.endsAt, now)));
await resolveExpiredPending(expiredPending, now);
...
}
```

Impact

The admin challenges page calls this endpoint on every filter change and on mount, and it's a GET (proxies/browsers may prefetch or retry GETs, and it's easy to poll accidentally via a bookmarklet or script). Because it performs real writes with no throttle, it's the one write path in this route group that lacks the protection every other mutation in the same directory already has. Note: this is distinct from the already-fixed #176 (which was about the cost of self-healing being N+1 — now addressed here via `mapWithConcurrency`); this issue is specifically about the missing rate limit on the write path itself.

Location

`app/api/admin/challenges/route.ts:22-49`

Suggested fix

Apply `rateLimitAdminMutation` (or a lighter per-admin throttle) before the self-heal writes, or move the self-heal logic to a scheduled job / restrict it to the existing `finalize` endpoint so `GET` stays read-only.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    To triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions