I scanned KaletoAI/anima-verse — a Python LLM project that literally says "Vibe-coded experiment" in the README.
Result: 137 BLOCK-level security issues across 212 files.
Here's exactly what the AI generated.
What is anima-verse?
An LLM-driven character simulation app. FastAPI backend, SQLite, multi-channel chat. Real project, actively developed. Not a toy.
The README says:
"Vibe-coded experiment in LLM-driven character simulation."
So this is a perfect real-world test case.
Bug #1 — SQL Injection × 4 files
queue_cli.py L111:
def cmd_list(username):
sql = f"SELECT * FROM queue WHERE user='{username}'"
db.execute(sql)
Same pattern in agent_loop.py, character_io.py (twice).
The AI generated parameterized queries in some places but reverted to f-string SQL in others — inconsistently, across the same codebase.
Fix:
db.execute("SELECT * FROM queue WHERE user=?", (username,))
Bug #2 — CORS Wildcard + Credentials (Critical)
app/server.py L373:
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True, # ← this is the problem
)
allow_origins=["*"] with allow_credentials=True is explicitly forbidden by the CORS spec — browsers block it. But more importantly: if you relax this in production (which developers often do to "fix" the browser error), any website can make authenticated requests to your server.
This is a classic AI pattern. It copies the CORS middleware boilerplate without understanding the credential interaction.
Bug #3 — SSRF × 2
app/core/channel_health.py L131:
def _check_backend(url):
response = httpx.get(url)
return response.status_code
url comes from config — but config is user-editable. No allowlist, no scheme validation, no internal IP check.
An attacker who can edit config (or MITM the config source) can make this server probe internal services: http://169.254.169.254/ (AWS metadata), http://localhost:5432/ (Postgres), etc.
Bug #4 — Path Traversal × 5
app/core/config.py, five separate functions:
def _seed_default_marketplace_catalogs(config_path):
with open(config_path) as f: # ← no validation
data = json.load(f)
config_path is constructed from user-controlled input in some call paths. No pathlib.Path.resolve(), no directory boundary check.
Full count
| Issue | Count | Severity |
|---|---|---|
| SQL Injection | 4 | BLOCK |
| Path Traversal | 5 | BLOCK |
| SSRF | 2 | BLOCK |
| CORS Wildcard + Credentials | 1 | BLOCK |
| Unreachable Return | 1 | BLOCK |
| Others | 124 | BLOCK |
| Warnings | 69 | WARN |
| Total | 206 |
Why does this happen?
AI models learn from open source code — which contains both good and bad patterns. When generating a new file, the model picks patterns based on frequency, not correctness.
SQL injection via f-strings appears millions of times in training data. CORS with wildcard appears everywhere. So the model generates it.
The fix isn't to stop using AI — it's to scan every file the AI generates before it goes to production.
How to scan your own vibe-coded project
# Scan a single file (free, no signup)
curl -X POST https://pleasing-transformation-production-90c2.up.railway.app/v1/scan \
-H "X-API-Key: vg_free_test" \
-F "file=@app.py"
Response:
{
"passed": false,
"block_count": 3,
"issues": [
{
"kind": "SQL_INJECTION_RISK",
"severity": "BLOCK",
"line": 111,
"detail": "unsafe SQL formatting in execute() — use parameterized queries"
}
]
}
Or add it to your GitHub Actions pipeline so every PR gets scanned automatically:
- uses: Moonsehwan/aina-vibeguard-action@v1
with:
api-key: ${{ secrets.VIBEGUARD_KEY }}
fail-on-block: 'true'
Links
- Free scan API: https://pleasing-transformation-production-90c2.up.railway.app
-
Free key (Pro features, until June 24):
vg_free_test - GitHub: https://github.com/Moonsehwan/aina-scan
- Scanned repo: https://github.com/KaletoAI/anima-verse
The scanner is deterministic AST — no LLM, same result every time, CI-safe.
Top comments (0)