Forge
AI-built apps

What AI-built apps get wrong, and how to check yours

AI builders like Lovable, Bolt, v0 and Replit, and coding agents like Cursor and Claude Code, ship working apps in hours. But usually nobody reviews the code they write. The gaps that come up again and again are open database tables, secret keys in the browser, pages search engines can't read, and code that gets harder to change with every prompt.

Last reviewed by the Forge team

What goes wrong

What it costs when this is missed

Critical

Data open to anyone

A Supabase or Firebase table without access rules can be read or changed by anyone holding the public key that ships with every page.

Critical

Secret keys in the browser

Anything the frontend can use, a visitor can copy. An AI, Stripe or service key in client code is a bill or a breach waiting to happen.

High

Pages search engines can't read

A client-rendered page sends crawlers an empty shell. Titles, text and links only appear after JavaScript runs.

Medium

Code that fights every change

Each prompt adds code next to the old code instead of changing it. A few months in, every fix breaks something else.

Common problems

What to look for in your code

Each one: why it hurts, and how to spot it in your own project.

01Tables without row-level security

Supabase's anon key is public by design. Row-level security (RLS) is what stops one user from reading another user's rows. In 2025 researchers found tables open to the anon key in 170 of 1,645 apps from Lovable's public showcase (CVE-2025-48757). Lovable disputes the CVE, saying each app's owner is responsible for their data. Either way, the fix is yours to make.

How to spot it: In Supabase, check that every table in the public schema has RLS enabled and at least one policy. Then try to read another user's row using only the anon key.

Risky
create table profiles (
  id uuid primary key,
  email text,
  plan text
);
-- RLS never enabled: the anon key reads every row
Better
alter table profiles enable row level security;

create policy "read own profile" on profiles
  for select using ((select auth.uid()) = id);

02Secret keys in frontend code

Builders often call an API straight from the page. Any key used in code that runs in the browser ends up in the JavaScript every visitor downloads.

How to spot it: Search the code for sk_, sk-, service_role and dangerouslyAllowBrowser. Check that variables starting with VITE_ or NEXT_PUBLIC_ hold nothing secret.

Risky
const openai = new OpenAI({
  apiKey: import.meta.env.VITE_OPENAI_KEY,
  dangerouslyAllowBrowser: true,
});
Better
// the page calls your server; the key never leaves it
const res = await fetch("/api/summarise", {
  method: "POST",
  body: JSON.stringify({ text }),
});

03Permission checks only in the UI

Hiding a button isn't a permission check. If the API route or the database doesn't check who is calling, anyone can call it directly with the browser's dev tools.

How to spot it: For every write (update, delete, payment, invite), find where the server checks the user. If the only check is in a React component, the check is missing.

Risky
{user.isAdmin && <button onClick={() => deleteUser(id)}>Delete</button>}
// …and the delete endpoint checks nothing
Better
export async function deleteUser(id: string) {
  const me = await requireUser();
  if (!me.isAdmin) throw new Forbidden();
  await db.users.delete(id);
}

04Pages rendered only in the browser

Older Lovable projects (React + Vite), and many Bolt and hand-prompted Vite projects, are single-page apps: the HTML holds an empty root element. Lovable now server-renders new apps (TanStack Start, since May 2026) and pre-renders older ones for verified crawlers. Other crawlers and many SEO tools still get the empty shell.

How to spot it: Use curl or 'View page source' (not the dev tools, which show the page after JavaScript ran). If the title, main text and links aren't in it, your pages depend on JavaScript to be read.

Risky
<body>
  <div id="root"></div>
  <script type="module" src="/assets/index.js"></script>
</body>
Better
<body>
  <h1>Invoices for small teams</h1>
  <p>Send, track and get paid…</p>
  <a href="/pricing">Pricing</a>
</body>

05One title, no sitemap, no robots.txt

Generated apps often keep the starter's title on every page and ship without sitemap.xml or robots.txt, so search engines can't tell the pages apart or find them all.

How to spot it: Open /robots.txt and /sitemap.xml on your live site. Then check that every public page has its own title and description.

06The same thing built three times

Agents often write a new component or helper instead of finding the existing one. The copies drift apart, and a fix lands in only one of them.

How to spot it: Look for near-identical names (Button, Button2, NewButton), several date or price formatters, and repeated fetch calls to the same endpoint.

07Huge files and no tests

A 1,500-line page that does everything is hard to change, for you and for the next prompt. Without tests, nobody notices when a prompt breaks sign-up or checkout.

How to spot it: List your largest files. Check that login, payments and data writes each have at least one test that would fail if they broke.

08Packages nobody chose

Generated projects pick up unused or overlapping packages, and models sometimes suggest package names that don't exist. Attackers register those names ('slopsquatting').

How to spot it: Check that each package in package.json is imported somewhere, is the well-known package you expect, and is maintained.

Quick self-check

How does your project do?

Tick each one that's true today. Anything left unticked is worth a closer look.

Tick what's true for your project. 8 checks.

How Forge checks this

A second opinion on your code, from the Issues, SEO and Structure scans.

  • Reads the code your builder generated (synced to GitHub and cloned to your computer), not just the live page.
  • Checks routes, server actions and database access for missing permission checks, and the code for secrets (Issues scan).
  • Fetches your live pages and compares them with the code: titles, robots, sitemap, rendering (SEO scan).
  • Finds duplication, oversized files and dead code before they slow you down (Structure scan).

Questions

Are Lovable, Bolt or v0 bad for SEO?

Not in themselves. v0 builds Next.js apps, which render on the server by default. Lovable server-renders new apps (since May 2026) and pre-renders older ones for verified crawlers. Bolt and coding agents build whatever stack you ask for. What matters is the HTML your pages actually send: check it with 'View page source' or curl.

Is the Supabase anon key a secret?

No. It's meant to be public, which is exactly why every table needs row-level security. The service_role key is the secret one: it bypasses RLS and must never reach the browser.

Can't I just ask the AI to review its own code?

It helps, but the same model tends to repeat its own assumptions. A separate review that follows a fixed checklist and has to point at real lines of code catches what the author missed.

Do I need the code to check my app?

For a real check, yes. A look at the live site can't see database rules or server routes. Most builders can sync your project to GitHub; clone it and review the folder.

Related guides

All guides

Sources