Describe the bug
The PUT /api/games/:game_id/players/:player_id/card endpoint in api_controller.ex has no game lifecycle guard. It only checks whether the card was already played and whether the player has already played in the current round — it never checks game.started_at or game.finished_at.
This means a script can call the API to play cards before the game starts (lobby phase, started_at == nil) or after the game ends (finished_at != nil), corrupting the game state.
Affected file
copi.owasp.org/lib/copi_web/controllers/api_controller.ex
Code snippet
def play_card(conn, %{"game_id" => game_id, "player_id" => player_id, "dealt_card_id" => dealt_card_id}) do
with {:ok, game} <- Game.find(game_id) do
...
current_round = game.rounds_played + 1
cond do
dealt_card.played_in_round ->
conn |> put_status(:not_acceptable) |> json(%{"error" => "Card already played"})
Enum.find(...) ->
conn |> put_status(:forbidden) |> json(%{"error" => "Player already played a card in this round"})
true ->
# No check: is the game started? Has it finished?
dealt_card = Ecto.Changeset.change dealt_card, played_in_round: current_round
Copi.Repo.update dealt_card
Expected behavior
The endpoint should reject card plays with 422 Unprocessable Entity if:
game.started_at == nil — game has not started yet
game.finished_at != nil — game has already ended
Steps to reproduce
# Play a card via the API *before* start_game is called
curl -X PUT https://copi.owasp.org/api/games/:game_id/players/:player_id/card \
-H "Content-Type: application/json" \
-d '{"dealt_card_id": "<id>"}'
# Returns 200 and records the play even though the game hasn't started
Additional context
Issue #2568 covers the equivalent gap in the toggle_vote LiveView handler. This is the same missing guard in the REST API endpoint for card plays.
Describe the bug
The
PUT /api/games/:game_id/players/:player_id/cardendpoint inapi_controller.exhas no game lifecycle guard. It only checks whether the card was already played and whether the player has already played in the current round — it never checksgame.started_atorgame.finished_at.This means a script can call the API to play cards before the game starts (lobby phase,
started_at == nil) or after the game ends (finished_at != nil), corrupting the game state.Affected file
copi.owasp.org/lib/copi_web/controllers/api_controller.exCode snippet
Expected behavior
The endpoint should reject card plays with
422 Unprocessable Entityif:game.started_at == nil— game has not started yetgame.finished_at != nil— game has already endedSteps to reproduce
Additional context
Issue #2568 covers the equivalent gap in the
toggle_voteLiveView handler. This is the same missing guard in the REST API endpoint for card plays.