Security Education

8 min

What I keep finding in Bolt.new apps before they go live

What I keep finding in Bolt.new apps before they go live

Bolt.new Security: The Most Common Vulnerabilities in Bolt-Built Apps

Bolt.new Security: The Most Common Vulnerabilities in Bolt-Built Apps

A while back I published a breakdown of what I consistently find across Lovable built apps, and it generated a fair amount of conversation from founders who recognized the patterns in their own builds. Bolt.new is a different platform with a meaningfully different output structure, and the findings that come up most consistently are worth documenting separately, because while some of the themes overlap, the specific ways things go wrong are distinct enough that a Lovable post doesn’t really cover you if you’ve built on Bolt.

That said, the important framing here: Bolt the platform is not insecure. StackBlitz runs the build environment in a sandboxed WebContainer, which means generated code can’t escape the browser tab during development. The risk is what gets exported and deployed, and that part is where the same patterns keep showing up across the apps I look at.


1. API keys hardcoded directly into the generated source

This is the most consistently present finding in Bolt-generated apps and also the one with the most immediate real-world consequences. When you ask Bolt to integrate with OpenAI, Stripe, or any other external service, it has a well-documented habit of writing the API key directly into the source file rather than referencing an environment variable. Sometimes this ends up in a client component, sometimes in a server function, and sometimes both.

The specific mechanism that makes this worse on Bolt than on some other platforms is the VITE_ prefix convention. Variables prefixed with VITE_ in a Vite-based project are explicitly bundled into the client-side JavaScript, meaning they ship to the browser by design. A secret key that should stay server-side but gets referenced as VITE_OPENAI_KEY is now publicly accessible to anyone with DevTools. Bolt-generated code sometimes makes exactly this mistake, treating VITE_ variables as a general-purpose way to share configuration rather than understanding that the prefix specifically marks something as public.

There’s also the git history problem worth knowing about. If a key makes it into a commit, rotating the key in your provider’s dashboard doesn’t remove it from the repository history. Anyone who clones or forks the repo can retrieve the old key from earlier commits. Squashing commits doesn’t reliably fix this either. The key needs to be treated as permanently compromised once it’s been committed, regardless of what you do after the fact.

One documented incident: a founder shipped a Bolt app with an OpenAI key hardcoded in the frontend. By the following morning, the key had been scraped and their bill had increased by hundreds of dollars overnight. This is not a theoretical risk, there are automated bots scanning deployed apps and public repositories for known credential patterns continuously.


2. Source maps left enabled in production

This one is fairly specific to Bolt and the Vite-based stack it commonly generates, and it’s worth covering because most founders have no idea what source maps are or that they’re a problem.

When a JavaScript application gets built for production, the code gets minified and bundled, which makes it harder to read. Source maps are files that map the minified output back to the original source code, primarily so that error stack traces in production make sense during debugging. The problem is that when source maps are included in the production build and deployed alongside the app, they effectively publish your entire original source code publicly. Business logic, API endpoint paths, internal comments, all of it becomes readable to anyone who knows to look in the browser’s Sources tab.

Bolt-generated apps frequently ship with source maps enabled by default because that’s a convenient development setting and the tool doesn’t distinguish between development and production configurations when generating the initial setup. The fix is a single line in the build configuration: setting productionBrowserSourceMaps to false. Worth checking before any deployment.


3. Auth checks in the frontend, not enforced server-side

Bolt generates full-stack apps, which means it produces both frontend components and backend API routes or Netlify functions. The issue that comes up consistently is that the authentication logic ends up in the frontend, where it controls what the UI shows and which buttons are visible, but the backend routes themselves don’t verify authentication independently.

What this means in practice is that an attacker doesn’t need to log in, they just need to call the API endpoint directly. The UI won’t let them click a button that isn’t there, but the endpoint the button calls will happily respond to a request that comes in without any credentials at all. This is sometimes called client-side-only authorization, and it’s a pattern that Bolt-generated route files produce fairly consistently, occasionally with the auth check written as a comment in the generated code but not actually implemented.

Every backend route or function that handles user data needs an explicit auth check at the server level, regardless of what the frontend does. The frontend check is a UX decision. The server-side check is the actual security control.


4. No RLS on Supabase tables

Bolt frequently pairs with Supabase as the backend database, and the same RLS issue I’ve written about extensively in the context of Lovable shows up here in the same form. Bolt’s code generation prioritizes getting features working quickly, which means RLS policies on database tables are almost never configured as part of the initial generation. The tables exist, the data flows correctly, and the app functions as intended during development. What isn’t set up is the access control layer that prevents authenticated users from reading or modifying data that belongs to other users.

This is covered in depth in the Supabase-specific posts, so I won’t go over the full picture here. The short version: if you’ve built on Bolt with a Supabase backend, check every table for RLS status before going live. The check takes about two minutes and the consequence of skipping it is the entire database being readable and writable by any authenticated user on your platform.


5. Hallucinated packages with real names

This one is worth knowing about specifically in the context of Bolt because the platform generates dependency choices on your behalf, and AI tools have a documented pattern of suggesting packages that don’t exist on npm or that have known CVEs. The attack vector this opens is called slopsquatting: attackers monitor the package names that AI tools commonly hallucinate and register malicious packages under those names before developers install them.

Georgia Tech’s Vibe Security Radar found that approximately 20% of AI-generated code samples reference packages that don’t exist, which is a predictable enough hallucination pattern that it’s being actively exploited. A malicious package that installs successfully and runs without errors but silently exfiltrates your environment variables during installation is not a theoretical attack, there are documented examples of exactly this from 2024 and 2025.

Running npm audit after every dependency addition and verifying that packages Bolt suggests actually exist and have reasonable download counts on npm is a simple enough step that’s worth making a habit.


The pattern that ties all of this together

Bolt is optimized for speed of iteration, which is exactly what makes it useful. The security issues that come out of it are the direct consequence of that optimization: it generates code that works, gets features in front of users fast, and handles the happy path well. What it doesn’t do is think adversarially about what happens when someone uses the app in a way it wasn’t designed for.

One thing worth knowing specifically: Bolt’s self-repair feature, where you ask it to audit and fix its own output, tends to add patches on top of patches rather than addressing root causes. A NetSPI experiment tested this exact workflow and found that after Bolt self-audited and applied its own suggested fixes, a subsequent penetration test still found remaining vulnerabilities that the AI review had missed entirely. The AI catches surface level syntax issues reasonably well. It misses architectural problems like wrong trust boundaries and missing access controls.

That gap, between what the tool can self-identify and what actually needs to be fixed before launch, is worth planning for rather than assuming away.


If you’ve built on Bolt and want someone to go through the actual deployed app before it goes live, that’s what a HollowByte pre-launch audit covers.

Back to blog

(function() { function applyMainRole() { var hero = document.getElementById('hero'); if (!hero) return false; var node = hero; while (node.parentElement && node.parentElement !== document.body) { node = node.parentElement; } if (node && node.parentElement === document.body) { node.setAttribute('role', 'main'); return true; } return false; } if (applyMainRole()) return; var attempts = 0; var interval = setInterval(function() { attempts++; if (applyMainRole() || attempts > 20) { clearInterval(interval); } }, 250); })();