Child of PAR-182. Highest-priority security fix.
Several by-UUID paths in PostgresTaskSystem (tesks/src/main/java/io/paradaux/tesks/tasks/store/PostgresTaskSystem.java) act on a raw issue UUID with no tenant_id predicate, so anyone who learns/guesses another workspace's issue UUID can read or mutate it. Today it's only safe because UUIDs leak solely through tenant-scoped lists — no defense-in-depth.
Fix: add and tenant_id = ?::uuid (from TenantContext) to every by-id read/write, matching the pattern reorderTask/moveTask already use.
Paths to fix (line numbers approximate):
taskById (~285)getTaskDetail (~307)listComments (~336)listActivity (~345)updateTask read-current + update (~425, ~467)deleteTask (~540)Acceptance: a request bound to tenant A cannot read/mutate/delete an issue, comment, activity row, or labels belonging to tenant B even with a valid UUID. Add a regression test asserting cross-tenant by-id access returns not-found.
Completed on develop (32a1fa2).
On review, the core IDOR was already closed: taskById, getTaskDetail, listComments, listActivity, deleteTask are all tenant_id-scoped, and updateTask does a tenant-scoped current-read and early-returns (if (cur == null) return false;) so its label/event writes are unreachable for an out-of-tenant issue.
Two checklist items remained, now done:
insert into issue_label accepted a raw label_id from the caller with no tenant check (could attach another tenant's label to your own issue). Scoped the insert to label rows owned by the active tenant (foreign/bogus ids are silently skipped).updateTask won't attach a foreign label. All 37 PostgresTaskSystemTest tests pass.Acceptance met.
Code-complete (pending commit). Added
and tenant_id = ?::uuid(fromTenantContext) to every by-UUID path inPostgresTaskSystem:taskById,getTaskDetail,listComments(via join toissue),listActivity(via join),updateTask(current-read + the update statement), anddeleteTask. Cross-tenant by-id access now returns not-found / no-op, matching thereorderTask/moveTaskpattern.Regression test added:
PostgresTaskSystemTest.byIdPathsAreTenantScoped— asserts a second tenant holding a known issue UUID cannot read detail/comments/activity, cannot update or delete it, and the owning tenant's data is untouched. Full suite green (93 tests).