You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
The public signup endpoint returns the whole database row, internal columns included #452
Needs your OK: this removes fields from a public API response (GET /api/public/signups/[slug]). Nothing in the app calls it, but anyone scripting against it would notice. Swap pipeline:proposed for pipeline:ready to have it built.
What happens
getPublicSignup in src/services/signups.ts selects every column of signups and every column of slots, and src/app/api/public/signups/[slug]/route.ts hands the result straight to respond(). So the unauthenticated endpoint returns, besides what a participant needs:
signup:workspaceId, organizerId, claimTokenHash (never written, always null), tags, all of settings (including lockoutHoursBeforeSlot, reminderFromFieldRef, confirmationMessage), deletedAt, createdAt, updatedAt
each slot:workspaceId, signupId, priceCents, createdAt, updatedAt
None of this gives access to anything; every organizer route checks ids against the policy layer. The problem is the default: any server-only column added to signups or slots later goes public without anyone deciding it should. #447 is the same problem for commitments.
The /s/[slug] page is fine: it passes only picked fields to its client components (toSignupViewSlots, toSignupViewFields).
Fix, in outline
Add a toPublicSignup() mapping with an explicit list of fields: slug, title, description, status, opensAt, closesAt; from settings only requireEmail, sendReminders, groupByFieldRefs; fields as ref, label, fieldType, config; slots in the same shape toSignupViewSlots gives the page; and committedBySlot.
Apply it in the route before respond(). If it's simple, have the page reuse the same slot shape so the two can't drift.
Watch for
The page still needs id and workspaceId server-side for view tracking, so don't narrow what getPublicSignup returns to the page; narrow what the route sends.
src/app/query-budget.db.test.ts caps statements for the public page. This is a mapping, so it should add none.
Choices this makes
Field list: the default is the list above, which is what the public page itself shows. tags and the other organizer settings are left out.
Done when
The route responds through the allow-list.
A test lists the response's keys (top level, slot, field) and fails if one is added.
Found by the pipeline's security scout in the participant flow (src/app/api/public, src/services/signups.ts), at a9bcff7. Checked by an independent DB test that called the route handler on a published signup and listed the keys returned. https://claude.ai/code/session_01H1WEpHa6h8WwVYLTAKoqhg
Activity