Skip to content

fix: override-winner admin action has no status guard, unlike end #270

Description

@nazarli-shabnam

Problem

In the admin challenge-moderation endpoint, the `end` action checks the challenge's current status before mutating it:

```ts
if (body.action === "end") {
if (challenge.status !== "active") {
return NextResponse.json({ error: "Only active challenges can be ended" }, { status: 409 });
}
...
}
```

But the `override-winner` action has no equivalent check — it only validates that `winnerId` is a participant (or null), then unconditionally sets `status: "completed"`:

```ts
if (body.action === "override-winner") {
const winnerId = body.winnerId ?? null;
if (winnerId !== null && winnerId !== challenge.challengerId && winnerId !== challenge.challengedId) {
return NextResponse.json({ error: "winnerId must be a participant or null" }, { status: 400 });
}
const [updated] = await db
.update(challenges)
.set({ status: "completed", winnerId })
.where(eq(challenges.id, challengeId))
.returning();
...
}
```

Impact

A direct API call (or a future UI regression that renders the override control for more statuses than intended) can force a `pending`, `declined`, or `cancelled` challenge straight to `completed` with a fabricated winner. This corrupts the stats shown on the admin challenges page (`byStatus` counts, `fromSuggestions` totals) and produces a "won: @user" result for a match that was never played, declined, or was cancelled. The client UI currently only renders override buttons for `active`/`completed` rows, but that's UI-only protection — the API itself doesn't enforce it, unlike the `end` action right above it in the same file.

Location

`app/api/admin/challenges/[id]/route.ts:77-96` (compare with the `end` action's guard at lines 42-45)

Suggested fix

Reject `override-winner` unless `challenge.status` is `"active"` or `"completed"` (mirroring the `end` action's status guard), returning a 409 otherwise.

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