13 Vibe Coding Security Mistakes That Will Get You Hacked in 2026
AI-generated code carries 2.74× more security vulnerabilities than handwritten code. The 13 specific mistakes that catch indie builders — leaked secrets, hallucinated dependencies, broken access controls, and the guardrails that actually work.

AI-generated code carries roughly 2.74× more security vulnerabilities than handwritten code, and most of those vulnerabilities get shipped because vibe coders trust the model's output without reading it. This is the deep dive on the 13 specific security mistakes that catch indie builders in 2026 — what each looks like, why it happens, and the guardrails that actually work without slowing you down.
Vibe coding is fast. That speed is the trap. Speed without review puts unaudited code into production at a velocity that bypasses every traditional security process.
TL;DR — the 13 mistakes
- Pasting .env files (or any secrets) into the model context.
- Accepting AI-suggested dependencies without verification.
- Letting the model "fix" auth without reviewing the change.
- Trusting AI-written input validation.
- Hardcoded credentials in example code that get committed.
- SQL injection through string-concatenated queries.
- Permissive CORS defaults the model picked.
- No rate limiting on AI-callable endpoints.
- Server-side secrets exposed in client-side code.
- Misconfigured Supabase/database row-level security.
- Webhook endpoints without signature verification.
- Untrusted file upload paths.
- Logging sensitive data in error messages.
Each one below in detail with the specific fix.
The state of AI-generated code security in 2026
Three numbers worth anchoring on:
- 2.74× — the security vulnerability rate of AI-generated vs handwritten code (research from METR, GitClear, and CodeRabbit).
- ~30% — the share of AI-generated code that introduces at least one new dependency, often without explicit user approval.
- ~17% — the share of AI-suggested package names that don't exist or that point to typo-squatted malicious packages, per security research.
This isn't a reason to stop vibe coding. It's a reason to *vibe code with guardrails*. The companies winning are doing both — using AI to accelerate development AND running security review at every commit.

