I'm Fast, Very Fast: What We Keep Finding in Vibe-Coded Apps

Over the past couple of years, we have watched the share of our web application engagements involving code that was partly or entirely built with AI coding assistants climb steadily. It used to be the exception. Now it is closer to the norm, and the pattern has become predictable enough that we can usually tell, within the first hour of testing, whether we are looking at a vibe-coded application before anyone tells us.

There is a moment in The Office where Michael Scott takes off sprinting during the Fun Run, shouting "I'm fast, very fast." He was fast. He had also just carbo-loaded on a giant plate of fettuccine alfredo right before the race, and it caught up with him almost immediately.

That is roughly what it feels like watching a lot of engineering teams right now with AI-generated code: moving faster than ever, and about to find out what they skipped to get there.

I am fast, very fast

What we actually find, over and over

The classic web vulnerabilities every appsec person trained on for the last two decades are, if anything, less common in these applications than in traditionally written ones. Ask an AI coding assistant to build a login form and it will almost always parameterize the query and encode the output correctly. Injection flaws are not where these applications tend to fall apart.

Where they fall apart is judgment. Specifically, three things show up constantly across our engagements:

Missing rate limiting and resource exhaustion. AI-generated code is optimized to satisfy the prompt, not to survive hostile traffic. It works perfectly in a demo and buckles the moment someone throws unexpected load at it, whether that is an attacker deliberately abusing an endpoint or just a traffic spike nobody planned for. Rate limiting, backpressure, and sane handling of malformed input are not things a model reaches for unless someone specifically asks for them, and almost nobody specifically asks for them.

Authorization flaws and IDOR that scale with the size of the codebase. This is the one we watch for most closely now. A model can reason reasonably well about a single endpoint in isolation. It is far less consistent at holding a coherent, application-wide permission model in its head across dozens of routes, especially once a codebase has grown well past what fits comfortably in a single working context. The bigger and older the vibe-coded application, the more likely we are to find a spot where an object reference or permission check was handled one way on one route and a slightly different way three routes over, and the gap between those two is exactly where we get in.

Hardcoded secrets and debug flags left live in production-intended code. These are the findings that never show up in a functional test and sit quietly until someone goes looking. We go looking.

This is a pattern we see often enough outside of vibe-coded applications too, particularly around Azure environments, where hardcoded connection strings and API keys tend to surface in the exact same places whether a human or an AI assistant wrote the code. If this is a category that concerns you, we cover it in more depth in our article on Azure secrets leaks by Charles Hamilton where the following shocking example is explained.

Why this keeps happening

None of these are new vulnerability classes. What has changed is the pace and the surface area. Every engineering team we work with now ships meaningful, AI-assisted changes on a weekly or even daily cadence. Very few of them have adjusted their security review cadence to match. Most are still testing on an annual or per-major-release schedule that was built for a much slower era of software delivery.

That gap shows up most clearly in the applications nobody thought to test in the first place. The quick internal dashboard someone vibe-coded over a weekend to unblock a project. The AI-modernized legacy tool nobody budgeted a real review for because it was "just a refresh." These are exactly the applications where we tend to find the authorization flaws described above, and exactly the applications that never make it onto a formal assessment calendar, because nobody thought of them as a real application in the first place.

The maintenance problem nobody budgets for

There is a second issue we run into constantly that rarely comes up before an engagement starts: code that a human did not write is code that a human has to learn before they can safely change it.

When a developer writes a feature themselves, they carry a mental model of why it was built that way. When code arrives fully formed from a prompt, that mental model does not exist for anyone on the team. The first time a bug needs fixing, or a new feature needs to slot into that code, an engineer has to reverse-engineer the intent before they can touch it safely. We see the consequences of that reconstruction gap directly during testing, in the form of inconsistent logic between parts of the application that were clearly generated at different times by different prompts, with nobody in between reconciling the two.

It gets harder, not easier, as the application grows. A model working inside a small, simple codebase can hold most of the relevant context at once and make a reasonably coherent change. As the codebase grows, that same model is working with an increasingly partial view of the system. It cannot see every place a permission check is supposed to apply, or every assumption from months earlier that has quietly become false. That is a large part of why we find more authorization drift in the larger, older vibe-coded systems we test than in the small, recently built ones.

Teams that treat AI-assisted development as a flat, permanent cost saving tend to discover otherwise during an incident rather than during a planning meeting.

What we tell clients to actually do about it

We are not telling anyone to stop using AI coding tools. That shift has already happened, and the productivity gains are real. What we do recommend, consistently, is this:

  • Review AI-generated code with a different checklist than human-written code. The failure modes are different. Look specifically for missing rate limiting, inconsistent authorization checks across similar-looking endpoints, and hardcoded values that look like they were lifted from a working example rather than pulled from a secrets manager.
  • Test the internal tools, not just the flagship product. The vibe-coded dashboard nobody thinks of as a real application is, in our experience, exactly where we find the worst authorization issues.
  • Match your assessment cadence to your shipping cadence. If a team is shipping AI-assisted changes weekly, an annual pentest is measuring a moving target from a year-old snapshot.
  • Budget for maintenance cost to rise as these applications grow, rather than assuming AI-assisted development stays cheap indefinitely. Plan for more review time, not less, as size and age increase.
  • Get a real, manual assessment on anything customer-facing or handling sensitive data before it ships, regardless of how it was built. Automated scanning catches individual bugs. It is the manual, adversarial part of a real pentest, chaining an IDOR into a data exfiltration path, or a debug flag into full remote code execution, that turns a list of findings into an accurate picture of business risk.

Asking your AI assistant to review its own code is not a security control, it is a second opinion from the same intuition that missed it the first time. A real pentest is what actually finds what both passes over when it comes to identify vulnerabilities and perform a risk assessment.

If your team has shipped AI-assisted code into production in the last year and it has not had a dedicated security assessment since, that gap is worth closing before someone else finds it first.