mirror of
https://github.com/pezkuwichain/pwap.git
synced 2026-07-27 10:05:41 +00:00
security(p2p): close escrow-RPC anon-drain (release/lock behind signed edge functions)
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).
This commit is contained in:
@@ -141,6 +141,50 @@ export function buildDepositChallenge(p: {
|
||||
].join('\n')
|
||||
}
|
||||
|
||||
/**
|
||||
* Canonical challenge for a seller confirming payment / releasing escrow.
|
||||
* MUST be byte-identical to the client (shared/lib/p2p-fiat.ts).
|
||||
*/
|
||||
export function buildConfirmPaymentChallenge(p: {
|
||||
tradeId: string
|
||||
sellerIdentity: string
|
||||
signerAddress: string
|
||||
timestamp: number
|
||||
nonce: string
|
||||
}): string {
|
||||
return [
|
||||
'Pezkuwi P2P Confirm Payment',
|
||||
`trade:${p.tradeId}`,
|
||||
`identity:${p.sellerIdentity}`,
|
||||
`signer:${p.signerAddress}`,
|
||||
`timestamp:${p.timestamp}`,
|
||||
`nonce:${p.nonce}`,
|
||||
].join('\n')
|
||||
}
|
||||
|
||||
/**
|
||||
* Canonical challenge for locking one's OWN balance into escrow (offer create).
|
||||
* MUST be byte-identical to the client (shared/lib/p2p-fiat.ts).
|
||||
*/
|
||||
export function buildLockEscrowChallenge(p: {
|
||||
identityId: string
|
||||
token: string
|
||||
amount: number
|
||||
signerAddress: string
|
||||
timestamp: number
|
||||
nonce: string
|
||||
}): string {
|
||||
return [
|
||||
'Pezkuwi P2P Lock Escrow',
|
||||
`identity:${p.identityId}`,
|
||||
`token:${p.token}`,
|
||||
`amount:${p.amount}`,
|
||||
`signer:${p.signerAddress}`,
|
||||
`timestamp:${p.timestamp}`,
|
||||
`nonce:${p.nonce}`,
|
||||
].join('\n')
|
||||
}
|
||||
|
||||
/**
|
||||
* Canonical challenge string for a privileged admin action (dispute claim /
|
||||
* resolve). MUST be byte-identical to the client (DisputeResolutionPanel).
|
||||
|
||||
Reference in New Issue
Block a user