fix: handle invalid dealt_card_id in PlayerLive toggle_vote gracefully (fixes #2843)#2865
fix: handle invalid dealt_card_id in PlayerLive toggle_vote gracefully (fixes #2843)#2865immortal71 wants to merge 11 commits intoOWASP:masterfrom
Conversation
Apply Copilot suggestion: use catalog.get('Categories') + isinstance(dict)
check before accessing nested keys, preventing TypeError when Categories
is null or a non-dict value.
Add unit tests for all missing/malformed Categories edge cases:
- Categories key absent
- Categories is None
- Categories is non-dict (e.g. list)
- Category key missing from dict
- Category value is not a list
Closes OWASP#2487
…ies-2487 # Conflicts: # copi.owasp.org/lib/copi_web/live/player_live/show.ex # copi.owasp.org/test/copi/ip_helper_test.exs # copi.owasp.org/test/copi/rate_limiter_test.exs # copi.owasp.org/test/copi_web/controllers/card_controller_test.exs # copi.owasp.org/test/copi_web/live/game_live/show_test.exs # copi.owasp.org/test/copi_web/live/player_live/show_test.exs # copi.owasp.org/test/copi_web/plugs/rate_limiter_plug_test.exs # scripts/capec_map_enricher.py
Fixes OWASP#2843 Previously, oggle_vote called {:ok, dealt_card} = DealtCard.find(dealt_card_id), which raised a MatchError (crashing the LiveView process) whenever the ID did not exist in the database. Replace the bare pattern-match with a case expression so that an {:error, _} result returns a flash error message and leaves the socket alive, matching the expected behaviour described in the issue.
There was a problem hiding this comment.
Pull request overview
This PR primarily aims to prevent PlayerLive.Show from crashing when toggle_vote receives an invalid/non-existent dealt_card_id (issue #2843), while also adding a number of additional robustness/test-coverage changes across the repo (CAPEC enricher script/tests, rate limiter/IP helper tests, and some LiveView template/test additions).
Changes:
- Updated
toggle_voteto handleDealtCard.find/1errors without crashing the LiveView, showing a flash instead. - Added/expanded ExUnit coverage for
PlayerLive.Show,PlayerLive.Index, rate limiter plug, IP helper, and some schema changesets (plus new “pure” unit tests). - Hardened CAPEC enricher extraction logic and expanded Python unit tests around malformed
Categories/Categorysections.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
copi.owasp.org/lib/copi_web/live/player_live/show.ex |
Handles DealtCard.find/1 failures in toggle_vote instead of crashing. |
copi.owasp.org/test/copi_web/live/player_live/show_test.exs |
Adds LiveView test for invalid/non-existent dealt_card_id behavior. |
copi.owasp.org/lib/copi_web/live/player_live/index.html.heex |
Guards rendering of the player form component when @player is nil (index route). |
copi.owasp.org/test/copi_web/live/player_live_test.exs |
Adds more PlayerLive index/new form behavior coverage. |
copi.owasp.org/test/copi_web/plugs/rate_limiter_plug_test.exs |
Adds init passthrough test (currently duplicates an existing one). |
copi.owasp.org/test/copi/ip_helper_test.exs |
Adds more LiveView socket connect_info IP extraction cases. |
copi.owasp.org/test/copi/rate_limiter_test.exs |
Minor test structuring update (new describe section boundary). |
copi.owasp.org/test/copi_web/live/player_live/show_pure_test.exs |
New pure unit tests for helper functions in PlayerLive.Show + FormComponent. |
copi.owasp.org/test/copi_web/live/game_live/show_pure_test.exs |
New pure unit tests for helper functions in GameLive.Show. |
copi.owasp.org/test/copi/cornucopia/vote_test.exs |
New changeset unit test for Vote. |
copi.owasp.org/test/copi/cornucopia/player_test.exs |
New changeset unit tests for Player. |
copi.owasp.org/test/copi/cornucopia/dealt_card_test.exs |
New changeset/find unit tests for DealtCard (one test name is misleading). |
scripts/capec_map_enricher.py |
Makes CAPEC extraction more defensive and adds warnings for malformed sections. |
tests/scripts/capec_map_enricher_utest.py |
Adds Python unit tests covering malformed/missing Categories and Category. |
| <%= if @player do %> | ||
| <.live_component | ||
| module={CopiWeb.PlayerLive.FormComponent} | ||
| id={:new} | ||
| title={@page_title} | ||
| action={@live_action} | ||
| player={@player} | ||
| client_ip={@client_ip} | ||
| patch={~p"/games/#{@game.id}"} | ||
| /> | ||
| <% end %> |
There was a problem hiding this comment.
This template change (guarding the form component when @player is nil on the index route) isn’t mentioned in the PR description, which focuses on toggle_vote. Please update the PR description to include this additional behavior change, or split it into a separate PR if it’s unrelated to #2843.
| case DealtCard.find(dealt_card_id) do | ||
| {:error, _reason} -> | ||
| Logger.warning("toggle_vote: dealt_card_id=#{dealt_card_id} not found for player_id=#{player.id}, game_id=#{game.id}") | ||
| {:noreply, socket |> put_flash(:error, "Invalid card selection")} |
There was a problem hiding this comment.
dealt_card_id comes from client params and is passed directly into DealtCard.find/1, which calls Repo.get/2 on an integer PK. For non-numeric IDs (e.g. "abc"), Repo.get/2 will raise a cast error rather than returning nil, so this code can still crash the LiveView. Consider validating/parsing dealt_card_id (e.g. Integer.parse/1) or rescuing cast errors inside DealtCard.find/1 and returning {:error, :invalid_id}.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
Superseded by #2866 — a clean branch rebased directly from upstream/master with no unrelated commits. |
Summary
Fixes #2843
toggle_votecrashed the PlayerLive process whenever a client sent a non-existent or invaliddealt_card_id. The bare pattern-match{:ok, dealt_card} = DealtCard.find(dealt_card_id)raised aMatchErroron{:error, :not_found}, killing the LiveView process and disconnecting the user.Root Cause
DealtCard.find/1returns{:error, :not_found}for missing records. The match operator raised aMatchError, crashing the LiveView process.Fix
Replaced the bare pattern-match with a
caseexpression so that{:error, _}returns a flash error and keeps the socket alive, while the{:ok, dealt_card}path is fully unchanged in behaviour.Changes
lib/copi_web/live/player_live/show.exDealtCard.find/1in acaseexpression{:error, _}logs a warning and returns a flash error ("Invalid card selection") keeping the socket alive{:ok, dealt_card}path is unchangedtest/copi_web/live/player_live/show_test.exsdescribe "toggle_vote with invalid dealt_card_id"blockdealt_card_id = "999999999"(guaranteed non-existent) via phx-click"Invalid card selection"flashTest Coverage — All 3 paths of toggle_vote
dealt_card_id→ flash error, no crashBefore / After