fix(billing): close TOCTOU race in subscription transfer, centralize stripe test mocks#4239
Conversation
…stripe test mocks
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview The transfer logic now performs an in-transaction duplicate-org-subscription lookup using Testing infrastructure updates: extends Reviewed by Cursor Bugbot for commit f14eac2. Configure here. |
Greptile SummaryThis PR closes a TOCTOU race in subscription transfers by wrapping the check-then-update logic in
Confidence Score: 4/5Safe to merge for the transfer-route fix; the unresolved new-org creation TOCTOU in organization.ts (flagged in prior review) should be addressed before shipping the webhook path. The route.ts fix is solid and well-tested. The existing-membership branch in organization.ts is now correctly protected. However, the new-org creation path (lines 222-250 in organization.ts) still lacks transaction protection — a P1 concern raised in a previous review that remains open in this iteration. apps/sim/lib/billing/organization.ts — new-org creation path (lines 222-250) is unprotected; apps/sim/lib/billing/organization.test.ts — transaction branch is untested. Important Files Changed
Sequence DiagramsequenceDiagram
participant C1 as Concurrent Request 1
participant C2 as Concurrent Request 2
participant TX as DB Transaction
participant Sub as subscription row
participant Org as organization row
C1->>TX: BEGIN TRANSACTION
C1->>Sub: SELECT ... FOR UPDATE (locks row)
C2->>TX: BEGIN TRANSACTION
C2-->>Sub: blocked — waiting for lock
C1->>Org: SELECT ... FOR UPDATE (locks row)
C1->>Sub: check existingOrgSub
C1->>Sub: UPDATE referenceId → org-id
C1->>TX: COMMIT (releases locks)
C2->>Sub: SELECT ... FOR UPDATE (now acquired)
C2->>Org: SELECT ... FOR UPDATE
C2->>Sub: check existingOrgSub → FOUND
C2->>TX: ROLLBACK (duplicate detected)
Reviews (4): Last reviewed commit: "fix(billing): gate subscription transfer..." | Re-trigger Greptile |
|
Re Greptile P1 (new-org branch race in |
|
@greptile |
|
@cursor review |
|
@greptile |
|
@cursor review |
Previously the 'already belongs to this organization' early return fired before the org/member lookups, letting any authenticated caller probe sub-to-org pairings without being a member of the target org. Move the noop check after the admin/owner verification so unauthorized callers hit the 403 first.
|
@greptile |
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit f14eac2. Configure here.
Summary
db.transactionwith.for('update')row-locks in bothapp/api/users/me/subscription/[id]/transfer/route.tsandlib/billing/organization.tsto close a TOCTOU race that allowed concurrent requests to attach two subscriptions to the same org.for('update')terminal todbChainMockand document it insim-testing.mdstripe.mock.ts(stripeClientMock,stripePaymentMethodMock,createMockStripeEvent) and migrate billing tests off inline mocksType of Change
Testing
All billing + subscription tests pass (32 tests, 8 files).
Checklist