Skip to content
← BlogDeveloperAnalysis9 min read

AI wrote the app. It also wrote about 15 security holes into it, on average

A July 2026 audit found 434 exploitable flaws across 28 AI-built applications, an average of about 15 per app. Here's what the pattern actually looks like.

By Gabriel Lara, Developer Relations, Norbelys

Founder-reviewed ·How we research and correct articles

“Vibe coding” — describing what you want and letting an AI agent write the implementation — produces working software fast enough that it’s easy to mistake “it runs” for “it’s safe.” A security audit published in late July 2026 put a number on the gap: 434 exploitable flaws found across 28 AI-built applications, according to reporting summarized by Cybersecurity News and corroborated by Help Net Security.

434
Exploitable flaws found
across the audited applications
28
AI-built applications
included in the audit
~15.5
Flaws per app
434 ÷ 28 — our calculation from the reported totals

Cybersecurity News, reporting on the audit findings, July 23, 2026.

That’s not “an app here or there had a bug.” That’s every audited application carrying, on average, over a dozen exploitable issues — in code that compiled, ran, and by every functional measure did the job it was asked to do.

Why the flaws cluster where they do

The reported root cause is more specific than “AI writes buggy code,” and it’s the specific part that matters: models handle vulnerability classes that are well represented in their training data reasonably well, and they omit controls that have no local syntactic signal — meaning nothing in the immediate code around a given line hints that a check is missing. An authentication bypass, a validation gap, an overly permissive default: none of these look wrong by just reading the ten lines around them. They look wrong only if you’re reasoning about the whole system’s trust boundaries, which is exactly the kind of reasoning a model optimizing for “does this satisfy the prompt” has the least incentive to do.

The kind of flaw this pattern predicts

You don’t need the audit’s full taxonomy to know roughly what it found, once you know the mechanism: it’s whatever a reviewer only catches by holding the whole request path in their head at once, not whatever’s visible in a single function. In practice, that’s a short, recognizable list that shows up again and again in AI-generated code specifically:

  • Authorization checked at the wrong layer. A route handler confirms the user is logged in, but never confirms they’re allowed to touch this specific record — a gap invisible unless you’re comparing the request’s identity against the resource it’s requesting, not just checking that some identity exists.
  • Defaults left wide open. CORS configured to accept any origin, a database migration that ships with permissive default roles, a new endpoint with no rate limit — all things a working demo doesn’t need, and a model optimizing for “make the demo work” has no reason to add.
  • Secrets treated as configuration, not as secrets. Logged in plaintext during debugging, committed in a .env.example file that quietly became the real one, passed as a URL query parameter that ends up in server access logs.
  • Trust boundaries collapsed. Input from a webhook, a URL parameter, and an authenticated session all get handled by the same code path with the same level of trust, because distinguishing them requires modeling where the data came from, not just what shape it’s in.

None of these require an attacker with unusual skill. They require an attacker who reads the same publicly available audit you just did, and checks whether your integration matches the pattern.

Where this bites hardest: integration code

General app-building flaws are one thing. The pattern gets sharper when the “app” is glue code between your business and a third-party API — the exact kind of thing a growth or ops team asks an AI agent to scaffold in an afternoon: “pull the reply list and post new ones to Slack,” “write a script that enrolls contacts from this CSV,” “build a small dashboard over the analytics endpoint.” That code almost always needs to hold a credential — an API key, an OAuth token — and the vulnerability classes the audit describes (missing checks with no local syntactic signal) map directly onto the mistakes that turn a scoped integration into an unscoped one: a key logged in plaintext, an endpoint called with no rate limiting, a webhook handler that trusts its payload without verifying a signature.

None of those mistakes will fail your tests. All of them will fail an audit, usually after something’s already gone wrong.

Why “it passed testing” doesn’t mean what it used to

