Repository navigation
core: the project-board endpoint never sees the actor — a member can read another member's Project cards #1723
Description
Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.
Written-by: FlyBot Lead
Claimed-by: FlyBot Lead
Claim-Id: 34c190be88e24d0caa4c1fa4483b1c5c
Branch: dev/1-board-actor-filter
Base: f0159ea
At: 2026-10-29T16:53:30Z
Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.
Written-by: FlyBot Lead
What — project-board is the one Project endpoint that never sees the caller. core/src/host/store.js case 'project-board' is return { tasks: rt.projectBoard?.(projectText(body.id, 200)) ?? [] } — no actor, no row filter — while tasks refuses the same caller for the same card (bot/forbidden, "You cannot see that Bot's tasks"). Because #1716 put project-board in authz.js's TASK_ENDPOINTS, any signed-in actor is allowed to call it, so a member can read cards on another member's Project board that tasks refuses to show.
Reproduction (behaviour, live main f0159ea8) — two members; member owner-2 owns Bot b and room r2 (projectId: project-p2); one card with ownerBotId: 'b', roomId: 'r2'; authz enforced:
tasks {botId:'b'} -> {"ok":false,"error":{"code":"bot/forbidden","message":"You cannot see that Bot's tasks"}}
project-board {id:'project-p2'} -> ok, {"id":"task-b1","source":"group","column":"in-progress","assignee":"Writer","updatedAt":10}
The probe is probe-board-cross-member.mjs (Product, tmp/audit-1644z/…), exit 1 on both f8e4943c and f0159ea8.
Why it now has a UI face as well: #1715's ProjectBoardOverlay reads #project=<id> (or projects[0]) and calls project-board, then a row click goes to #task=<id> — so the overlay can paint a card the same actor cannot open. Neither #1716's test nor #1715's projects.test.js has a cross-member case; the former only covers same-owner ALLOW, the latter only asserts a signed-in member may read project-board at all.
Expected — a board row is filtered by the same visibility rule tasks applies (mayAct / ancestorOwned + room visibility), so an actor sees on a Project board exactly the cards it could open through tasks. Concretely: pass the actor into the project-board case, filter in project-board.js with the existing predicate (one place, not a second copy), and keep the empty result a state, not an error. authz.js whitelist, endpoint names and the row shape stay as they are.
Out of scope — the row board:{kind,id} addition (Product's P-167/P-171) and any client change; this issue is the host-side filter plus its test.
Red first — a cross-member probe that fails on live main: member A's project-board must not return member B's card (expected [] / actual [task-b1]), while member B's own read still returns it, and the same-owner ALLOW case from #1716 stays green.
Duplicate check (live main, before opening) — open+closed searched for project-board actor, cross-member, project-board forbidden, actor filter board: no match. Nearest are #1716 (closed; added the whitelist, did not filter), #1715/#1710 (closed; actor wired into create/read/bind only), and #1713 (closed; prototype-chain ids). Nothing covers row filtering.
Activity
HugoXK commentedon Oct 29, 2026
Written-by: FlyBot Lead
Acceptance boundary added (decided by Chief, and it is the distinction this bug must not blur):
An empty result is a state, not an error. After filtering, a Project the actor may read but which has no row it may see must answer
okwithtasks: []— it must not answerbot/forbidden.forbiddenis only for an actor that may not read the Project at all; "no cards you may see" is the normal empty state, exactly as an unknown Project already answers[](core/src/host/project-board.js:25-28).So the red-first set is four, not three:
expected [] / actual [task-b1];#1716's same-owner ALLOW stays green;ok+[], neverforbidden(new — protects the "no rows" path from being collapsed into "no permission").authz.jswhitelist, endpoint name and row shape stay unchanged; this narrows the read surface, it does not add one.HugoXK commentedon Oct 29, 2026
Release
Written-by: FlyBot Lead
Released: 34c190be88e24d0caa4c1fa4483b1c5c
Reason: shipped: #1725 merged 241d76a (merged_at 2026-10-29T17:23:46Z)
At: 2026-10-29T17:30:52Z
core: paint only the Project cards `tasks` would let a member open (T…
Add the per-Project assistant and its daily digest
Add the per-Project assistant and its daily digest