fix: harden authorization checks on freezeorder and paytobuyer - #834
Conversation
- Refuse freezeorder on orders that are not in a freezable state (ACTIVE, FIAT_SENT, DISPUTE), since it settles the hold invoice. - Compare community_id with strict string equality instead of loose ObjectId comparison. - Require community solvers to be the solver assigned to the order's dispute, matching the existing behavior of settleorder/cancelorder. - Refuse solver actions on orders where the solver is the buyer or the seller. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
WalkthroughTwo admin commands— ChangesAdmin command authorization hardening
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install failed due to a network error. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@bot/start.ts`:
- Around line 342-350: The handler for freezeorder sets order.status = 'FROZEN'
before calling settleHoldInvoice, which can leave an order stuck in FROZEN if
settleHoldInvoice fails (because the helper swallows/logs errors); change the
flow so you only persist order.status = 'FROZEN' after settleHoldInvoice
succeeds (or make settleHoldInvoice throw on failure and abort/rollback the
status change), i.e., locate the freezeorder flow and the settleHoldInvoice call
and move the DB update of order.status (and related save/update code) to run
after a successful settleHoldInvoice result (apply the same change to the
duplicate block around lines 383-390).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
freezeorder saved order.status = 'FROZEN' before calling settleHoldInvoice, and the helper swallowed LND errors, so a failed settlement left the order stuck in FROZEN with the invoice still held. Settle first and save after, and make settleHoldInvoice rethrow after logging so callers can abort their state changes. All call sites (freezeorder, settleorder, release) already run inside try/catch. Also fixes prettier formatting that broke CI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
I reviewed the current HEAD strictly. The authorization hardening looks good, and the freezeorder persistence ordering bug is fixed. But I still see one blocker in the new paytobuyer flow.
When the command is invoked on a FROZEN order, the handler now persists order.status = 'PAID_HOLD_INVOICE' before calling payToBuyer(bot, order). If payToBuyer then fails before the payment is actually sent or before the retry row is created, the order is no longer FROZEN, so:
- the admin cannot safely retry through the same
FROZENpath anymore, and subscribe_invoice.tsalso treatsPAID_HOLD_INVOICEas a progressed state and will not re-enter the same freeze guard.
This is the same class of ordering problem that the PR correctly fixed for freezeorder: persist the state transition only after the irreversible side effect succeeds, or make the failure path fully transactional/idempotent. Right now the code moves the order out of FROZEN before the payout operation has proved success.
Please either:
- keep the order in
FROZENuntilpayToBuyerhas definitely taken ownership of the retry lifecycle, or - prove the pre-write is safe by making the failure path impossible to lose (for example, no swallowed exceptions before pending-payment persistence, and explicit idempotent recovery for
PAID_HOLD_INVOICEorders with no successful payout).
|
I re-checked the current code path more carefully and my previous The
So the blocker I raised is not actually a valid regression in this PR. The real changes here are the auth hardening plus the |
There was a problem hiding this comment.
Re-reviewed the current HEAD after re-checking the existing payout semantics. The previous REQUEST_CHANGES I left on paytobuyer was too aggressive: PAID_HOLD_INVOICE is already the established pre-payout state in the normal subscriber path, /paytobuyer explicitly accepts both FROZEN and PAID_HOLD_INVOICE, and failed payout attempts already feed the retry lifecycle through PendingPayment or manual re-invocation. With that corrected, the actual changes in this PR look good: the auth hardening is sound and the freezeorder persistence ordering fix is the right one.
Summary
This PR tightens the authorization logic of the
freezeorderandpaytobuyeradmin/solver commands inbot/start.ts, bringing them to parity with the checks already enforced bysettleorderandcancelorder.Changes
freezeorder: since freezing settles the hold invoice, the command now refuses to run on orders that are not in a state with funds in escrow (ACTIVE,FIAT_SENT,DISPUTE).order.community_idis now compared against the solver'sdefault_community_idusing strict string equality instead of a loose ObjectId comparison.Full admins (
ctx.admin.admin) are unaffected.Testing
npx tsccompiles cleanly.🤖 Generated with Claude Code
Summary by CodeRabbit