Discovered during
#10219 (board reachability, PR #10293). While adding GET /api/llc/boards (which I built tenant-scoped via ctx.org_id), I found the existing boards.py endpoints have the same IDOR class that #10148/#10258 fixed for sprints.py.
Problem
llc/api/boards.py endpoints depend only on get_session — no get_current_user + require_org_context:
GET /api/llc/boards/{board_id} — returns ANY board by id (cross-tenant read)
GET /api/llc/boards/{board_id}/items — cross-tenant work-item read
POST /api/llc/boards/{board_id}/move — cross-tenant mutation
POST /api/llc/boards/kanban / /sprint — trust body company_id, no tenant check
Any authenticated caller can read/mutate another company's boards by enumerating board UUIDs.
Fix
Mirror the #10258 fix for sprints.py: inject get_current_user + require_org_context on every boards.py endpoint; entity-keyed routes load the board, derive its company_id, return 404 on cross-tenant; create endpoints validate the parent (project/sprint) company. Add test_boards_idor.py mirroring test_sprints_idor.py. (The new GET /api/llc/boards list endpoint added in #10293 is already guarded.)
Part of umbrella #9923. Related: #10148, #10258 (the sibling sprints.py IDOR fix).
Discovered during
#10219 (board reachability, PR #10293). While adding
GET /api/llc/boards(which I built tenant-scoped via ctx.org_id), I found the existing boards.py endpoints have the same IDOR class that #10148/#10258 fixed for sprints.py.Problem
llc/api/boards.pyendpoints depend only onget_session— noget_current_user+require_org_context:GET /api/llc/boards/{board_id}— returns ANY board by id (cross-tenant read)GET /api/llc/boards/{board_id}/items— cross-tenant work-item readPOST /api/llc/boards/{board_id}/move— cross-tenant mutationPOST /api/llc/boards/kanban//sprint— trust body company_id, no tenant checkAny authenticated caller can read/mutate another company's boards by enumerating board UUIDs.
Fix
Mirror the #10258 fix for sprints.py: inject
get_current_user+require_org_contexton every boards.py endpoint; entity-keyed routes load the board, derive its company_id, return 404 on cross-tenant; create endpoints validate the parent (project/sprint) company. Addtest_boards_idor.pymirroringtest_sprints_idor.py. (The newGET /api/llc/boardslist endpoint added in #10293 is already guarded.)Part of umbrella #9923. Related: #10148, #10258 (the sibling sprints.py IDOR fix).