DEV Community

Edison Flores
Edison Flores

Posted on

We open-sourced our security audit. Here's what we found (and fixed).

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:

  1. Security pentest — exploit-ready vulnerabilities
  2. Performance load test — concurrent users
  3. Functional/i18n audit — translations and UX
  4. 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:

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

The full report

Everything is documented at:

Lessons

  1. Fail-closed > fail-open. If something goes wrong, don't issue the license.
  2. Exact match > range check. === not >= for payment amounts.
  3. Validate the sender. A valid txHash doesn't mean YOUR customer paid.
  4. Dedup store is essential. Cache is for performance, dedup is for security.
  5. 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.

Top comments (0)