Mistake #1 — Pasting secrets into the model context
What it looks like: "Here's my .env file, can you debug why my Supabase connection is failing?"
Why it's catastrophic: model context windows are not private. Pasted secrets may end up in:
- Training data feedback loops (depending on the provider's policies).
- API logs.
- Analytics exports the AI vendor produces for itself.
- The shared chat history if you collaborate with a teammate.
Your pasted SUPABASE_SERVICE_ROLE_KEY is now potentially in 3-5 places you can't audit.
The fix: never paste raw secrets. When you need to show shape, use placeholder values. When you need to debug an env issue, redact the value:
``` # what NOT to paste SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiI...
# what to paste SUPABASE_SERVICE_ROLE_KEY=<redacted_64_char_jwt> ```
Treat the chat like a public Slack channel. If you wouldn't post it to #general, don't paste it to the model.
If you've already pasted secrets, rotate them. All of them. Today.
Mistake #2 — Accepting AI-suggested dependencies without verification
What it looks like: Cursor or Claude Code says "I'll add some-helper-lib to handle that," and you accept the diff without checking the package.
Why it's catastrophic: AI models confidently suggest packages that:
- Don't exist (hallucinated names) — and may be squatted by malicious actors specifically because the model's hallucination patterns are predictable.
- Are deprecated and unmaintained.
- Have known CVEs the model isn't aware of.
- Are typo-squats of legitimate packages (
requetsinstead ofrequests).
This is the supply chain attack vector specific to vibe coding.
The fix: the magic phrase, in every session opener:
Do not install any new npm packages without asking first. If you propose a package, include the GitHub URL, last commit date, and weekly downloads. Prefer Node built-ins.
When the model does propose a package, take 30 seconds:
- Open the npm page. Check weekly downloads (anything under 10,000/week needs scrutiny).
- Open the GitHub repo. Last commit within 6 months? Maintained.
- Spot-check the install count vs the GitHub stars. Mismatches signal squatting.
See How to stop AI hallucinations when vibe coding for the deeper hallucination patterns.
Mistake #3 — Letting the model "fix" auth without reviewing the change
What it looks like: Auth is broken. You ask the agent to fix it. The agent rewrites the auth flow. It "works." You ship.
Why it's catastrophic: auth code is the most security-sensitive code in your app. AI-rewritten auth often introduces subtle bugs — wrong session handling, missing CSRF protection, password hashing with wrong parameters, JWT validation that accepts unsigned tokens.
The fix: auth changes get a manual line-by-line review. Every time. No exceptions. If the change is more than 30 lines, read it twice. If you don't understand a function the agent used, look it up before accepting.
The rule: the agent can write auth, but it cannot ship auth.
Mistake #4 — Trusting AI-written input validation
What it looks like: You ask for a form handler. The agent writes the route. Inputs go to the database.
Why it's catastrophic: the agent's default validation is "does it look like the type I expect?" — not "is this safe to put in a SQL query, render as HTML, or pass to a shell command?"
Specific footguns:
- No length limits — a 10MB string in a name field crashes your DB.
- No character whitelisting —
'; DROP TABLE users--works on naive query construction. - No HTML escaping — stored XSS through "comment" fields.
- No file-type validation on uploads.
The fix: write validation explicitly. For each input field, specify max length, allowed characters, and the parser type. Use a library like zod or valibot and ask the agent to validate against an explicit schema.
Add a Zod schema for this form.nameis 1-100 chars,messageis 1-10000 chars. Reject anything that doesn't match before touching the database.
Mistake #5 — Hardcoded credentials in example code that get committed
What it looks like: the agent writes example code with placeholder credentials like API_KEY = "sk-proj-abc123". You replace them with real ones to test. You commit. The real key is now in git history.
Why it's catastrophic: git history is forever. Even if you remove the key in a later commit, anyone who clones the repo can find it via git log.
The fix: never put real credentials in code, even temporarily. Always use .env and process.env.API_KEY. Add a pre-commit hook that scans for credential patterns:
`` # pre-commit hook (e.g., via husky) npx secretlint "**/*" ``
If you've already committed a secret, rotate the key AND scrub the git history (git filter-repo or BFG).

Mistake #6 — SQL injection through string-concatenated queries
What it looks like: the agent writes db.query(\SELECT * FROM users WHERE email = '${email}'\).
Why it's catastrophic: classic SQL injection. The user submits '; DROP TABLE users-- and your database is gone.
The fix: parameterized queries, always. Most modern ORMs (Prisma, Drizzle, Supabase client) do this by default — but raw SQL queries that the agent occasionally produces don't.
Search your codebase for template-literal SQL after every session:
`` grep -rn "\SELECT\|\INSERT\|\UPDATE\|\DELETE" src/ ``
Any hits get rewritten as parameterized queries.
Mistake #7 — Permissive CORS defaults the model picked
What it looks like: the agent sets up an API route. To get past CORS errors during development, it adds Access-Control-Allow-Origin: *. You ship.
Why it's catastrophic: * means any website can call your API on behalf of an authenticated user. Combined with Access-Control-Allow-Credentials: true, this is a CSRF attack waiting to happen.
The fix: explicit origin allowlist:
`` const allowedOrigins = [ 'https://yourapp.com', 'https://www.yourapp.com', 'http://localhost:3000', // dev only ] ``
Audit every CORS-related config the agent produces. Default-deny, allowlist-explicit.
Mistake #8 — No rate limiting on AI-callable endpoints
What it looks like: an API route that calls OpenAI/Anthropic per request. No rate limit. Goes live.
Why it's catastrophic: one user with a script can drain your monthly API budget in an hour. We've seen builders wake up to $2,000 OpenAI bills from a single weekend after a public post.
The fix: Upstash Redis + a 5-line middleware:
`` // pseudo-code const rate = await ratelimit.limit(ip:${ip}) if (!rate.success) return new Response('rate limited', { status: 429 }) ``
Set conservative limits per IP (10 requests/minute) AND per authenticated user (100/day). Both. Always.
Mistake #9 — Server-side secrets exposed in client-side code
What it looks like: the agent imports an API key into a React component because that's where the API call happens.
Why it's catastrophic: anything imported into a client component ships to the user's browser. Your OPENAI_API_KEY is now in the page source.
The fix: API calls that need secrets go through API routes. The client makes a request to *your* server; your server makes the request to OpenAI/Anthropic.
In Next.js, the NEXT_PUBLIC_ prefix is the line — anything with that prefix ships to the client; anything else stays on the server. Never put real secrets behind NEXT_PUBLIC_.
Mistake #10 — Misconfigured Supabase RLS
What it looks like: Supabase project. Tables created. RLS not enabled, or enabled with permissive policies.
Why it's catastrophic: with the anon key (which ships to clients), an attacker can query any table directly via the Supabase JS client. We've seen real production apps where select * from users over the public Supabase URL returned every user's email and hashed password.
The fix: RLS on every table on day one. Default-deny policies. Explicit allow rules for each operation:
``` -- enable RLS alter table habits enable row level security;
-- explicit policy: users can only read their own habits create policy "users read own habits" on habits for select using (auth.uid() = user_id);
-- and another for insert, etc. ```
Test from an unauthenticated client to confirm. Test as user A trying to access user B's data to confirm. RLS bugs only show up under adversarial use.
Mistake #11 — Webhook endpoints without signature verification
What it looks like: a Stripe webhook handler that just reads the JSON body and acts on it.
Why it's catastrophic: webhook URLs are typically public. Anyone who finds yours can fake a "payment succeeded" event and grant themselves access to a paid product without paying.
The fix: verify the signature on every webhook:
`` const event = stripe.webhooks.constructEvent( rawBody, signature, process.env.STRIPE_WEBHOOK_SECRET ) ``
Same pattern for any provider — Resend, Clerk, Polar, GitHub. They all sign their webhooks. Verify, every time.
Mistake #12 — Untrusted file upload paths
What it looks like: the agent writes a file upload handler that saves to ./uploads/${filename}.
Why it's catastrophic: filename can be ../../../etc/passwd. Path traversal lets attackers write or read files outside the intended directory.
The fix: never use user-provided filenames as paths. Generate a random ID, store the original filename as metadata:
`` const id = crypto.randomUUID() const path = ./uploads/${id}.${getValidatedExtension(filename)} ``
And validate the extension against a whitelist. Don't trust the MIME type from the request.
Mistake #13 — Logging sensitive data in error messages
What it looks like: console.error("Failed for user", user) — and user includes the password hash, the API key, or the session token.
Why it's catastrophic: logs end up in:
- Vercel/Netlify build logs (often retained for months)
- Sentry / Datadog / Logtail
- Customer support tickets where you copy-paste error messages
- Public bug reports if you ever share an error trace
Each of these is a place a secret can leak.
The fix: explicit redaction in error paths:
``` function safeUser(u) { const { password, apiKey, sessionToken, ...rest } = u return rest }
console.error("Failed for user", safeUser(user)) ```
Better: a structured logger (Pino, Winston) with explicit redaction config.

The guardrails that actually work
You don't need to memorize all 13 mistakes. You need a process that catches them.
1. The session opener rule
Every Claude Code or Cursor session starts with:
Do not install npm packages without asking. Do not write SQL with template literals. Do not change auth code without flagging it for review. Use environment variables for all secrets. Validate all user inputs against explicit schemas.
This single paste blocks 60% of the common mistakes.
2. Pre-commit secret scanning
Hook secretlint or gitleaks into your pre-commit:
`` # .husky/pre-commit npx gitleaks protect --staged ``
You will catch the "I accidentally committed an API key" mistake before it lands.
3. Automated dependency audits
GitHub Dependabot. Snyk. Socket.dev. Pick one. Turn it on. Treat the alerts as work, not noise.
4. RLS policies before queries
In any Supabase or RLS-capable database, write the access policy BEFORE you write the query that uses it. The discipline of "policy first" catches the "oh I forgot to lock that down" mistake.
5. Manual review on auth and money paths
Every PR that touches auth, sessions, payments, or user permissions gets a human review. No exceptions. The agent can write it; the human ships it.
6. Spend caps everywhere
API keys (OpenAI, Anthropic, Stripe) have spend limits set. Cloud providers (Vercel, Supabase, Cloudflare) have spend alerts. The cost of a misconfigured loop is bounded.
Common questions
Is vibe coding fundamentally insecure?
No — it's faster, which means insecure code can ship faster if you don't have process. With process (session opener, pre-commit scans, manual review on sensitive paths), vibe-coded projects can be as secure as handwritten ones. Often more secure, because the agent is more consistent than a tired human.
What's the single most important guardrail?
Manual review on auth and money paths. Everything else is helpful; this one is non-negotiable.
Do these mistakes show up in Cursor or Claude Code more often?
Both. The mistakes are model-pattern-driven, not editor-driven. Cursor and Claude Code (and Copilot, and Windsurf) all produce the same classes of vulnerability at similar rates.
How do I catch hallucinated dependencies?
Lock down the session opener. Run pnpm audit after every dependency change. Use Socket.dev's npm audit which specifically checks for typo-squatted packages and known supply chain compromises.
What about for production-scale codebases?
The same guardrails apply, plus: SAST tools (Semgrep, CodeQL) on every commit, SCA tools (Snyk) on every PR, and a documented "code provenance" rule that says any AI-generated code lives in a clearly-marked branch until reviewed.
Should I run security tests on AI-generated code?
Yes — and you can use AI to do it. "Review this code for security vulnerabilities" is one of the highest-value prompts in vibe coding. The model is often better at *finding* security issues than *avoiding* them in the first place.
Are some AI editors more secure than others?
Marginally. The mistakes are mostly model-pattern-driven, not editor-driven. The bigger differentiator is whether the editor encourages plan-first review (Claude Code, Cursor's composer) vs accept-by-default autocomplete (some Copilot configurations).
What about for indie builders without a security team?
The session opener + pre-commit scans + manual review on auth/money is enough. You don't need a security team. You need 30 minutes of setup once and a habit of reviewing diffs on sensitive paths.
The bottom line
Vibe coding without guardrails is faster than handwriting code AND insecure. Vibe coding with guardrails is faster than handwriting code AND as secure or more secure. Pick the second one.
The 13 mistakes above are the specific patterns that catch indie builders. The guardrails are short, cheap, and one-time setup. Set them once, ship faster, sleep better.
For the broader workflow context: What is vibe coding, The vibe coder's stack 2026, and How to stop AI hallucinations.
For weekly AI-tooling coverage including security incidents in the wild: humanai.news. To deploy a personal AI agent in 60 seconds with managed security defaults: RapidClaw.