We Scanned 12,000 Vibe-Coded Apps. Not One Came Back Clean.
Across 12,000 recently launched products built with AI coding tools, every single app had at least one security finding. Here is what the data actually looks like, and why the pattern is entirely predictable.
12,000
Apps scanned
100%
Had findings
45%
High or critical
191k
Total findings
This report is based on passive surface scans run against recently launched products built with AI coding tools. These are real apps, built by real founders, shipping to real users. The scans are passive and read-only: no exploitation, no authentication, no brute-force. Just the same reconnaissance checks a competent attacker runs before deciding whether a target is worth their time.
The results are not what most founders expect to hear. Across every single domain scanned, zero came back with a clean bill of health. Not one. Every app had at least one security finding. Forty-five percent had findings rated high or critical severity.
This is not a referendum on AI coding tools. The bugs these tools produce are the same bugs developers have shipped for twenty years. The difference is volume and velocity. When a solo founder can ship a full-stack app in a weekend, security review rarely makes it into that weekend.
The findings that matter most
Severity labels are easy to dismiss. "Critical" sounds alarming in the abstract, but the specific findings are what make it concrete. Here is what was actually showing up.
Secrets committed to public repositories
Eighty-seven apps had credentials or tokens exposed in their public GitHub repositories. Not in private repos with a misconfigured permission. In public repos, readable by anyone. The secrets ranged from PostgreSQL connection strings to private keys, GitHub personal access tokens, Cloudflare API tokens, MongoDB credentials, Azure credentials, JWTs, npm tokens, and GitLab personal access tokens.
When a secret lands in a public repo, the clock starts immediately. Automated scanners maintained by threat actors sweep GitHub continuously. A Stripe key committed at 9pm on a Friday has a realistic chance of being harvested and tested before the founder wakes up Saturday morning. The fact that the commit was from eighteen months ago and has since been overwritten does not matter. Git history is permanent. The secret is still there.
| Secret type | Occurrences |
|---|---|
| Exposed URIs / connection strings | 59 |
| PostgreSQL credentials | 32 |
| Box API tokens (cloud storage) | 25 |
| Private keys | 20 |
| DockerHub credentials | 18 |
| Virus Total API keys | 16 |
| GitLab personal access tokens | 13 |
| npm tokens | 12 |
| JSON Web Tokens | 11 |
| GitHub personal access tokens | 9 |
| MongoDB credentials | 9 |
| Cloudflare API tokens | 8 |
| Azure credentials | 6 |
| Sentry tokens | 6 |
Git repository exposed over HTTP
Seventy-three apps had their /.git/ directory accessible over the public internet. When a web server serves the .git directory, an attacker can reconstruct the entire repository: every commit, every file, every secret that ever passed through the codebase. This is a deployment misconfiguration, not a code bug, which is exactly why it persists. The app works fine. Nothing looks broken. The exposure is invisible to the founder and completely visible to anyone who checks.
Credential files served directly
Seventeen apps returned their .env file when requested over HTTP. This is as bad as it sounds. The environment file is the single document that contains every production secret the app relies on: database passwords, API keys, signing secrets, OAuth credentials. Serving it publicly is the equivalent of taping your house key to the front door.
The near-universal gaps
The critical findings above are the most urgent, but they affect a minority of apps. The findings that affect almost everyone are the security headers that are either missing entirely or configured incorrectly.
Missing Content-Security-Policy
~9,700 apps
81%
Missing X-Frame-Options
~8,900 apps
74%
Missing HSTS
~5,300 apps
44%
These headers are not optional polish. HSTS tells browsers to enforce HTTPS for future visits, which blocks protocol downgrade attacks. Content-Security-Policy restricts what scripts and resources a page can load, which is the primary browser-side defence against cross-site scripting. X-Frame-Options prevents the page from being embedded in a frame on an attacker-controlled site, which is the primary defence against clickjacking.
None of these headers require application changes. They are server or CDN configuration. They take under an hour to add. The fact that they are missing from the majority of recently shipped apps is not a technical problem. It is a prioritisation problem, which is a much harder thing to fix.
Why this happens, and why it will keep happening
The standard narrative around security is that it requires expertise. That framing lets most founders off the hook. The issues described above do not require expertise to fix. They require awareness that they exist.
The gap is in the feedback loop. When you ship a feature, you know immediately if it works. When you deploy with an exposed git directory or a missing security header, nothing tells you. The app functions correctly. Users sign up. Revenue comes in. Everything looks fine until it is not.
Attackers do not need to be sophisticated to find these issues. They run the same passive checks described in this report, automated, against every domain they encounter. The asymmetry is the problem: attackers check every target, most founders check none.
AI coding tools make this worse in a specific way. They are extraordinarily good at producing working code quickly. They are not trained to pause and ask whether the deployment configuration is exposing git history, or whether the repo they are committing secrets to is public. The tools optimise for shipping. Security review is left to the person, and the person is usually moving too fast.
What to do about it
The good news is that the most common findings in this dataset are fixable in an afternoon, often without touching application code. Here is the priority order:
01
Audit your public GitHub repositories
Search your commit history for API keys, connection strings, and private keys. Rotate anything that has ever been committed, regardless of whether it has since been deleted. Tools like TruffleHog and git-secrets will scan history for you.
02
Confirm your .git directory is not publicly accessible
Request /.git/config from your production domain in a browser. If you get a file back instead of a 403 or 404, your entire repository is accessible. Fix the web server or CDN configuration to block that path.
03
Confirm your .env file is not publicly accessible
Request /.env from your production domain. Same test, same fix. Add an explicit deny rule for dotfiles in your web server configuration.
04
Add security headers
HSTS, Content-Security-Policy, and X-Frame-Options can all be set at the CDN layer (Vercel, Cloudflare, etc.) without touching application code. Most platforms have a one-page guide for this.
The bigger point
Security is not a feature you add when the product is mature. By the time a product has meaningful user data, the window to fix foundational issues without consequence has already narrowed. The credential exposed in a public repo from launch week does not wait for traction.
The 12,000 apps in this dataset are not outliers. They are a representative sample of what recently shipped software looks like. The finding rate is not 1 in 100 or 1 in 10. It is 1 in 1. Every app had something. The question is not whether your app has security findings. The question is whether you know what they are before someone else does.
Run a free surface scan
Talon checks your domain against the same passive reconnaissance categories described in this report. No access required. Results delivered by email as a PDF.
Methodology
Data collected from passive surface scans run against recently launched products. Scans are read-only: HTTP header inspection, certificate transparency log enumeration, public GitHub repository scanning via TruffleHog, and common path probing (no brute-force, no authentication, no exploitation). 12,250 domains scanned; 191,040 total findings logged. Data as of April 2026.