fix: end-to-end audit remediation — money path, security, trust surfaces - #52
Merged
Merged
Conversation
Critical money-path hardening: - webhook outbox: replay re-processes events with processedAt NULL instead of swallowing them; stale-event sweep wired into escrow cron; orphaned charges verified with provider and escalated to ops - deposit overpayment capped: second deposit on a locked vault records but never double-locks; audited and paged for manual refund - attestation survival: cold-start vault address fallback, state-aware handlers (skip already-applied chain writes, mirror missing ledger rows), retryLimit 10 / 1h expiry, reconcile gains the chain leg with auto-repair - stuck-PROCESSING recovery: PaystackPort.transferStatus, sweep resolves from provider truth, admin retry gated on verified-terminal original - reversal integrity: clawback-after-success paged, success-after-reversed refused; manual-paid now attests with receipt; vault healing in cron - refundLockedVault primitive + organizer/admin event cancellation - announce validation (distinct teams, exact place coverage), judge / participant mutual exclusion at join + invite + openJudging - contracts: attester key holds ATTESTER_ROLE only; factory rotateAttester Security & ops: - CSP/HSTS/nosniff/referrer/permissions headers, X-Powered-By off - rate limiting: DB-backed RateLimitBucket (async), per-IP secondary limits, coverage on register/cancel/judge-invite routes - fail-closed payment port in production (PAYSTACK_ALLOW_SIMULATION_IN_PROD opt-in), dev upload routes hardened (traversal + authz + prod guard) - Sentry v11 (DSN-gated), /api/health, coverage gate (75/95/75 floors on escrow+payout), script env guards, Cloudflare ranges auto-refresh Product integrity: - /trust public ledger restored + /organizers/[slug] trust pages + /organizer/organization settings; 13 loading.tsx skeletons - invite-link onboarding dead-end fixed; claim-first invite acceptance with email binding; KYB precondition; newsletter confirm flow + token TTL - media upload confirm-after-PUT (no phantom assets); scoring screen no longer fabricates defaults; ConfirmMoneyAction shared by announce / milestone / mark-paid; prize-verified badge wired; wizard per-step validation; dashboard/workspace/winners empty-state and copy fixes - seed rebuilt invariant-respecting (vaults, winners, payouts, disputes, ADMIN/HIRING users); categories NOT NULL migration; docs synced to reality 449 vitest (59 files) + 14 hardhat green; typecheck/lint/build clean
3 tasks done
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Full remediation of the 2026-09-27 end-to-end audit (85+ findings). Every critical and high item is fixed, tested, and verified live.
Critical money-path fixes
processedAt NULLnow re-processes instead of returning a bareduplicate(the crash-window money-loss hole). Stale-event sweep added to escrow cron; orphaned charges are verified with Paystack and paged to ops.vaultAddressForfallback, state-aware handlers (skip already-applied writes, mirror missing ledger rows, retryableAttestationNotReadyError), retry ×10 / 1h expiry, reconcile now has the chain leg with auto-repair.transferStatusprovider-truth lookup; sweep resolves stuck payouts from Paystack state; admin retry refuses while the original transfer is unverified (double-pay path closed).refundLockedVaultprimitive + organizer (pre-funding) and admin (pre-winners, 2-admin dispute REFUND) cancellation paths.rotateAttester(grant-before-revoke) with 5 new tests.Security & ops
X-Powered-Byoff (verified live).RateLimitBucket, async, in-memory fallback) with per-IP secondary limits; register/cancel/judge-invite routes covered.PAYSTACK_ALLOW_SIMULATION_IN_PROD=true(explicit pre-go-live opt-in — set it in Vercel until real keys land)./api/health, coverage gate wired into CI (75/95/75 floors on escrow+payout, ratchet comment), operator-script prod guards, Cloudflare range auto-refresh.Product integrity
loading.tsxskeletons.ConfirmMoneyActionacross announce / milestone / mark-paid; prize-verified badge wired on listing + detail; wizard per-step validation; ghost/empty-state and stale-copy fixes.categoriesNOT NULL migration; docs synced (README, CONTRIBUTING, runbooks, BUILD_PLAN amendments, 45 stale image refs).Verification
pnpm typecheck✅ zero errors ·pnpm lint✅ clean ·pnpm build✅ zero warnings (67 routes)migrate diffempty, seed idempotent (3 runs), reconcile 0 findingsDeployment notes
PAYSTACK_ALLOW_SIMULATION_IN_PROD=truein Vercel before this deploys (current prod intentionally runs simulation; without the flag the payment port now throws in production).SENTRY_DSN+SENTRY_AUTH_TOKENto activate error reporting.