Repository navigation
fix(organizations): harden invite acceptance and settings writes - #7869
Conversation
- acceptOrganizationInvite logs the invitation row id instead of the invite token value, so the secret no longer reaches stdout. - acceptOrganizationInvite advances the organization group-policy revision for the membership it inserts, matching the SSO path, so consumers keyed by policyRevision observe the change. - updateOrganizationSettings validates its argument with OrganizationSettingsSchema before the UPDATE, matching the locked mutateOrganizationSettings path.
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Files Reviewed (3 files)
Incremental review of Remaining invite-acceptance vs. member-removal/invite ordering around the invitation-row lock is unchanged pre-existing behavior and is outside this diff. Previous Review Summary (commit 9ca5eb8)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit 9ca5eb8)Status: No Issues Found | Recommendation: Merge Executive SummaryAll three hardening changes in Files Reviewed (2 files)
Notes on verification
Reviewed by deepseek-flash-latest · Input: 102K · Output: 28.3K · Cached: 1.4M Review guidance: REVIEW.md from base branch |
|
Changes required before merge: the new policy revision bump creates a lock-order inversion. At packages/web-shared/src/lib/organizations/organizations.ts:699, acceptOrganizationInvite locks the organization row with FOR UPDATE. The new call at line 801 then requests the organization policy advisory lock. The settings path takes the opposite order: apps/web/src/routers/organizations/organization-settings-router.ts:330 acquires the policy advisory lock, then mutateOrganizationSettings locks the organization row at organizations.ts:882. An isolated PostgreSQL reproduction completed with the base lock sequence and raised SQLSTATE 40P01 with the added policy lock. This reproduces the lock sequence, not the full application flow. Use a consistent lock order across invitation acceptance and settings mutations. Add a concurrent PostgreSQL regression test for these paths before merging. |
Invitation acceptance now takes the membership-mutation advisory lock and the group-policy advisory lock before the organization row lock, matching SSO joins and the settings mutations that advance the policy revision. Adds a concurrent PostgreSQL regression test that holds the settings-path policy lock and then updates the settings, while invitation acceptance runs at the same time, and asserts both complete.
|
bot: Addressed the lock-order finding.
Suites run on Thanks for the precise reproduction — it pointed straight at the ordering rather than the revision bump itself. |
|
bot: Correction to the evidence line in my previous comment, so the record matches what actually ran.
Scope note on ordering, to avoid overstating the guarantee: the single order now covered is membership-mutation advisory lock, then organization group-policy advisory lock, then organization row lock. Invitation acceptance, SSO membership joins, member removal, and the settings mutations all follow it for those three locks. The invitation row lock that acceptance takes before the membership advisory lock is unchanged and not part of that order; member removal deletes pending invitation rows after taking the membership advisory lock, so that pair keeps whatever behaviour it had before this branch. |
Evidence
Evidence: partially demonstrated — the follow-up verification ran the organization PostgreSQL suites on this head and observed both operations complete under a deliberately overlapped run; no visual surface changed.
Verification
NODE_ENV=test pnpm exec jest --runInBandoverpackages/web-shared/src/lib/organizations/organizations.test.ts,organization-groups.test.ts,organization-group-policy-context.server.test.ts, andapps/web/src/routers/organizations/organization-settings-router.test.ts— 4 suites passed, 156 tests passed and 2 skipped, against a fresh local PostgreSQL with all migrations applied.pnpm --filter @kilocode/web-shared typecheckand the repository lint configuration over the changed files — passed.Change
Invitation acceptance now takes its membership-mutation advisory lock and the organization group-policy advisory lock before the organization row lock, the same order the settings mutations and SSO membership joins use. The policy lock helper is shared rather than duplicated, so every caller requests the same lock.
Concurrent invitation acceptance and settings writes for one organization therefore queue instead of holding each other's locks, and the policy revision still advances in the transaction that inserts the membership.
Production prerequisites
None.
Proxy answers