Theori, an offensive security research firm, published a study yesterday that's going to make a lot of small teams uncomfortable. They built 28 apps from scratch using AI coding agents — five different models, multiple providers — covering apps written from proper specs, thrown together from casual prompts, and rewritten from aging PHP codebases. Then they ran each one through their automated pentesting platform.
Total exploitable vulnerabilities found: 434.
The distribution is not what the security community expected. And the mismatch matters.
The injection boogeyman didn't show up
Everyone assumed the main risk in AI-generated code was injection flaws. SQL injection. Cross-site scripting. The classic bugs that have plagued web apps for decades, now multiplied because AI cranks out code fast and without careful review.
That's not what showed up. SecurityWeek covered this specifically: the AI models tested almost automatically reached for parameterized queries, ORMs, and input sanitization. They've been trained on enough secure-coding examples that injection-style flaws actually showed up less in AI-generated code than in code written by a rushed junior developer.
So what did show up in those 434 findings?
The stuff nobody thinks to specify
Missing rate limiting and DoS protection: 93 of the 434 flaws. An app with no rate limit on its login or password reset endpoint will happily accept 10,000 guesses per second. AI adds rate limiting when you tell it to. When you don't mention it, the feature gets built and ships. You now have a brute-force-friendly login form.
Authorization and IDOR: 88 flaws. This one should stop you mid-read. IDOR — Insecure Direct Object Reference — is when a URL like /api/records/1234 just works for any logged-in user, even if record 1234 belongs to someone else entirely. The AI writes "fetch the record by ID and return it." Unless the prompt explicitly says "first check that this record belongs to the authenticated user," that check doesn't exist.
SSRF and directory traversal: 54 flaws. Attackers can sometimes trick server-side features into making requests to internal infrastructure or walking outside the intended directory structure.
Hardcoded secrets: Nearly half of the 23 critical findings were hardcoded or predictable secrets — JWT signing keys, encryption keys, default credentials baked directly into the codebase. The AI generates a placeholder secret, nobody replaces it before deploying, the app ships with a known key. Anyone who finds that key can forge any user session in the system.
Why this is a small-org problem specifically
Large organizations have AppSec teams and penetration testing contracts. When an enterprise builds something with AI, there's usually a review process before it touches production data.
Small teams — NGOs, small businesses, government offices — don't have that process. Someone had a problem, used one of the popular AI coding tools to build a solution in an afternoon, it worked, and now it's running. Nobody ran a security scan. Nobody's coming to run one either.
Think about what your team has built in the last 18 months with AI help: an intake form, a client portal, an internal dashboard, a grant tracking tool, a case management system. Most of those things touch real data about real people. Most of them were never security tested beyond "does the feature work."
Theori's research didn't test actual small-org tools — they built controlled test apps. But the vulnerability patterns are structural. As CyberSecurityNews put it, when developers only ask AI to implement features — without specifying security requirements — the generated code omits critical protection mechanisms. That's not a bug in the AI. It's a gap in how teams prompt it.
If you didn't say "and make sure this record belongs to the requesting user," that check probably isn't there.
What to actually check
This doesn't require hiring anyone yet. It requires a few hours.
IDOR check: For every feature where different users should only see their own data, test it with two accounts. Log in as user A. Grab a URL that includes a record ID. Log in as user B. Visit that URL. Can you see user A's data? If yes, broken access control. This takes 5 minutes per feature and requires zero technical background.
Rate limit check: Hit your login or password reset endpoint 20 times in 10 seconds from a browser or simple script. If nothing blocks or slows you down, there's no rate limiting. Try the same against any form that looks up user data by email or ID.
Secret scan: Run Gitleaks or TruffleHog against your repo. Both are free and open source. Run against your git history too, not just current files — secrets that were committed and later deleted are still in the history. Takes 20 minutes to set up.
Environment check: Is your .env file excluded by .gitignore? Are your production secrets different from the dev defaults in your .env.example? Are any signing keys or encryption keys that appear in the codebase ones you actually generated, not generated by the AI as a placeholder?
None of this requires a security background. It requires being willing to look.
The uncomfortable part
We've been telling small orgs to use AI tools to build things they couldn't afford to build before. We stand by that. But it comes with a responsibility to check what got built.
AI-generated code works in the obvious sense — features run, users log in, data gets saved. What it doesn't do automatically is enforce who's allowed to see what, slow down someone hammering an endpoint, or generate secrets you actually control. Those gaps are quiet. Nobody trips over them during normal use. An attacker looks for them on day one.
If your team has built anything with AI in the last couple of years and it handles client data, donor records, health information, or case files, it's worth doing this sweep. Probably takes half a day. Probably surfaces a few fixes. Occasionally surfaces something that needs to move faster than that.
This is the kind of review we run with small orgs — focused, hands-on, looking at what AI gets wrong that normal testing misses. If you want someone to do this with you, we're easy to reach.