Skip to content
Vibecoding

Vibecoding · · 7 min read

Is AI code secure? Vibecoding and security

AI code is neither secure nor insecure, it is unverified. The real risk classes and a checklist.

The short answer to the title: AI code is neither secure nor insecure — it is unverified. A language model generates what is statistically most likely, not what is safest. The difference stays subtle right up until your endpoint ships to the internet with a hardcoded key or a SQL query glued together from a string. Vibecoding does not create new vulnerability classes. It only accelerates them — and that is the whole point.

Why we are even talking about this

Over the last two years the profile of the person shipping code to production has changed. Before, “I’m pushing to prod” meant someone who had been through code review, knew the difference between GET and POST, and understood why you must never trust user input. Today the same deploy is done by someone who asked an agent to “add login” and pasted the result. The code runs, the demo passes, the client is happy. The vulnerability is invisible because nothing is on fire — until someone goes looking.

This is not an anti-AI piece. I write most of my own code with an assistant and I would not go back to a bare editor. But treating generated code as ready to deploy is a category error. It is a first draft from a very fast junior — not a security audit. The stakes are real: a leaked customer database, a hijacked account, a GDPR fine. And it is not about whether you can code — it is about whether someone reviewed the code for security before it went out into the world.

Real risk classes, not theoretical ones

Let’s skip the abstractions. Here is what actually comes out of models when you ask for “working code”, ordered by how often I see it in practice:

  • Secrets in code. API keys, database passwords, tokens — dropped straight into a file “as an example” that then lands in the repository. The model has no idea your example is going to production. Worse, once pushed a secret stays in Git history forever, even after you delete it from the current file — the only real fix is rotating the key.
  • Injection — SQL and commands. Queries built by string concatenation instead of parameterization. "SELECT * FROM users WHERE id = " + userId is a classic the model keeps suggesting, because it has seen it millions of times in training data.
  • XSS in the frontend. Rendering user data without escaping,dangerouslySetInnerHTML with untrusted input, a missingContent-Security-Policy.
  • Insecure dependencies. The model suggests a package that exists but is abandoned, has a known vulnerability, or — worst case — does not exist at all, and someone malicious has registered that name on npm (typosquatting and “slopsquatting”).
  • Over-broad permissions. An S3 bucket set to public, an IAM role with*:*, CORS open to *, a database port exposed to the world. “To make it work” — and it works, for everyone.
  • Weak authentication. JWTs without signature verification, passwords hashed with MD5, no rate limiting on the login endpoint, sessions that never expire.

None of these is a new vulnerability. They have all been in the OWASP Top 10 for years. What is new is the speed at which they reach production.

Why AI makes some risks worse

Two mechanisms turn the assistant into a risk multiplier rather than the source itself.

Speed. When you write by hand, you have a natural time gate — every line costs seconds of thought. With an agent you get 200 lines in ten seconds. The volume of code that “flies past” your attention without a real review grows by an order of magnitude. The gap in your review process is not constant — it scales with how fast you generate code.

Confidence. This is the more dangerous one. Model code is syntactically perfect: sensible variable names, comments, error handling, every bracket closed. It looks like senior code. Our brain reads that aesthetic as a signal of competence and lowers its guard. Meanwhile a tidy try/catch may catch an error and silently swallow it, and a nicely namedsanitizeInput function may sanitize nothing. Plausibility is not correctness.

On top of that comes hallucination: a model will confidently invent a config flag, an SDK method, or a package that does not exist. In a security context that means “I enabled encryption” can be a call to a non-existent option that quietly does nothing.

A pre-ship checklist

Before generated code goes to production, walk through these points. You do not need to do all of it by hand — most of it can be automated — but no point may be skipped “because it is only an MVP”.

  1. Secrets. Is there no key, password, or token anywhere in the diff? Is everything sensitive in environment variables, with .env in .gitignore?
  2. User input. Is every piece of external data (request, form, URL parameter, file) validated and parameterized? Database queries — never via concatenation.
  3. Permissions. Are roles, buckets, and CORS narrowed to the minimum? Least privilege — closed by default, you open only what is necessary.
  4. Dependencies. Does every new package actually exist, is it maintained, and free of known CVEs? Check the download count and the last publish date before you add it topackage.json.
  5. Authentication and authorization. Do protected endpoints really check identityand permissions? Missing object-level access control is the most common API hole.
  6. Error handling. Does any catch swallow an error silently? Do error messages avoid leaking stack traces or sensitive data to the user?
  7. Understanding. Can you explain what every non-obvious line does? If you do not understand a fragment, you do not ship it — asking the model to explain is free.

Tooling that does this work for you

A checklist in your head does not scale. Fortunately most of these classes are caught by automation — and they should be a gate in CI, not an option.

Secret scanning. Gitleaks or TruffleHog as a pre-commit hook and a pipeline step. Add push protection at the repository level too (GitHub and GitLab have it built in). This is the first and cheapest line of defense — a secret that never reaches history needs no rotation.

SAST — static analysis. Semgrep, CodeQL, or Snyk Code read the code without running it and flag injection patterns, XSS, or unsafe APIs. Semgrep has a readable rule syntax and it is easy to write your own rules tailored to your stack.

Dependency audit. npm audit, Dependabot, or Snyk — they check for known vulnerabilities in the dependency tree and raise update PRs. Wire it into CI with a threshold: high and critical block the merge.

Infrastructure scanning. If you have Terraform or Kubernetes manifests, Checkov or tfsec will catch a public bucket and an over-broad IAM role before anything is provisioned. This matters especially with vibecoding, because models happily generate “wide open” config just to make it pass — secure defaults already require a conscious human decision.

Important: these tools also produce false positives. Their job is not to replace thinking, only to shrink the surface a human has to review. A green pipeline is not a security certificate — it is the absence of known problems.

The right mindset: AI is a fast junior, not an auditor

The healthiest mental model I know: treat the assistant as a very capable, very fast intern. It writes an impressive amount of code, knows the syntax better than you, never gets tired. And — like any junior — it has no context for your system, bears no consequences for an incident, and will sometimes state false things with full confidence.

There are two things you do not do with a junior: you do not ship their code without review, and you do not make them the reviewer of their own work. The same two rules apply to the model. You can ask AI to point out potential security problems — that can help as an extra pair of eyes — but it does not exempt you from real verification. A reviewer who wrote the code has a blind spot exactly where they made the mistake.

Responsibility for security stays with the human who presses “deploy”. This is not about distrusting the tool — it is about who picks up the phone at three in the morning.

TL;DR

AI code is neither inherently secure nor insecure — it is unverified. The real risks are the old familiar ones: secrets in code, injection, XSS, insecure dependencies, over-broad permissions, and blind faith in good-looking code. AI makes them worse through speed and false confidence. The defense is a pre-ship checklist plus automation in CI: secret scanning, SAST, dependency audit, infrastructure scanning. The mindset that works: AI is a fast junior, not a security auditor — the review and the responsibility stay with you.

Is AI code secure? Vibecoding and security | vibecoding.pl