mirror of
https://github.com/pezkuwichain/pwap.git
synced 2026-07-26 12:05:41 +00:00
165cb47c64
Round 2 — fixes the CRITICAL discovered during round 1 (as severe as the withdrawal BOLA): lock_/release_/refund_escrow_internal had default PUBLIC EXECUTE and the client called release_escrow_internal directly with the anon key, so anyone could drain any victim's LOCKED balance. - release_escrow_internal now reachable only via new signed confirm-payment edge function: verifies seller wallet signature (raw + <Bytes>), asserts the wallet owns the seller identity, derived user_id == trade.seller_id, trade == payment_sent, single-use nonce, then release runs with the service role. confirmPaymentReceived now invokes confirm-payment (no more anon rpc). - lock_escrow_internal now behind new signed lock-escrow edge function (a caller can only lock its own balance; stops griefing a victim's balance). Removed a zero-amount no-op lock call. - Migration 20260725030000: REVOKE EXECUTE on lock_/release_/refund_escrow_internal from PUBLIC/anon/authenticated; GRANT to service_role only. refund has no direct client caller (service-role + admin_resolve_dispute only). - DEPLOY_RUNBOOK.md consolidates the ordered migrations, edge functions and secrets for both security rounds. Migration 20260725030000 must ship WITH the two new edge functions + frontend or offer-create/payment-release break. Remaining (non-fund, Round 2+ follow-up): read RPCs still key on a non-secret user_id (binding to a signed session would force a sign-prompt on every passive balance read — deferred); p2p_fiat_offers/trades/messages retain USING(true) (status labels move no funds now that all escrow movement is service-role-gated).
53 lines
2.8 KiB
SQL
53 lines
2.8 KiB
SQL
-- =====================================================
|
|
-- Migration: Lock down internal-ledger escrow RPCs
|
|
-- Date: 2026-07-25
|
|
-- =====================================================
|
|
--
|
|
-- Fix for the CRITICAL drain: lock_/release_/refund_escrow_internal (migration
|
|
-- 014) had default PUBLIC EXECUTE, and the client called release_escrow_internal
|
|
-- directly with the public anon key. An anon caller could therefore run
|
|
-- release_escrow_internal(victim, attacker, token, amount)
|
|
-- and drain any victim's LOCKED balance.
|
|
--
|
|
-- These functions now execute ONLY as the service role. Every legitimate caller
|
|
-- goes through a service-role edge function that first verifies a wallet
|
|
-- signature + identity ownership:
|
|
-- * release -> confirm-payment edge function (seller-authorized)
|
|
-- * lock -> lock-escrow edge function (owner-authorized)
|
|
-- * refund -> process-withdraw (on failed payout) + admin_resolve_dispute
|
|
-- (dispute refunds/splits). No direct client caller.
|
|
--
|
|
-- SECURITY DEFINER functions that call these internally (admin_resolve_dispute,
|
|
-- cancel_expired_trades, process_deposit) are unaffected: EXECUTE for an inner
|
|
-- call is checked against the function OWNER, not the session role.
|
|
|
|
-- release_escrow_internal(p_from_user_id, p_to_user_id, p_token, p_amount, p_reference_type, p_reference_id)
|
|
REVOKE EXECUTE ON FUNCTION release_escrow_internal(UUID, UUID, TEXT, DECIMAL, TEXT, UUID)
|
|
FROM PUBLIC, anon, authenticated;
|
|
GRANT EXECUTE ON FUNCTION release_escrow_internal(UUID, UUID, TEXT, DECIMAL, TEXT, UUID)
|
|
TO service_role;
|
|
|
|
-- refund_escrow_internal(p_user_id, p_token, p_amount, p_reference_type, p_reference_id)
|
|
REVOKE EXECUTE ON FUNCTION refund_escrow_internal(UUID, TEXT, DECIMAL, TEXT, UUID)
|
|
FROM PUBLIC, anon, authenticated;
|
|
GRANT EXECUTE ON FUNCTION refund_escrow_internal(UUID, TEXT, DECIMAL, TEXT, UUID)
|
|
TO service_role;
|
|
|
|
-- lock_escrow_internal(p_user_id, p_token, p_amount, p_reference_type, p_reference_id)
|
|
REVOKE EXECUTE ON FUNCTION lock_escrow_internal(UUID, TEXT, DECIMAL, TEXT, UUID)
|
|
FROM PUBLIC, anon, authenticated;
|
|
GRANT EXECUTE ON FUNCTION lock_escrow_internal(UUID, TEXT, DECIMAL, TEXT, UUID)
|
|
TO service_role;
|
|
|
|
COMMENT ON FUNCTION release_escrow_internal IS
|
|
'Release escrow seller->buyer. SERVICE ROLE ONLY — invoked by the confirm-payment
|
|
edge function after verifying the seller''s wallet signature + identity ownership.';
|
|
|
|
COMMENT ON FUNCTION refund_escrow_internal IS
|
|
'Refund locked escrow to its owner. SERVICE ROLE ONLY — invoked by process-withdraw
|
|
(failed payout) and admin_resolve_dispute (dispute refund/split).';
|
|
|
|
COMMENT ON FUNCTION lock_escrow_internal IS
|
|
'Lock a user''s own available balance into escrow. SERVICE ROLE ONLY — invoked by
|
|
the lock-escrow edge function after verifying the caller owns that identity.';
|