Repository: cocoa007/x402-nostr-relay
Scope reviewed: x402 payment verification, NIP-01 handling, event store logic, and general security posture.
I found 2 high-impact issues and 3 medium/low-risk issues.
- High 1: Contract-call payment verification accepts any fungible transfer event to relay address without enforcing the asset is sBTC.
- High 2: Replay protection in
x402.mjsuses in-memorySetand is lost on process restart, enabling tx reuse after restart.
Location: src/x402.mjs (verifyPayment contract_call branch)
The code accepts a transfer event if:
- event type looks like fungible transfer
asset_event_type === transfer- recipient is relay
PAY_TO
But it does not validate the token contract/asset identifier is actually sBTC. A transfer of another fungible token to the relay address can satisfy the current check and be treated as valid payment.
Impact: Payment bypass using non-sBTC asset.
Recommendation: Require an allowlist match on asset identifier (e.g., exact sBTC asset ID) before accepting amount.
Location: src/x402.mjs (usedTxIds Set)
Replay tracking is held in memory only. Restarting the service clears this set, allowing previously-used tx IDs to be re-submitted.
src/store.mjs already defines a used_tx_ids SQLite table and helper methods, but verifyPayment does not use persistent tracking.
Impact: Replay vulnerability after restart/failover.
Recommendation: Check/mark tx IDs via persistent store (SQLite table) in an atomic flow.
Location: src/x402.mjs (verifyPayment)
A tx is accepted when tx_status === success without explicit confirmation/finality policy.
Impact: Potential acceptance risk around short-lived reorg/finality edge cases depending on chain/API semantics.
Recommendation: Enforce minimum confirmation depth or finalized-state criteria before accepting payment.
Location: /api/events flow (reviewed via relay/payment code)
No explicit rate limit/throttling guard is visible for repeated invalid proof submissions.
Impact: Resource pressure and verification endpoint abuse.
Recommendation: Add IP/key-based rate limiting and request-size limits for payment verification path.
The parser handles amount with BigInt safely, but additional schema validation for expected fields/types would harden behavior against malformed API responses.
Recommendation: Strict schema check for tx structure before business logic.
- Relay websocket behavior (
REQ,CLOSE,EOSE) is broadly aligned with basic NIP-01 relay semantics. - Event store replaceable / parameterized replaceable handling is directionally correct.
- Ephemeral kinds are correctly not persisted.
- Enforce sBTC asset identifier checks in
verifyPayment. - Move replay protection to persistent DB (
used_tx_ids) and remove process-local-only trust. - Add confirmation/finality threshold.
- Add rate limiting on payment verification endpoint.
Prepared for bounty issue: cocoa007#1