Help

Securing vibe-coded apps

The gaps apps built with Lovable, Bolt, Cursor, Claude Code, Codex, v0 or Replit often have, what Hullwise finds and what to do right away when a key is exposed.

With Lovable, Bolt, v0 or Replit, a business team can build a customer portal or an internal tool in an afternoon. With Cursor, Claude Code or Codex, development teams move just as fast. The app works. Whether it leaks data or keys is often something nobody has checked. Left open, customer data becomes readable for anyone, or outsiders use your keys and you pay the bill. An oversight then quickly turns into a reportable data breach.

Responsibility lies with the company that runs the app. Lovable took that position itself in 2025, when a vulnerability in many Lovable apps became public (CVE-2025-48757): each customer is responsible for the data in their app.

Typical gaps

  • Keys in browser code. Access keys easily end up in variables such as VITE_ or NEXT_PUBLIC_. At build time they are written into the JavaScript files every visitor downloads. In 2025, Escape examined more than 5,600 publicly reachable vibe-coded apps and found more than 400 exposed keys, including Supabase master keys (Escape, October 2025).
  • Databases without access rules. Many of these apps talk to their Supabase or Firebase database directly from the browser. If there are no rules for who may read which row, anyone can fetch all the data. On the Moltbook platform, Wiz found such a key in the JavaScript and behind it an open database with 1.5 million API tokens and 35,000 email addresses (Wiz, February 2026).
  • AI calls straight from the browser. To make a chat or a summary feature work, the OpenAI or Anthropic key sits in the frontend. Any visitor can copy it and use it at your expense.
  • Missing basics. Browser security headers, readable original source code through source maps and outdated libraries usually stay the way the tool generated them.
  • Permissions only in the interface. A hidden button is no substitute for a check on the server. Anyone who calls the API directly gets around it.

What Hullwise finds

The check reads your homepage and the JavaScript files it includes from your domain, the same way every browser loads them, for Vite apps also the parts loaded when switching pages. In apps built with Vite or Next.js, that is where keys end up. We don’t probe paths and we don’t use anything we find. The limits are described in What we don’t check.

RiskDoes the check find it?
Secret keys in the HTML or JavaScript files, for example for OpenAI, Anthropic, Stripe, AWS or GitHubYes, with type, where it was found and a shortened value. We never show the full value.
Supabase master key (service_role or sb_secret) in browser codeYes. We read the role stored in the key.
AI API called directly from the browserYes
Variables with SECRET, PRIVATE or PASSWORD in the name in public codeYes
Supabase database reachable with the public keyYes, as a notice: check your access rules
Tables without access rules (Row Level Security)Not from the outside. With our self-check you verify it yourself in a minute.
Firebase configuration in the browser codeYes, as a notice: check the Security Rules
Readable original source code through source mapsYes
Missing security headers, certificate, email protection, outdated librariesYes
Login, roles and permissions, payment flow, everything behind the loginNo, only in the audit
Keys in the Git repository or in a chat history with the assistantNo, we check the running app

The result also shows which platform we detected, such as Lovable, Bolt, v0, Replit, Supabase or Firebase. Apps built with Cursor, Claude Code or Codex carry no trace of the tool. For those we detect host and framework, such as Vercel and Next.js. If an app runs at an address like name.lovable.app, the email protection result applies to the platform’s domain. Check your company domain separately.

A key is exposed: first steps

Assume the key has already been copied. According to GitGuardian, 64% of the keys confirmed as valid in 2022 were still not revoked in 2026 (State of Secrets Sprawl 2026).

  1. Revoke. Revoke the key with the provider right away, before you change the code. Deleting it from the code is not enough. It still lives in old files, copies and the Git history.
  2. Create a new one. Create a new key with the provider, with as few permissions as possible.
  3. Use it on the server only. The new key belongs in a server function and in your host’s protected settings, for example a Supabase Edge Function, a Vercel or Netlify function, or an API route in Next.js. It must not carry a prefix like VITE_ or NEXT_PUBLIC_.
  4. Check the logs. Find out whether and how the old key was used: usage and costs at OpenAI or Anthropic, API logs in Supabase, events in Stripe, CloudTrail on AWS.
  5. Check your reporting duties. If someone could use the key to access personal data, that can be a personal data breach, which you report to the supervisory authority under GDPR Art. 33 (72 hours). Entities covered by NIS2 send an initial report to Germany’s Federal Office for Information Security (BSI) within 24 hours of a significant security incident (NIS2 / German BSIG § 32). Your data protection officer or legal counsel can tell you whether a duty applies.
  6. Redeploy and check again. Then run a new check. The finding has to be gone.

