Guide
Security checklist before launching a Cursor or AI-built app
AI coding tools like Cursor, Claude Code, Windsurf and Copilot are very good at making features work. They are less reliable at making them safe, mostly because "safe" is about what the app refuses to do, and nobody tests for that while building. This checklist covers the problems we see most often in AI-built apps. Most items take a few minutes each on your own app.
Secrets
- No secret keys in the browser. Open your live site, press Ctrl+Shift+F in the DevTools Sources tab and search for
sk-,sk_live_,service_roleandsecret. Full method in our bundle check guide. .envis in.gitignoreand was never committed. Rungit log --all --oneline -- .env. If it prints anything, the file is in your history. Rotate every key in it.- Scan your repo history with gitleaks:
gitleaks git .from your project folder. - No secrets in logs. Search your code for
console.logcalls that print request headers, tokens or full user objects. - Hidden files aren't served. Visit
/.envand/.git/configon your own domain. Both should return 404.
Authentication
- Every API route checks the session on the server. Hiding a button in the UI is not access control. Ask your AI tool: "List every API route and server action in this project and show how each one verifies the user." Any route that skips the check should be deliberate, like a public health check.
- Middleware isn't your only check. In Next.js, checking auth in middleware alone has caused real bypasses. Verify the user again inside the route or server action that touches data.
- Password reset and magic links expire and can only be used once. If you use a hosted auth provider (Supabase Auth, Clerk, Auth0, Firebase), this is handled for you. If your AI tool wrote its own auth, consider switching.
- Email verification is on if anything in your app depends on knowing the email is real.
Authorization
This is where AI-built apps fail most. Authentication asks who you are. Authorization asks what you're allowed to touch.
- Change the ID test. Log in as test user A, open a page like
/invoices/123, then log in as user B and open the same URL. B should get a 404 or 403. Do the same for API calls in the Network tab by editing the ID in the request (right click, Copy as fetch, change the ID, paste in the console). - Users can't set their own role, plan or credits. If a profile update request includes the whole user object, try adding
"role": "admin"to your own request. If it sticks, the server is trusting fields it should ignore. - Database rules are right. On Supabase, every table needs RLS with ownership checks. Use our Supabase RLS checklist. On Firebase, open the Rules tab and make sure nothing says
allow read, write: if true;. - Admin pages check admin status on the server, not just by hiding a link.
Payments
- Paid status comes from your server, set by a payment webhook, never from a query string like
?paid=trueor a value in local storage. - Webhooks verify signatures. For Stripe, use
stripe.webhooks.constructEventwith the raw request body and your webhook signing secret. Without it, anyone can POST a fake "payment succeeded" event. - Prices are set on the server. The client should send a product or price ID, never an amount.
Abuse and cost
- Rate limits on login, sign-up, password reset, contact forms, anything that sends email or SMS, and every AI endpoint.
- Spend limits set at each paid provider: AI models, email, SMS, hosting. See protecting AI features from surprise bills.
- Bot protection such as Cloudflare Turnstile or hCaptcha on public forms and sign-up if you offer free credits.
- File uploads have a size limit and an allow list of file types, and are stored in a private bucket unless they're meant to be public.
Browser-side protections
- HTTPS everywhere, with HTTP redirecting to HTTPS. Most hosts do this for you.
- Security headers. Check your site's response headers in DevTools (Network tab, click the document request). At minimum:
Strict-Transport-Security,X-Content-Type-Options: nosniff, aContent-Security-Policy(even a basic one), andframe-ancestorsin the CSP orX-Frame-Optionsto stop clickjacking. - CORS isn't wide open. If your API returns
Access-Control-Allow-Origin: *together with credentials, or reflects any origin it's sent, other websites may be able to make requests as your logged-in users. Allow only your own domains. - User content is escaped. Search for
dangerouslySetInnerHTML,innerHTMLandv-html. Each one that renders user input or AI output needs sanitizing with a library like DOMPurify. - No public source maps unless you're fine with your source code being readable.
Dependencies
- Run
npm audit(orpnpm audit) and update anything with a high or critical issue that affects code you actually ship. - Every package is real and the one you meant. AI tools sometimes suggest package names that don't exist, and attackers register those names. Check unfamiliar packages on npmjs.com: look at weekly downloads, the repo link and the publish date.
- Lockfile is committed so production installs the same versions you tested.
Errors and debug settings
- Production errors don't show stack traces, SQL errors or file paths to users.
- Debug and test routes are gone. Search for routes named
test,debug,seed,resetoradminthat your AI tool created while you were building. - Default and demo accounts are deleted, especially any with a password like
admin123.
Email and domain
- SPF, DKIM and DMARC records are set for your domain, so nobody can easily send email that looks like it came from you. Your email provider's setup page lists the exact records.
- Old preview deployments are not exposing older, less secure versions of your app with production data.
If your app has AI agents or tools
- Agent endpoints and MCP servers require auth.
- Tools run with the user's permissions, not admin rights.
- Risky actions (sending email, spending money, deleting data) need a confirmation step.
Our prompt injection guide explains why these matter.
After launch
Security isn't finished at launch. Every new feature your AI tool writes can bring back an old problem. Rerun the secrets check and the change-the-ID test after big changes, keep spend alerts on, and keep a short list of where your keys live so you can rotate them fast if one leaks.
FAQ
What is the most common security problem in AI-built apps?
Missing authorization. The app checks that a user is logged in but not that the record belongs to them, so changing an ID in a URL or request shows someone else's data.
Is hiding a button enough to protect an admin feature?
No. Anyone can call your API directly. Every route that reads or changes data needs to check the user and their permissions on the server.