You are a senior security engineer conducting a full pre-production security audit of this Next.js application.
Stack: Next.js (App Router), Supabase, Drizzle ORM, Zod.
Perform a thorough review across every layer. For each issue found, show:
1. The file/location
2. The vulnerability or weakness
3. A concrete code fix
---
## 1. AUTHENTICATION & AUTHORIZATION
- Verify Supabase Auth session validation on every protected route and API route
- Check that Row Level Security (RLS) is enabled on ALL Supabase tables — never trust client-side auth alone
- Look for missing `auth.uid()` checks in RLS policies
- Confirm server actions and API routes re-validate the session server-side (never trust cookies alone without verification)
- Check for missing or misconfigured `middleware.ts` protecting routes
- Flag any admin-only actions that lack role checks
## 2. INPUT VALIDATION & ZOD SCHEMAS
- Every API route, server action, and form handler must validate with Zod before touching the DB
- Check for schemas that are too permissive (e.g., `z.any()`, missing `.max()` on strings, no email format check)
- Ensure Zod errors return generic messages to the client — never leak schema details
- Look for places where raw `req.body` or `params` is used without Zod parsing
## 3. DRIZZLE ORM & DATABASE SECURITY
- Confirm there are NO raw SQL strings with user input — all queries must use Drizzle's parameterized builders
- Check for missing `.where()` clauses that could expose all rows
- Look for insecure direct object references: e.g., `WHERE id = userSuppliedId` without ownership check
- Ensure database credentials are only in environment variables, never hardcoded
- Check that the Supabase service role key is ONLY used server-side and never exposed to the client
## 4. ENVIRONMENT VARIABLES & SECRETS
- Audit all `NEXT_PUBLIC_` variables — nothing sensitive should be prefixed with NEXT_PUBLIC_
- Supabase `service_role` key must NEVER be NEXT_PUBLIC_
- Check `.env.example` for accidentally committed real secrets
- Verify `.env.local` and `.env` are in `.gitignore`
- Look for any API keys or secrets hardcoded in source files
## 5. API ROUTE HARDENING
- Every API route should explicitly check HTTP method (reject unexpected methods)
- Add rate limiting to auth endpoints, form submissions, and any public-facing route
- Check for missing error boundaries — unhandled errors must not leak stack traces or DB info to the client
- Ensure all responses use `NextResponse` with proper status codes
- Flag any routes that return more data than the client needs (over-fetching user records, etc.)
## 6. XSS & CONTENT SECURITY POLICY
- Look for any use of `dangerouslySetInnerHTML` — flag every instance
- Check `next.config.js` for a Content-Security-Policy header
- Ensure user-generated content is never rendered as raw HTML
- Verify that any rich text or markdown rendering is sanitized (e.g., using DOMPurify)
## 7. CSRF PROTECTION
- Confirm that state-mutating server actions and API routes are protected (Next.js server actions have CSRF built-in, but custom API routes do not)
- Check for any forms that POST to API routes without CSRF tokens
## 8. HEADERS & NEXT.CONFIG
Review `next.config.js` and ensure these security headers are set:
- `X-Frame-Options: DENY`
- `X-Content-Type-Options: nosniff`
- `Referrer-Policy: strict-origin-when-cross-origin`
- `Permissions-Policy` restricting camera, mic, geolocation
- `Strict-Transport-Security` (HSTS)
- A tight `Content-Security-Policy`
## 9. DEPENDENCY AUDIT
- Run `npm audit` and flag any high/critical severity vulnerabilities
- Check for unused or abandoned packages that should be removed
## 10. SUPABASE-SPECIFIC CHECKS
- Confirm Supabase Realtime subscriptions filter by user — no broadcast of other users' data
- Storage bucket policies: are buckets private by default? Public buckets should be intentional
- Verify email confirmation is required before granting access
- Check OAuth redirect URLs are whitelisted in the Supabase dashboard
## 11. LOGGING & ERROR HANDLING
- No `console.log` with sensitive data (tokens, user PII, passwords) in production paths
- Global error handlers must not expose internal errors to the client
- Ensure a proper error monitoring tool is in place (e.g., Sentry) with PII scrubbing enabled
## 12. PRODUCTION BUILD CHECKS
- Confirm `NODE_ENV=production` disables dev tools and verbose errors
- Check that source maps are NOT publicly exposed in production
- Verify the Supabase project has email rate limiting and brute-force protection enabled
---
After the audit, produce:
- A prioritized list of findings: CRITICAL > HIGH > MEDIUM > LOW
- For each CRITICAL and HIGH finding, provide the exact code fix
- A checklist of items to verify manually before go-live
## 13. REST API / MOBILE CLIENT SECURITY (iOS)
### Authentication & Token Handling
- Every API route consumed by the iOS app must validate the Supabase JWT on the server — never trust client-supplied user IDs in the request body or query params
- Check that Authorization header is parsed and verified server-side: `supabase.auth.getUser(token)` not `getSession()`
- Ensure tokens are short-lived and refresh logic is handled correctly
- Flag any endpoints that accept a userId in the body/params instead of extracting it from the verified JWT
### API Versioning & Deprecation
- Check that routes are versioned (e.g., /api/v1/) so the iOS app can be migrated without breaking old clients
- Ensure old app versions hitting deprecated endpoints fail gracefully with a clear error, not a 500
### Request Validation
- Mobile clients send unpredictable payloads — every endpoint must Zod-validate ALL fields including headers, body, and query params
- Check that missing or malformed Content-Type headers are rejected
- Ensure file uploads from iOS (images, documents) are validated for: file type (magic bytes, not just extension), file size limits, and stored in a private Supabase bucket — never a public one unless intentional
### Rate Limiting & Abuse Prevention
- Auth endpoints (login, signup, password reset) must have per-IP and per-user rate limiting — mobile clients are a common brute-force vector
- Check for missing rate limits on any endpoint that sends emails, SMS, or triggers expensive operations
- Ensure there is no way to enumerate users via timing differences in auth responses
### CORS Configuration
- CORS should NOT be open (`*`) on any route — mobile apps using the API directly don't need CORS, only browser clients do
- Audit `next.config.js` and any middleware for overly permissive CORS headers
- Ensure OPTIONS preflight requests are handled correctly and don't expose unexpected allowed origins
### Sensitive Data in Responses
- API responses to the iOS app must never include fields like `passwordHash`, `serviceRoleKey`, internal IDs, or other users' data
- Check for over-fetching: Drizzle queries should SELECT only the columns the iOS client actually needs — not `SELECT *`
- Ensure paginated endpoints have a max page size enforced server-side (mobile clients can't be trusted to self-limit)
### Error Messages
- API errors returned to the iOS app must be generic — no stack traces, no DB error messages, no schema hints
- Use consistent error shapes (e.g., `{ error: { code: string, message: string } }`) so the iOS app can handle them predictably
- Log the real error server-side (Sentry), return a sanitized version to the client
### Certificate & Transport Security
- Confirm the API is HTTPS-only — no HTTP fallback
- If the iOS app implements SSL pinning, document the certificate rotation process so pinning doesn't break on cert renewal
- Ensure HSTS is set so iOS clients are never downgraded to HTTP
### Idempotency
- Check that POST endpoints for critical actions (purchases, submissions, account changes) are idempotent or protected against duplicate requests — iOS clients retry on network failure
- Consider idempotency keys for any endpoint that creates or charges a record