In Lovable or Bolt you can let the assistant do the rework, for example like this:

Prompt for the assistant
Move all OpenAI calls into a Supabase Edge Function. The API key lives only there as a secret, not in the frontend and not in a VITE_ variable.

Still review the result: afterwards the key must not appear in any file the browser loads.

Access rules in Supabase (RLS)

Supabase gives every app two kinds of keys. The public one (anon or sb_publishable) may sit in the browser. It only protects your data, though, if access rules are active on every table. These rules are called Row Level Security. Without them, anyone with this key can read every row and, depending on the settings, change them too. The secret key (service_role or sb_secret) bypasses all rules and never belongs in the browser.

The 2025 vulnerability in Lovable apps shows how often this goes wrong: a researcher examined 1,645 published apps, and 170 of them had open tables (Matt Palmer, CVE-2025-48757). A check from the outside can’t see this gap. We only report that the app talks to its database directly.

Self-check in one minute

Open the SQL Editor in your Supabase project, paste this query and click Run. It only reads and changes nothing. Every row in the result is a table without access rules or a policy that allows every visitor everything. If the result is empty, we found nothing. Whether your rules fit in substance, for example showing only a user’s own rows, the query can’t tell.

SQL · Supabase SQL Editor
-- Hullwise self-check: read-only, changes nothing. Empty result = nothing found.
select c.relname as table_name,
       'Row Level Security is off' as issue
from pg_class c
join pg_namespace n on n.oid = c.relnamespace
where n.nspname = 'public' and c.relkind in ('r', 'p') and not c.relrowsecurity
union all
select p.tablename,
       'Policy "' || p.policyname || '" allows ' || p.cmd || ' for everyone without a condition'
from pg_policies p
where p.schemaname = 'public'
  and (p.roles && array['public', 'anon']::name[])
  and (p.qual = 'true' or p.with_check = 'true')
order by 1;
  1. Open the Table Editor in the Supabase dashboard. Every table with real data must have RLS enabled. The Security Advisor in the dashboard also lists tables without RLS.
  2. Enable RLS and define for each table who may read and write which rows.
  3. Test while logged out: without a matching rule, the app must no longer show other people’s data.
SQL · Supabase
alter table public.orders enable row level security;

create policy "Customers read only their own orders"
  on public.orders for select
  using (auth.uid() = customer_id);

Whether the rules are complete is covered by the audit for €1,500 per application, after you have proven that you own the domain.

Security Rules in Firebase

With Firebase, the configuration with apiKey, authDomain and projectId is always in the browser code. That is by design, the key in it is not a secret. Who may read and write what is governed only by the Security Rules of Firestore, Realtime Database and Storage. If test mode or a rule such as allow read, write: if true is still in place, anyone with the configuration from your code can read and change all data. We can’t see the rules from the outside, so we only report a notice.

  1. In the Firebase console, open the Rules tab for Firestore, Realtime Database and Storage.
  2. Allow access only to signed-in users and only to their own data, for example with request.auth.uid.
  3. Test changes first in the console’s Rules Playground.
  4. Restrict the API key in the Google Cloud Console to your domain and the Firebase services.

Rules for your team

According to Bitkom, only 23% of companies have set rules for using generative AI (Bitkom, October 2025). For apps built with AI, a few rules go a long way:

  1. Every app that processes customer or company data is reported to IT or the information security officer before it goes live.
  2. Apps are built with company accounts, not personal ones. The German BSI and the French ANSSI also recommend this for AI coding assistants (BSI, October 2024).
  3. Real keys never go into a prompt, a chat or a variable with VITE_ or NEXT_PUBLIC_. They live only in the host’s protected settings.
  4. Every AI key has a spending limit set with the provider.
  5. RLS is enabled on all tables before the first real record goes in.
  6. Generated code is read and tested before it goes live. After that, every app goes through a check.

How to check your apps

The free check on the homepage gives you a first overview. The Business plan checks your apps every week and emails you as soon as a grade gets worse. The history serves as evidence of regular reviews. More under Pricing.

Hullwise checks the running app, not your repository. Keys in the Git history are found by a secret scanner in the repository, for example the one your Git provider offers. If you work with Cursor, Claude Code or Codex, turn it on: according to GitGuardian, commits made with the help of Claude Code contained a secret in about 3.2% of cases in 2025, roughly twice the average (State of Secrets Sprawl 2026).

Still have questions? Email us.

Is your app
watertight?

Hullwise finds the leaks before someone else does: exposed keys, database credentials and outdated software. Every finding comes with steps to fix it.

https://

Click a finding to mark it as fixed.

See pricingHow a scan works

Ship fast. Stay hullwise.