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(notMath.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
-
perPurchaseCapUsddefaults tomin($50, limit)not full limit -
INTERNAL_SECRETfails 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.
Top comments (0)