All articles

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

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.

April 19, 2026VibeWShield Team3 min read

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/me

If 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