DEV Community

We open-sourced our security audit

We launched MarketNow 2 weeks ago. Then we got pentested. This is the honest report of what we found, what we fixed, and what's still pending.

Why we did it

MarketNow handles real money - USDC payments on Base for AI agent skills. Before we promote it broadly, we wanted to know: is it actually secure?

We ran 4 parallel audits:

  • Security pentest - exploit-ready vulnerabilities
  • Performance load test - concurrent users
  • Functional/i18n audit - translations and UX
  • SEO audit - discoverability

CRITICAL findings (4) - all fixed

C1: Mandate spend failed silently

What: When an agent bought a skill, we called recordMandateSpend() to debit the mandate. But the call didn't include the internal secret, so it was rejected with 403. The license was issued anyway.

Impact: An agent with a $10 mandate could buy 100 skills at $5 each = $500 spent, $10 cap never enforced.

Fix: Pass _internal: true, _secret: process.env.MANDATES_INTERNAL_SECRET. Throw on failure - fail-closed: no license if spend fails.

C2: txHash replay

What: Same USDC txHash could be used to issue unlimited licenses.

Impact: Pay $1 once, get every $1 skill for free.

Fix: Dedup store via GitHub _data/used_txs/. Check if txHash was already used before issuing license.

C3: Amount mismatch

What: We checked received >= expected instead of received === expected.

Impact: Pay $50 once, redeem every skill priced ≤ $50.

Fix: Exact match: if (value !== BigInt(expectedAmountRaw)).

C4: Stolen txHash redemption

What: We verified the txHash was valid and the amount was correct, but never checked WHO sent it.

Impact: Scrape Basescan for USDC transfers to our wallet, redeem them before the real buyer.

Fix: Validate from wallet matches the caller's walletAddress.

HIGH findings (5) - all fixed

# Issue Fix
H1 CORS * on all endpoints Allowlist (only marketnow.site)
H2 Mandate PII (emails) exposed in list API Redact: hasEmail: true instead of email
H3 Rate limiter per-instance (not global) Documented limitation; Upstash Redis on roadmap
H4 Stripe webhook no idempotency Check if file exists before writing
H5 Default fallback secrets Fail-fast if STRIPE_SECRET_KEY unset

MEDIUM findings (7) - all fixed

  • License keys now use crypto.randomBytes (not Math.random)
  • Webhook URL allowlist (anti-SSRF: only Slack, Discord, Telegram, Zapier)
  • Security headers on /api/* (nosniff, no-referrer, DENY)
  • CSP header on SPA routes
  • Error messages no longer leak RPC internals
  • perPurchaseCapUsd defaults to min($50, limit) not full limit
  • INTERNAL_SECRET fails closed if unset

Performance results

Test Result
200 concurrent /api/search 0% error, p95=1.09s
50 concurrent homepage 0% error, p95=853ms
Sustained 5 req/s × 30s 100% success, p95=291ms
Memory (700+ requests) 0 growth (92MB → 92MB)
Cache effectiveness 1 cold load + 700+ hits
npm vulnerabilities 0

What's still honest-pending

We don't claim everything is perfect:

  • Independent third-party audit - Cure53 / Trail of Bits deferred until we have revenue. Volunteer peer review open.
  • Rate limiter - in-memory per-instance, not global. Upstash Redis fix on roadmap.
  • On-chain escrow - USDC is irreversible. Manual dispute process for now. Smart contract escrow targeting Q1 2027.
  • SPA duplicate HTML - all routes serve same HTML. SSG migration on roadmap.

The full report

Everything is documented at:

  • Trust roadmap - 7-point public response
  • Security methodology - Sentinel L1.5/L1.6/L2
  • Source code - every fix is a commit

Lessons

  • Fail-closed > fail-open. If something goes wrong, don't issue the license.
  • Exact match > range check. === not >= for payment amounts.
  • Validate the sender. A valid txHash doesn't mean YOUR customer paid.
  • Dedup store is essential. Cache is for performance, dedup is for security.
  • Pentest before launch, not after. We got lucky - found 4 criticals before any real money flowed.

MarketNow - trust layer for agent commerce. 8,560 MCP skills, Sentinel L2 security, x402 + USDC on Base. AliceLabs LLC (Wyoming, USA). Try it.

Comments

No comments yet. Start the discussion.