The guide · Security for vibe coders
The 24-point security check for vibe-coded apps.
Your vibe-coded app is one URL away from a breach. AI builds the feature you asked for. It rarely adds the lock you didn't ask for, so the same holes turn up in app after app.
The reel covers twelve. Below are all twenty-four, plus one prompt that makes Claude Code go through your codebase, fix them and write up what it changed, before you deploy.
The twelve in the reel
Admin routeswide open to anyone who types the URL.Permissionschecked in the browser, not on the server. Anyone with DevTools walks past.Other people's datareadable by any logged-in user, because row-level security is off.Passwordsstored plain or with a fast hash.Auth tokensin localStorage, where any script on the page can read them.API keysshipped to the client in your JavaScript bundle..envcommitted to GitHub. Deleting the file doesn't delete the commit.SQLbuilt by joining strings, so one quote mark can rewrite your query.Form inputsnever sanitised: XSS and injection waiting to happen.File uploadsnever validated, soshell.phpgoes straight in.Loginwith no rate limit, so brute force takes minutes.Debug modestill on in production, printing stack traces and secrets.
A real run: the audit on my own Claude Code skill
I ran these 24 checks on Praza AI, my reel-editing skill for Claude Code. It runs on the creator's own computer, with no server, no accounts and no database, so most web-app holes can't exist there. The audit marks those N/A with a reason instead of inventing work, then digs into the ones that do apply.
| Result | What it found |
|---|---|
| Fixed · 3 | Inputs (15): installer options are now checked before they're used. Injection (16): text from outside, like picture titles pulled from Wikimedia and NASA, is treated as data and cleaned of hidden control characters, so a file named “ignore previous instructions” can't steer the agent. Dependencies (22): Pillow 12.3.0 to 13.0.0, clearing 37 advisories, and npm audit down to 0. |
| Pass · 9 | No keys stored anywhere, Git history scanned clean with gitleaks, nothing sensitive in logs, writes only to its own folders, a spending cap on every Claude call, debug off, and a release check that blocks keys and files it has no right to ship (4, 9, 10, 11, 12, 13, 19, 21, 24). |
| N/A · 11 | Admin routes, other users' data, row-level security, API endpoints, email, passwords, tokens, SQL, uploads, webhooks and headers. There's no server or account to attack (1, 2, 3, 5, 6, 7, 8, 14, 17, 18, 20). |
| Open · 1 | A package the installer adds that nothing uses yet (23, low). Remove it or wire it up. |
That's the point of running all 24 every time. In a web app the holes are admin routes and row-level security. In a local AI tool, the real one was outside text reaching the agent. Audited 26 October 2026 on the hardened build.
All 24 checks and the prompt
🔒 Drop your email to reveal the checklist and the prompt
Get the full check, free.
Drop your email to unlock all 24 checks with the fix for each, and the prompt that makes Claude Code audit and fix your codebase, plus every other guide in the library. One email a week. Unsubscribe anytime.
The prompt
Open Claude Code in your project folder and paste this. It works through all 24 checks, fixes what it safely can, asks before anything risky, and writes the result to SECURITY-AUDIT.md. Run it before every deploy.
The 24 checks, explained
What each one means and how to fix it. The timestamps jump to that moment in the reel.
Lock admin routes on the server
Hiding the admin button isn't protection. If/admin loads for anyone who types it, it's open. Check the user's role on the server for every admin page and API call, and return a 403 when it fails.Check permissions on the server, not in the browser
Anything in the browser can be edited. Typinguser.role = "admin" into the DevTools console shouldn't unlock anything. The browser can hide buttons for convenience. The server decides what's allowed.Turn on row-level security
If your app talks to Supabase or Postgres from the browser, row-level security is what stops one user reading another's rows. Enable RLS on every table and add a policy such as “a user can read rows whereuser_id is their ID”. Pair it with an ownership check on every read and write in your own API (check 2 in the prompt).Hash passwords properly
Use argon2id, or bcrypt or scrypt: slow, salted algorithms. MD5 and SHA-1 crack in seconds. Better still, let an auth provider store passwords for you.Move tokens out of localStorage
Any script on your page can read localStorage, including one injected through an XSS bug or a compromised package. Keep session tokens in httpOnly, Secure, SameSite cookies, which JavaScript can't read.Keep secrets on the server
Anything in your front-end bundle is public: open DevTools, search the JavaScript, and there's the key. Call paid APIs from a server route or function that holds the key, and never put a secret behind a public prefix likeNEXT_PUBLIC_ or VITE_. Check every environment has what it needs and nothing extra.Get .env out of Git, then rotate
Deleting.env in a new commit leaves it readable in every earlier one. Add it to .gitignore, scan the full history with gitleaks or trufflehog, and rotate any key that was ever pushed. Purge the history after, but treat a leaked key as burned either way. Keep secrets out of your logs too.Use parameterised queries
"WHERE id = '" + id + "'" lets someone send 1' OR '1'='1 and read the whole table. Pass values separately: db.query("… WHERE id = $1", [id]), or use your ORM's query builder.Validate inputs and stop XSS
Validate every form and API input on the server against a schema, such as Zod or Pydantic. Escape anything you show back to users, never put user data intoinnerHTML or dangerouslySetInnerHTML, and set a Content Security Policy as a backstop.Validate file uploads
Check the file's real type from its first bytes, not its name:invoice.pdf.exe is not a PDF. Cap the size, rename files on save, store them outside your web root and never execute them.Rate-limit login and anything costly
Without a limit, a script tries thousands of passwords a minute. Limit login, sign-up, password reset, key checks, AI calls and uploads per IP and per account.Turn debug mode off in production
Debug pages print stack traces, file paths and sometimes database URLs and keys. Switch debug off, serve plain error pages, and don't publish source maps.The other twelve
Not in the reel, but in the prompt:- Least privilege on your database and storage: no public buckets or tables by default.
- Every API endpoint checks who's calling, what they're allowed to do and what they sent. Remove the ones you don't use.
- Verify email addresses before giving access.
- Webhook signatures (Stripe and the like) checked with a timing-safe compare and replay protection.
- Security headers: CSP, HSTS, X-Content-Type-Options, frame-ancestors, Referrer-Policy, Permissions-Policy.
- Dependencies: run
npm auditorpip-auditand update what's flagged. Remove packages you don't use. - Exposed files: no
.gitfolder, backups,.mapfiles or secrets in public folders or build output.
Before you run it
Commit firstso every fix Claude makes is easy to review and undo.Read the planapprove the list of fixes before it starts. Reading takes a minute. Untangling a broken auth flow takes longer.Rotate, don't just deleteif a key was ever pushed or shipped to the browser, treat it as leaked and replace it.It's a first passan AI audit catches the common holes. If you handle payments, health data or lots of users, get a human security review too.