IonWarpbotexample reviewconst project = await projects.get(params.id);const project = await projects.getForMember({projectId: params.id, userId: session.user.id,});
The endpoint authenticates the caller but never binds the requested project to their membership. Changing the URL ID returns another workspace’s private project. Resolve membership before returning it.
P1Blocked-email list is matched case-sensitively — trivially bypassed at signup · Security Review
When an admin blocks an email, a blocked spammer re-registers with different letter case ('BAD@spamclub.com') and the account is created: should_block? does a case-sensitive where(email:) and the signup path never downcases the submitted email, so the new control fails its one job.