Bolt.new Security Audit: 7 Blind Spots in Every App You Ship

Bolt.new generates full-stack apps in minutes, but the default project template hides unsafe patterns. Here are the seven checks every Bolt app should pass before it goes to production.
Bolt.new — the StackBlitz-powered vibe-coding IDE — shipped over two million projects by the end of 2025. It is absurdly good at scaffolding a working full-stack application from a one-line prompt. It is also absurdly consistent about leaving the same seven holes in every project it produces.
This is not a Bolt problem. It is a prompt problem: the default system prompt rewards working software, not secure software. Here is what to audit before your Bolt app meets the internet.
1. The entire .env is inside the client bundle
Bolt runs the app inside a WebContainer, so every file — including .env — is bundled and shipped to the browser. When you deploy the exported project to Vercel or Netlify, the bundling step often preserves the habit.
Check:
curl -s https://your-app.vercel.app/_next/static/chunks/*.js \
| grep -E "(sk_live|sk_test|rk_live|SUPABASE_SERVICE_ROLE|OPENAI_API_KEY)"If that prints anything, rotate the key today and rename the variable to drop NEXT_PUBLIC_ / VITE_.
2. Supabase anon key used as admin key
Bolt's default Supabase wiring calls createClient(url, anonKey) in a server handler and ships the anon key to the browser. Both endpoints then rely on RLS — except Bolt's starter RLS templates are USING (true).
Check — anonymously hit every table:
curl "https://<ref>.supabase.co/rest/v1/orders?select=*" \
-H "apikey: <anon-key>"If you get rows back for orders, users, invoices, or messages, your RLS is performative. See How to secure Supabase RLS →.
3. "Admin" checks on the client
Bolt loves this pattern for a first demo:
{user?.role === "admin" && <AdminDashboard />}It hides the admin UI — not the admin API. The /api/admin/* endpoints it also generates are routinely left open, because the auth check lived in JSX.
Check — open DevTools → Console:
fetch("/api/admin/users").then(r => r.json()).then(console.log)A 200 with user data is game over.
4. Server routes without input validation
Bolt generates Next.js App Router or Remix actions. Both flavors ship without Zod validation in the first draft:
export async function POST(req: Request) {
const body = await req.json();
return Response.json(await db.user.update({ where: { id: body.id }, data: body }));
}data: body is mass assignment — the client can send { id: ..., role: "admin", credits: 99999 } and the DB accepts it.
Fix — always peel inputs through Zod and whitelist updatable fields:
const UpdateSchema = z.object({
name: z.string().min(1).max(80),
avatarUrl: z.string().url().optional(),
});
const parsed = UpdateSchema.safeParse(await req.json());
if (!parsed.success) return new Response("Bad Request", { status: 400 });
return Response.json(
await db.user.update({
where: { id: session.user.id }, // always session, never body
data: parsed.data,
}),
);5. Stripe webhooks that skip signature verification
Bolt's payment starter wires a webhook like this:
export async function POST(req: Request) {
const event = await req.json();
if (event.type === "checkout.session.completed") {
await grantCredits(event.data.object.customer);
}
return Response.json({ ok: true });
}Anyone can curl -d '{"type":"checkout.session.completed","data":{"object":{"customer":"<user>"}}}' and claim unlimited credits. The webhook MUST verify the stripe-signature header with stripe.webhooks.constructEvent.
6. Default CORS *
Bolt WebContainers add Access-Control-Allow-Origin: * to let the preview iframe talk to anything. That config gets exported with the project. On a real domain with cookies, this is the setup for credential theft on auth-gated endpoints.
Check:
curl -I -H "Origin: https://attacker.example" https://your-app.com/api/meIf the response contains access-control-allow-origin: * and access-control-allow-credentials: true, browsers will normally reject it — but not if * is replaced by a reflected Origin. Audit the header in middleware.
7. console.log(secret) forever
The WebContainer experience encourages console.log(process.env.STRIPE_SECRET_KEY) during debugging. Those lines stay in the exported bundle and reach production devtools. Scanners that capture console output — including ours — flag them as critical secret exposures.
Check — deploy a test build and open devtools:
// Ctrl+Shift+I → Console tab → refresh page
// Search the console history for 'sk_', 'eyJ', 'AIza', 'AKIA'The audit in one command
You can reproduce checks 1-7 by hand, or you can point VibeWShield at the deployed URL:
https://vibewshield.com/scan?url=https://your-app.vercel.app
It runs all seven checks plus the broader Bolt/Lovable/v0 vulnerability catalog in under three minutes, no signup required.
The short version: Bolt is outstanding at showing you a working app fast. It is not responsible for remembering that the app eventually needs to face real users. That part is on you — and it takes about twenty minutes once you know what to look at.
Free security scan
Test your app for these vulnerabilities
VibeWShield automatically scans for everything covered in this article and more — 18 security checks in under 3 minutes.
Scan your app free