| Reviewer | Method | Findings | Verdict |
|---|---|---|---|
| Codex (GPT-5.5) | Direct repo read, sandboxed, 110 categorized findings across 6 categories | 8 CRIT 26 HIGH | NO-GO |
| Grok 4.3 | Curated brief, CTO production-readiness review | 5 named blockers + 5 systemic risks | GO-WITH-CONDITIONS |
| Claude (main session) | Manual line-by-line read of auth + middleware + RBAC + CSRF + encryption | 7 CRIT 10 HIGH | GO-WITH-CONDITIONS |
| Code-quality agent | Architecture + type safety + schema + React hooks | 2 CRIT 9 HIGH 12 MED | Defects-only |
| Test-coverage agent | Mapping __tests__/ against production surfaces (598 files) |
6 CRIT 9 HIGH | Defects-only |
| Deploy-infra agent | Dockerfile, fly.toml, Sentry, BullMQ, Prisma migrations, Redis | 3 CRIT 9 HIGH | Defects-only |
| Gemini 2.5-pro | — | — | BLOCKED — monthly spending cap exceeded |
| Feature-readiness agent | 11.5min run, 127 tools, crashed mid-stream — coverage folded into Codex | partial | CRASHED |
Flagged by 2+ reviewers independently. All must close before external VASP traffic. Counts of findings per category appear in the heatmap below.
app/api/v2/transfers/[id]/route.ts:61-63, 186-190 look up transfers by id + environment only. No owner / API-key / tenant check. Any transfers:read key in an environment can read ANY transfer by ID. Any transfers:write key can accept, reject, or retry someone else's transfer.
transfers:write, ignores legal holdsapp/api/v2/customers/[id]/erase/route.ts:83-93, 123 authorizes irreversible KYC/customer destruction with transfers:write. Checks "active transfers" only; never consults LegalHold, RetentionPolicy, or RetentionExecutionLock models that exist in the schema.
app/api/system/trp/veriscope/incoming/route.ts persists peer-supplied TA-account, callback, and identity fields into KYC templates (lines 391-421) without verifying sender. The stock Veriscope protocol is unsigned by design — but the route also mutates persistent state. Skipped by both middleware.ts:94-107 and csrf.ts:151-153. Zero integration tests. Only ingress controls are 120/min IP rate limit + Zod schema validation.
app/lib/actions/auth.ts:127-135 signs the 2FA JWT with NEXTAUTH_SECRET. app/lib/actions/auth.ts:181-185 verifies with TWO_FA_JWT_SECRET || NEXTAUTH_SECRET. In production where TWO_FA_JWT_SECRET is required and different from NEXTAUTH_SECRET, sign uses key A, verify expects key B — tokens fail. Either 2FA is broken or silently bypassed. Note: this contradicts what I read in auth/config.ts:48-92 which uses a consistent getTwoFaJwtSecret(). The two files differ — run the integration test.
app/lib/jobs/transfer-queue.ts:211-213 explicitly states compliance checks (sanctions, blockchain screening) do NOT run before provider cascade. They run AFTER both IVMS payloads are exchanged. Sensitive originator IVMS101 data (names, addresses) is transmitted to counterparty VASPs before sanctions screening. If a transfer would have been sanctions-blocked, the disclosure already happened.
csrf.ts:197-202 checks next-auth.session-token (v4 names). Auth.js v5 defaults to authjs.session-token. If v5 uses authjs.* names, any browser-session + arbitrary x-api-key header request bypasses CSRF entirely (skip logic at csrf.ts:147-149 triggers because hasSessionCookie returns false). Cross-origin attacker against a logged-in admin → state-changing action runs as victim. Verify in dev session via document.cookie.
withRBAC fails OPEN on unmapped routesmiddleware.ts:184-193 gates /api/v1/admin/* and /api/v1/users/* — these paths do not exist. (b) rbac.ts:91-95 returns {allowed: true} when path missing from ROUTE_PERMISSIONS (17 routes vs 79 admin sections + 17 API routes). (c) Server components like app/admin/users/page.tsx:18-35 call only requireAuth() — no permission check. A viewer user can hit every admin page and read every user/transfer/customer record.
_prisma_migrationsstart.sh:27-40: each psql -c "SELECT pg_advisory_lock(42)" is a separate session — the session-level lock is released the instant psql exits. npx prisma migrate deploy runs with no lock held. Comment at L26 claims the opposite. On Fly scale-up, two machines can race the migration.
auth/config.ts:297-306 and redis/client.ts:14-30 use .catch(() => null) on every rate-limit call. Redis down or slow → null → treated as allowed. Brute-force throttling vanishes during the exact outage windows when credential-stuffing campaigns happen.
withRBAC fail-closed default + admin page authorizationapp/api/**/route.ts × {happy, no-auth, wrong-scope, malformed, rate-limited}middleware.ts branch testscoverage.all: true, coverage.include: ['app/**/*.ts']withApiMiddleware composition-order testweb and workerbeforeSend PII scrubber + release tagging + source-maps mandatoryprod-deps stagePII_ENCRYPTION_KEY from CONFIG_ENCRYPTION_KEY.env.example var or wire it throughisUnhosted flag (not client-supplied)AUDIT_SIGNING_KEY to actual AuditLog hash-chain columnstransfer-workflow.ts (1862 LOC) into per-workflow modulesveriscope/incoming/route.ts (1334 LOC) into thin route + service layer/api/v2/transfers POST to createOutgoing (de-dup with [id]/route.ts)as any on session/roledb:push)react-hooks/exhaustive-deps disableFEATURES_STATUS.md until the claims can be proven by running tests or schema inspection.
ATRSA is, on net, an unusually clean hand-built MVP for a regulated product. Zero TODO: / FIXME: in app/, structured pino logging everywhere, strong env validation, AES-GCM at rest, double-submit CSRF, single-use 2FA JWTs, audit logging, header spoofing prevention, PII scrubbers. The defects are not laziness — they are the kind of architectural drift that happens when one person writes 125K LOC over months without a second pair of eyes. The blockers above are mostly one engineer-day each to fix individually; the test coverage gap is the only thing that takes real calendar time.
.github/workflows/test.yml exists with a 60% coverage gate. Deploy-infra agent reports "no CI workflows in repo." Run ls -la .github/workflows/ to resolve.app/lib/actions/auth.ts. Direct read of auth/config.ts showed consistent secret usage. The two files differ — run an integration test against the live 2FA flow.document.cookie in devtools to confirm.