Skip to content

[SEC-14] Security audit: x402 anti-replay never uses PostgreSQL (useDatabase always false) (#245)#299

Open
GEEKYFOCUS wants to merge 1 commit into
ericmt-98:mainfrom
GEEKYFOCUS:docs/sec-14-x402-replay-report
Open

[SEC-14] Security audit: x402 anti-replay never uses PostgreSQL (useDatabase always false) (#245)#299
GEEKYFOCUS wants to merge 1 commit into
ericmt-98:mainfrom
GEEKYFOCUS:docs/sec-14-x402-replay-report

Conversation

@GEEKYFOCUS

Copy link
Copy Markdown

Summary

Security audit report for issue #245 ([SEC-14] Anti-replay de x402 nunca usa la DB).

Finding: CONFIRMED (Medium severity)

The x402 anti-replay mechanism in apps/api/src/middleware/x402.ts has two code paths — PostgreSQL (isPaymentUsed / markPaymentUsed) and an in-memory Set (usedTxHashes) — selected by a useDatabase flag. Static analysis confirms:

  • useDatabase is declared false at line 18 and never reassigned anywhere in the repository.
  • ensureX402Initialized() successfully calls initX402Tables() but does not set useDatabase = true.
  • Replay protection therefore always runs in-process memory, even when PostgreSQL is configured (DATABASE_URL in docker-compose.yml) and x402_payments tables exist.
  • The same valid XDR is blocked on a second request within the same process, but is re-accepted after an API restart and can be consumed once per replica in horizontal deployments.

This report is audit-only — no application or backend code changes, per issue requirements.

Report location

docs/security-reports/SEC-14-x402-replay-sin-db.md

Key answers (from issue template)

Question Answer
Does the used hash survive a process restart? No — in-memory Set is lost on restart
Is useDatabase ever activated? No — zero reassignments in entire repo
Can the same payment pass on two instances? Yes — each replica has an isolated Set

Reproducible on testnet

Yes — documented reproduction steps include same-process replay block, post-restart replay acceptance, and multi-instance bypass. Impact is amplified when combined with SEC-13 (XDR accepted without on-chain settlement).

Suggested fix (documented only, not implemented)

Set useDatabase = true after successful initX402Tables(), add production fail-closed behavior, env flag for explicit store selection, and regression tests for DB-backed replay.

Test plan

  • Static code review of apps/api/src/middleware/x402.ts and apps/api/src/db/x402.ts
  • grep confirms useDatabase has a single assignment (false) and no reassignments
  • Verified docker-compose.yml provides DATABASE_URL but middleware ignores DB path
  • Reviewed existing x402.test.ts — no replay or DB persistence coverage
  • (Optional, for reviewers) Run E2E steps in report with docker compose up to confirm post-restart replay

Closes #245 (report delivery)

Confirm useDatabase is never enabled; replay protection stays in-memory
only, allowing XDR reuse after restarts and across API replicas.

Co-authored-by: Cursor <cursoragent@cursor.com>
@drips-wave

drips-wave Bot commented Jun 30, 2026

Copy link
Copy Markdown

@GEEKYFOCUS Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[SEC-14] Anti-replay de x402 nunca usa la DB (useDatabase siempre false)

1 participant