The uncomfortable part of this finding isn’t that AI-generated code has bugs — every codebase does. It’s that the specific bugs it tends to have are the ones a functional test suite is structurally bad at catching. A test that confirms “this endpoint returns the right contact record for the right user” doesn’t confirm “this endpoint refuses to return a different user’s contact record when asked” — those are different assertions, and only one of them is the one most generated test suites write by default, because it’s the one the happy-path prompt implies.

That gap used to be closed, informally, by a human writing the code also having some background awareness of what an attacker would try. An agent optimizing purely against “does this satisfy the request and pass the tests I wrote” has no equivalent background awareness unless something in the prompt or the surrounding tooling supplies it. Which is exactly why a deliberate, separate review pass — not just “run the tests, ship it” — is the piece of process that closes the gap testing alone won’t.

What to actually check before you ship AI-generated integration code

A short, specific review pass catches a disproportionate share of what audits like this one keep finding, and none of it requires distrusting the tool that wrote the code — it requires treating “an agent wrote this” the same way you’d treat “an unfamiliar contractor wrote this”:

A five-minute review pass before merging AI-generated integration code

  1. Find where the credential lives

    AI agents reach for the easiest working pattern, which is frequently an environment variable read directly into code or a hardcoded string rather than a secrets manager. Check the diff specifically for this before merging, not after something leaks.

  2. Check what the integration's key can actually do

    A script that only needs to read reply status shouldn't be handed a key that can also send, delete, or export. Scope every key to the operation it actually performs, and re-check that scope every time an agent regenerates the integration.

  3. Look for a rate limit or retry ceiling

    Missing backoff logic is exactly the kind of omission the audit describes — nothing local flags it as wrong in a code review, and it only becomes a problem under load, a failure, or a bug that loops.

  4. Ask what the code trusts without verifying

    Webhook handlers are a common blind spot: an agent will happily wire one up that processes whatever payload arrives, with no signature check, because the happy path works fine in testing and nothing about the code looks wrong on its own.

None of these four checks require deep security expertise, and none of them show up in a normal functional test suite — they’re specifically the class of thing the audit found missing, which is exactly why they’re worth making a deliberate, named step in review rather than trusting they’ll get caught incidentally.

The honest reason most teams skip this pass isn’t that it’s hard — each of the four checks above takes a few minutes on a small integration. It’s that nothing in the normal development loop prompts anyone to run it. The agent doesn’t flag “here’s what I didn’t verify,” the tests pass, the demo works, and the integration ships. Making this a named, required step — a line item in a pull request template, a checklist a reviewer actually has to tick — closes more of the gap than any amount of trusting the tool to get better on its own.

What this means for building on Norbelys

The audit’s finding generalizes past whatever 28 applications it actually looked at: the fewer decisions your integration code has to make about authentication, scope, and trust boundaries on its own, the less surface there is for an agent to get one of those decisions wrong. That’s a platform-design problem as much as it’s a code-review problem, and it’s worth choosing the API you build against with that in mind.

If an AI coding agent is scaffolding your integration against the Norbelys API, you get a meaningfully smaller version of this problem for free, because there’s less hand-rolled surface for a flaw to hide in. The official SDKs are generated from a single typed contract, so an agent reaching for @norbelys/sdk instead of assembling raw HTTP calls inherits request signing, idempotency handling, and typed error responses rather than reinventing them — and reinventing them badly is exactly where the audit’s 434 flaws came from. API keys are scoped per workspace by design, so an agent that over-requests scope still can’t reach past the boundary you set when you issued the key.

The review discipline still matters — no SDK stops an agent from logging a key to a file it shouldn’t, and that check is on you. But the gap between “an agent wrote working integration code” and “an agent wrote safe integration code” gets a lot narrower when the building blocks it’s reaching for were built to be hard to misuse in the first place. Read the OpenAPI spec, pick the SDK for your runtime, and let your coding agent build against a contract that was designed for exactly this — instead of the 434-flaws-per-batch version of the same afternoon’s work.