#1–#3: dangerouslySetInnerHTML and the .gitignore pair
dangerouslySetInnerHTML topped the list at 42.6% of repos. React names it dangerously for a reason: it injects raw HTML, and if any of that HTML ever derives from user input, it's cross-site scripting. Generated code reaches for it because it's the shortest path to rendering rich content. The fix: render text as text, and when you genuinely need HTML, sanitise it with a maintained library like DOMPurify first — never interpolate user input into it raw.
Numbers two and three are the same discipline failing twice: 35.7% of repos have a .gitignore that doesn't cover .env files, and 15.3% have no .gitignore at all. Both are prevention failures — the repo may be clean today, but nothing stops tomorrow's .env commit. The fix is minutes: add a root .gitignore covering .env, key files, and your stack's build artifacts. It's the single cheapest security improvement in this study, which makes its absence in 15% of repos the study's quietest bad news.
#4–#5: curl | sh installs and hardcoded passwords
curl-piped-to-shell appeared in 9.8% of repos: an install step that downloads a script from a URL and executes it immediately, usually in a README or setup script. You're running unreviewed remote code with your local privileges, and the script can change after you've vetted it. The fix: download first, read it, pin to a specific version or checksum, then run — or prefer the package-manager install the project almost always also offers.
Hardcoded passwords also hit 9.3% of repos — a password literal sitting in committed code. This is the finding that overlaps with the study's scariest aggregate (20.4% of repos had at least one secrets finding of some kind). The fix is the standard two-step: move the value to an environment variable, and if the repo is public, rotate the credential first — git history keeps what you committed, so deletion alone doesn't end the exposure.
#6: wildcard CORS — mostly harmless, and why we're saying so
Wildcard CORS (Access-Control-Allow-Origin: *) showed up in 11.3% of repos. We originally listed it as a security finding. A reader challenged that, and they were right, so here's the correction.
On its own a wildcard gives an attacker nothing. Browsers refuse to attach cookies to a request whose response allows any origin, so a malicious page reading your API learns exactly what a logged-out visitor could have fetched anyway. If the data is public, this is a non-issue.
It turns dangerous in one specific pairing: a wildcard plus credentials. We tested four libraries. FastAPI/Starlette, flask-cors and django-cors-headers all quietly rewrite that '*' into whatever origin asked, and send Access-Control-Allow-Credentials: true alongside it — which the browser will hand over. Express's cors package doesn't; it sends a literal '*' and the read gets blocked. So the same config is a real hole in Python and a shrug in Node.
We went back through all 120 wildcard hits to see which were which. 40 weren't running code at all — markdown, prompt-library templates, another scanner's rule definitions, good/bad teaching snippets. 76 were bare wildcards with no credentials. Two repos were genuinely exploitable. That's 0.4%, not 11.3%. One repo we'd flagged had shipped a guard that refuses to boot on the invalid combination, and we'd cited its own warning message back at it.
So the scanner now reports the wildcard only where it's paired with credentials. A bare one produces nothing at all — we dropped that check rather than hand you 42 findings that all resolve to "this is probably fine". Read the 11.3% as what AI codegen emits by default, not as a count of vulnerable apps.
Read together, the six are one story: AI tools generate the most direct working version, and the most direct version skips the guard rails. None of these findings breaks an app, which is why they survive the does-it-run feedback loop and ship. All six are pattern-detectable, which is the point of a rules-based scan — and all six come from this study: 549 public repos self-described as AI- or vibe-coded, scanned by the secure·vibes rules engine, data collected July 2026. Per-rule reporting caps mean the per-repo counts behind these percentages are floors.
The top 6 security findings across 549 vibe-coded repos (secure·vibes rules engine, July 2026)
| Finding | % of repos | The fix |
|---|---|---|
| dangerouslySetInnerHTML | 42.6% | Render text as text; sanitise unavoidable HTML with DOMPurify |
| .gitignore missing .env coverage | 35.7% | Add .env, key files, and artifacts to the root .gitignore |
| No .gitignore at all | 15.3% | Add one — minutes of work, prevents most future data-exposure findings |
| curl | sh install | 9.8% | Download, read, pin a version/checksum — or use the package manager |
| Hardcoded password | 9.3% | Move to an env var; rotate first if the repo is public |
| Wildcard CORS | 11.3% (0.4% exploitable) | Only a hole when paired with credentials — then allowlist your real origins |
frequently asked
Is dangerouslySetInnerHTML always a vulnerability?
No — with static, trusted HTML it's a smell rather than a hole. It becomes XSS the moment user-influenced data flows into it, and in a generated codebase nobody has audited that flow. The scan flags it so you check; the fix (sanitise or render as text) is cheap either way.
Why do two of the top three involve .gitignore?
Because nobody prompts for one. The AI was asked for an app, not repo hygiene — so a quarter of self-described vibe-coded repos ship with no .gitignore or one that won't stop a .env commit. No other five-minute fix in the study prevents as much damage.
Where do these percentages come from?
From our corpus study: the secure·vibes rules engine run over 549 public GitHub repos that describe themselves as AI- or vibe-coded, collected July 2026, AI pass off. Each percentage is the share of repos with at least one instance of that finding. Full method is on the methodology page.
How do I check my repo for all six at once?
Paste the public repo link into secure·vibes. The same rules that produced these numbers run against your code — plus a Claude review the study deliberately switched off — and every finding comes back with file, line, and a paste-ready fix prompt.
Last updated June 11, 2026