You're vibe-coding. Claude or GPT writes your backend in 10 minutes. It works. You ship it.
Three weeks later: SQL injection in production. Save function that never saved. Async endpoint blocking the event loop.
Here's how to add a security gate in 5 minutes that catches these before they ship.
The problem with AI-generated code
AI models generate code that looks correct. It passes type checks. It runs. It returns the right status code.
But structurally, it has consistent failure modes:
# Looks fine. Breaks everything.
async def get_user(user_id: str):
result = db.query(f"SELECT * FROM users WHERE id = '{user_id}'")
return result
# save() that doesn't save
def save_preferences(user_id: str, prefs: dict):
validated = validate(prefs)
return {"status": "saved", "user": user_id} # no INSERT anywhere
# async with no await
async def send_notification(msg: str):
requests.post(WEBHOOK_URL, json={"text": msg}) # blocks event loop
These aren't caught by mypy, pylint, or Bandit. They're structural patterns specific to AI-generated code.
Step 1: Scan locally (30 seconds)
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": 2,
"issues": [
{
"kind": "SQL_INJECTION_RISK",
"severity": "BLOCK",
"line": 3,
"detail": "unsafe SQL formatting — use parameterized queries"
},
{
"kind": "MISSING_WRITE",
"severity": "BLOCK",
"line": 8,
"detail": "function 'save_preferences' has no DB write call"
}
]
}
If passed: true — ship it. If not — fix the BLOCKs first.
Step 2: Add to GitHub Actions (2 minutes)
Create .github/workflows/vibeguard.yml:
name: VibeGuard Scan
on: [pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Scan changed files
run: |
git diff --name-only origin/main...HEAD \
| grep -E '\.(py|js|ts|go|rb|java|php|kt)$' \
| while read f; do
echo "Scanning $f..."
result=$(curl -s -X POST \
https://pleasing-transformation-production-90c2.up.railway.app/v1/scan \
-H "X-API-Key: ${{ secrets.VIBEGUARD_KEY }}" \
-F "file=@$f")
echo "$result" | python3 -c "
import json,sys
d=json.load(sys.stdin)
if not d.get('passed'):
for i in d.get('issues',[]):
if i['severity']=='BLOCK':
print(f'BLOCK [{i[\"kind\"]}] line {i[\"line\"]}: {i[\"detail\"]}')
sys.exit(1)
"
done
Add secret: Settings → Secrets → VIBEGUARD_KEY = vg_free_test
Every PR now gets scanned. BLOCK = PR fails. Green = safe to merge.
Step 3: Pre-commit hook (optional, 1 minute)
# .git/hooks/pre-commit
#!/bin/sh
for file in $(git diff --cached --name-only | grep -E '\.(py|js|ts|go)$'); do
result=$(curl -s -X POST \
https://pleasing-transformation-production-90c2.up.railway.app/v1/scan \
-H "X-API-Key: vg_free_test" \
-F "file=@$file")
passed=$(echo "$result" | python3 -c "import json,sys; print(json.load(sys.stdin)['passed'])")
if [ "$passed" = "False" ]; then
echo "BLOCK found in $file — run curl scan to see details"
exit 1
fi
done
chmod +x .git/hooks/pre-commit
Now every commit is scanned before it's made.
What gets caught
| Pattern | Example | Why AI generates it |
|---|---|---|
SQL_INJECTION_RISK |
f"SELECT ... WHERE id='{x}'" |
f-strings are common in training data |
MISSING_WRITE |
def save(): return {"ok": True} |
AI optimizes for "looking complete" |
FAKE_ASYNC |
async def f(): return requests.get(url) |
copies async signature without understanding |
CORS_WILDCARD |
allow_origins=["*"] + credentials |
copies boilerplate without understanding interaction |
STUB_SKELETON |
def process(data): return {} |
placeholder that AI forgot to implement |
HARDCODED_TABLE |
40-key dict instead of DB query | AI avoids DB setup complexity |
SSRF_RISK |
httpx.get(user_url) unvalidated |
doesn't think about internal network access |
PATH_TRAVERSAL |
open(user_path) unvalidated |
doesn't add boundary checks |
9 languages supported: Python, JS, TS, Go, Ruby, Java, PHP, Kotlin, C/C++.
Free tier
-
vg_free_testkey: full Pro features, 50 files/day - No signup required
- Code not stored — processed in memory, discarded after scan
API: https://pleasing-transformation-production-90c2.up.railway.app
GitHub: https://github.com/Moonsehwan/aina-scan
The whole point: vibe-coding is fast. The gate should be faster. 30-second scan before you merge beats a 3-week postmortem.
Top comments (0)