Security scanner for AI-generated / vibe-coded apps — Lovable, Bolt, v0, Supabase, MCP.
Apps built with AI tools ship fast, and ship the same handful of critical holes:
exposed API keys, missing Row-Level Security, authorization checked in the browser.
vibeward finds them from the outside, read-only, in one command.
⚠️ Authorized use only. Run this only against applications whose owner hired or authorized you to audit them. It is strictly read-only — no writes, no deletes, no data exfiltration — but scanning systems you don't own without permission may be illegal.
The failure is documented, not hypothetical:
- CVE-2025-48757 (CVSS 9.3) — insufficient Supabase RLS in Lovable let unauthenticated attackers read/write arbitrary tables. 170 of 1,645 analyzed projects (10.3%) were vulnerable.
- Moltbook (2026) — a vibe-coded app with no RLS policies and a public key in the client bundle exposed ~1.5M auth tokens and 35,000 emails.
- Veracode (2025) — AI-generated code introduced security flaws in 45% of tests.
The platforms tell you securing the data is your responsibility. vibeward is how you check.
- Exposed secrets in every JS bundle — Supabase
service_role(decoded from the JWT role, so the publicanonkey is not flagged), Stripe, OpenAI, Anthropic, Google, AWS, GitHub, Resend, SendGrid, Twilio, private PEM keys, and suspicious secret assignments. - Supabase Row-Level Security — probes ~60 common table names with the public key. If a table returns data, RLS is broken, and it flags which columns hold personal data.
- HTTP security headers — CSP, HSTS, X-Frame-Options, X-Content-Type-Options, and tech leaks.
Server-side checks (authorization, input validation, rate limiting, backups) are covered by the
manual part of an audit — see CHECKLIST-25.md.
No install needed — use npx, so you always run the latest version:
npx vibeward@latest https://client-app.lovable.app(All the examples below drop the npx vibeward@latest prefix for brevity.)
Most vibe-coded holes start with a prompt: "disable RLS so it works", "use the service_role
key in the frontend", "make it public to debug", "remove the login for now". vibeward guard
catches those the moment you ask — and tells you why and what to do instead — before the agent
acts. The rules are deterministic (no LLM call), so they can't be prompt-injected away.
Add it as a Claude Code UserPromptSubmit hook (in ~/.claude/settings.json or a project's
.claude/settings.json):
{
"hooks": {
"UserPromptSubmit": [
{ "hooks": [{ "type": "command", "command": "npx vibeward@latest guard" }] }
]
}
}A risky request is now blocked with an explanation before the model runs. Use guard --warn to
warn without blocking.
Beyond the guardrail, vibeward also scans finished code — the backstop.
Black-box (from a URL) is the quick outside-in check — great as a first look:
vibeward https://client-app.lovable.appWhite-box (from the code) is the deep audit — it reads the actual folder (a git repo, a
downloaded ZIP from v0/Bolt/Lovable, or a synced folder) and reports exact file:line context:
vibeward scan ./client-appIt scans source files for secrets and a committed .env, and analyzes any Supabase/SQL
migrations for tables created without RLS, permissive USING (true) policies, and
SECURITY DEFINER functions.
Row-Level Security, policies and functions live in the Supabase project, not always in the code. Print a read-only query, run it in the Supabase SQL Editor, download the single JSON result, and feed it in — zero access to the client's project required:
vibeward supabase-sql > audit.sql # 1. send/run this query in the SQL Editor
vibeward scan ./client-app --supabase result.json # 2. fold the export into the report| Flag | Description |
|---|---|
--supabase <file.json> |
Fold in a Supabase audit export (from supabase-sql) |
--supabase-url <url> / --anon-key <key> |
URL mode: Supabase config if not auto-detected |
--no-rls |
URL mode: skip the Row-Level Security probe |
--sarif <file> |
Write SARIF 2.1.0 (for GitHub code scanning) |
--out <file.md> |
Report path |
--json |
Also dump raw findings as JSON |
--yes |
Confirm authorization without the interactive prompt |
Exit code 2 when critical findings are present (useful in CI).
Run vibeward on every push and see findings in the repo's Security tab:
# .github/workflows/vibeward.yml
name: vibeward
on: [push, pull_request]
permissions:
security-events: write
contents: read
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: JSiapoDEV/vibeward@v1
with:
path: '.'
# supabase: audit.json # optional: a committed Supabase exportFindings upload as SARIF; the job fails on a critical finding (set fail-on-critical: 'false' to
report without gating).
Run npm run demo to generate a sample report from synthetic data — it's identical to what a
client receives, with critical findings, an executive summary and a database-exposure table.
Requires Node 24 (.nvmrc); the published build targets Node ≥ 20.
nvm use
npm install
npm run dev -- https://example.com --yes # run from source (tsx)
npm test # synthetic checks, touches nothing external
npm run build # compile to dist/
npm run lint && npm run format:check # ESLint + Prettiersrc/
scan.ts CLI entry (url / scan / supabase-sql)
demo.ts sample-report generator
lib/
fetchers.ts fetch + bundle discovery
secrets.ts secret patterns & detection (URL + source)
supabase.ts RLS probe + audit-export analysis
folder.ts white-box folder walk
migrations.ts SQL migration analysis
headers.ts security headers
report.ts Markdown report
sarif.ts SARIF 2.1.0 output
types.ts shared types
test/self-test.ts synthetic tests
action.yml GitHub Action (composite)
MIT © José Siapo