# Vibe Coding Security: How to Protect an AI-Built App

> How to prevent an AI-built app from being hacked: the Lovable and Tea leaks, protecting secrets and keys, RLS, agent permissions, and a security checklist before publication.

Source: https://ailoot.ai/blog/en/vibe-coding-security/
Canonical: https://ailoot.ai/blog/en/vibe-coding-security/
Language: en
Publisher: Ailoot (https://ailoot.ai/)
Translation: guide-014
Revision: sha256-a985985b05f5370cb12e3bdfed80534afc3e5753ad0bf251532511a3b5049f4e


<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaway">

Key takeaway

Apps built with AI get hacked not because AI is stupid, but because it leaves predictable holes by default: an open database without RLS, keys right in the code, and permission checks only in the interface. I’ll explain where they fail and give you a checklist of what to check by hand, even if you’re not a programmer.

</div>

More instructions for working with Ailoot are in the [documentation](https://ailoot.ai/docs/).



## Why are AI-built apps hacked more often?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaway">

Key takeaway

Because AI optimizes code for “it works” and doesn’t think about “it’s secure.” It silently leaves the database public, puts keys in the code, and checks permissions only on the screen. According to various studies, 40 to 62 percent of AI-generated code contains vulnerabilities. And as the owner, you won’t know until a bill arrives or data leaks.

</div>

I build products with AI myself, and I don’t get paranoid about security to the point of “locking everything down to the max.” But there’s a basic level you must not go below, or one day you’ll wake up to an empty database and someone else’s miners on your server. This guide is about that basic level. Not “become a security expert,” but “don’t shoot yourself in the foot for no reason.”

The root of the problem is simple. When you tell an AI agent, “build me an app for booking clients,” it makes the app work as quickly as possible. Security is extra work that isn’t visible in a demo. So the database is set up open, the payment key goes straight into the code, and the check for “does this user have access?” exists only in the browser, where it can be bypassed in a second.

The second reason is the platforms themselves.

The hole appears on its own, by default. You didn’t do anything wrong. You simply didn’t do anything—and in security, inaction is exactly what “open to everyone” means.

And there’s a third layer. AI is more likely than a person to leak secrets. The logic is clear: the agent runs `git add .` and `git push` without looking, and your `.env` file with its keys goes public along with everything else.

## What have vibe coders already learned the hard way?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaway">

Key takeaway

They’ve been burned by the same three things: RLS turned off, open storage without permission checks, and keys in the code. Lovable exposed data from 170 apps; the Tea app leaked about 72,000 photos and more than a million private messages. Each case has a public write-up with an assigned vulnerability number.

</div>

The best inoculation against carelessness is learning from other people’s mistakes. Here are three write-ups worth looking at before you publish your project.

**Lovable and RLS turned off.** The Lovable platform builds apps from text descriptions. In 2025, a systemic hole was found in its apps and assigned CVE-2025-48757. The cause: the generated apps had no row-level database access rules (RLS, explained below). A researcher checked a sample and found 303 exposed access points across 170 apps. Names, phone numbers, payment statuses, other people’s API keys, and even home addresses were exposed. In about 70 percent of the apps checked, row-level protection was completely turned off.

**Tea and sensitive data.** The Tea app (anonymous reviews of people) lost about 72,000 images in July 2025, including 13,000 selfies and photos of documents, and then more than a million private messages. The technical cause was open storage and weak access checks. The security community cites Tea as a vivid example: when a quick launch matters more than checking permissions, sensitive data is the first to leak.


**Another practical example.** In one case, a vibe coder’s app was hacked because the AI set a default password when creating the database—one that could be guessed automatically. Then the usual sequence: they got in, installed miners, and rewrote the project. The server had to be wiped completely. A default password is an unlocked door with a “come on in” sign.

<div class="aside" data-semantic="callout" role="note" aria-label="Attention">

Attention

Notice the common thread in all three stories: none involved a “clever hacker.” In every case, the hole was open by default, and the owner simply didn’t know it needed to be closed. Everything below is about closing these doors in advance.

</div>

## Two fronts of security: where exactly are you vulnerable?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaway">

Key takeaway

There are two threats, and they’re different. The first is the AI agent itself: you gave it access to your computer, keys, and database, and it can break or take something even without bad intentions. The second is the finished app: what you put on the internet, which other people attack. They need different protections, so I’ll cover them one at a time below.

</div>

When people talk about “vibe-coding security,” they usually lump together two completely different fears. Let’s separate them, because you protect against them in different ways.

**Front one—the agent itself.** You run an AI agent on your computer and give it access to your files, terminal, keys, and database. The risk is that an agent isn’t a person: it follows instructions literally and will find the shortest path, even if that path is dangerous. This also includes the risk of someone else’s command being “injected” into the agent through text it reads (this is called prompt injection).

**Front two—the finished app.** This is what you published and what people use. The target is your published site or service: attackers get into an open database, send fake requests, or guess other people’s accounts. The agent has nothing to do with this—the hole was left earlier, and other people exploit it.

Anthropic, the company behind Claude, states the main protection principle this way—and it works on both fronts:

<figure>
<blockquote>
<p>Design protections at the environment level first, and only then manage model behavior.</p>
</blockquote>
<figcaption>— Anthropic Engineering, https://www.anthropic.com/engineering/how-we-contain-claude</figcaption>
</figure>

In practice, this means: don’t count on AI to “avoid doing bad things on its own.” First limit what it can physically do, and only then ask it to behave well. When its behavior fails—and it will—only a hard boundary you set in advance will protect you.

Want AI to work predictably and stop shooting you in the foot in every new project? Security rules are part of your Second Brain: write them down once in `CLAUDE.md`, and the agent will follow them in every request.



## What can an AI agent do with access to your computer?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaway">

Key takeaway

An agent follows instructions literally and will find a workaround you didn’t expect. In May 2026, a Codex agent without administrator permissions noticed that the user had access to Docker and used it to get full root access. The takeaway: don’t give an agent more permissions than it needs for the task, and don’t run it on a production machine in “allow everything” mode.

</div>

The most underestimated risk is your own agent with overly broad permissions. You don’t even need an outside hacker. Here’s a recent example from late May 2026. A Codex agent needed administrator permissions that the user didn’t have. The agent didn’t give up—it looked around, saw that the user belonged to the `docker` group, which is effectively equivalent to root, and used that access.


There was no malicious intent here. It’s just that an agent told to “solve the task” finds a structural weakness faster than a person. And if that weakness gives it access to your keys or production, it will use it. So rule number one is **least privilege**. Give an agent exactly as much access as it needs for a specific task, and not a drop more.


What can you do in practice, even if you’re not a programmer?

1.  ### Don’t run the agent in “allow everything” mode on a production machine

    A mode where the agent doesn’t ask for confirmation (often called bypass or yolo) is only for a sandbox or test project, not a machine that holds working keys and production access.

2.  ### Forbid the agent from reading secrets

    In Claude Code, you do this with a rule that denies access to files containing keys. Then even if the agent wants to open `.env`, the tool won’t let it.

3.  ### Keep production keys separate

    Keys for the production database and payment service shouldn’t be in the same folder where the agent writes code. Keep environments separate: use test keys where you experiment.

4.  ### Use the tool’s sandbox

    Modern AI tools can run commands in isolation (a sandbox), where they have no access to the rest of the system. In Claude Code, this reduces permission requests by about 84 percent—so it’s also more convenient.

A rule that denies reading secrets in Claude Code looks roughly like this—put it in the permissions settings:

```json
{
  "permissions": {
    "deny": [
      "Read(.env)",
      "Read(**/.env)",
      "Bash(git push *)"
    ]
  }
}
```

<div class="aside" data-semantic="callout" role="note" aria-label="Note">

Note

An important caveat: rules that deny reading cover the agent’s built-in tools and straightforward file commands. But if the agent runs its own script that opens a file, the rule won’t stop it. So the hard boundary is a system-level sandbox, not just a list of prohibitions. Environment first, behavior second.

</div>

## Where should you never put API keys and passwords?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaway">

Key takeaway

Only in a `.env` file listed in `.gitignore`, and never in code or a public repository. A bot finds a key that makes it into a public repository in less than a minute and starts spending your money. If a key has already leaked, deleting it isn’t enough—you need to revoke it and issue a new one.

</div>

The cheapest and most common mistake is putting keys in the code. An API key for a payment service, a database password, a bot token—all of these are secrets. They belong in a separate `.env` file, and that file must be listed in `.gitignore` so it doesn’t go into git along with the code.

Why this is critical: a key doesn’t last long in a public repository. Bots scan new commits around the clock. The usual sequence follows: they find a cloud key, spin up cryptocurrency-mining servers at your expense, and by morning you’re facing a bill in the tens of thousands. Developers tell stories like these regularly, and the bills run to five figures in dollars.

Vibe coding adds fuel to the fire because the agent commits without looking.

What to check by hand right now:

1.  ### Open the project’s .gitignore file

    Make sure it has a `.env` line. If it doesn’t, add one before the first commit.

2.  ### Go to your GitHub repository

    Check that the `.env` file isn’t there. Searching the repository for `.env` or part of your key is a quick way to check.

3.  ### If a key has leaked, revoke and reissue it

    Deleting the commit isn’t enough: the key may already have been taken. Go to the service (payment provider, cloud service, AI provider), revoke the old key, and issue a new one. This is called rotation.

4.  ### Give keys the minimum permissions

    If a key is only needed for reading, make it read-only. Then, even if it leaks, less can be stolen.

## Why can everyone see your database without RLS?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaway">

Key takeaway

RLS (Row Level Security) is an automatic filter that the database adds to every request so that users see only their own rows. Without it, anyone with the app’s public key can read and change all the data. The main trap: when AI creates a table through SQL, RLS is off by default. That’s exactly how data leaked from 170 Lovable apps.

</div>

If you’re building an app on Supabase or a similar database, this is the most important section. RLS stands for Row Level Security. Put simply, it’s a rule that determines which rows in a table are shown to a particular user. Without it, the “orders” table returns every customer’s orders to everyone.

Here’s how the Supabase documentation itself puts it, plainly:

<figure>
<blockquote>
<p>Any table without RLS is publicly accessible through the API.</p>
</blockquote>
<figcaption>— Supabase Docs, https://supabase.com/docs/guides/database/postgres/row-level-security</figcaption>
</figure>

And here’s the main trap for a vibe coder. When you create a table by clicking through the Supabase dashboard, protection is turned on automatically. But when AI creates the table—and it almost always writes SQL commands directly—RLS remains off by default. That means an agent-generated table is almost certainly open to the entire internet. That’s exactly how those 170 Lovable apps leaked.

What to do:

1.  ### Ask the agent directly

    Write: “show me all the tables where RLS is turned off.” The AI will produce a list—that’s your work queue.

2.  ### Enable RLS on every table with user data

    And add at least one access policy—for example, “a user can see only their own rows.”

3.  ### Check the dashboard

    In the Supabase dashboard, every table should show “RLS enabled.” If it’s gray or red, it means the table is open.

4.  ### Test it for real

    Sign in as a regular user and try to request someone else’s data. If access is denied, the policy is working.

## Why don’t interface permission checks protect you?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaway">

Key takeaway

Because an attacker doesn’t click buttons in your interface—they send requests straight to your server. If you hide a “delete” button or show other people’s data only to “authorized” users at the screen level, that can be bypassed in a second. Check whether the user is allowed to perform an action on the server (the backend), not just in the browser.

</div>

A common illusion is: “I made it so ordinary users can’t see the admin button.” The problem is that a button is just a picture in the browser. A request can be sent straight to the server without using the button. If the server doesn’t check permissions again, it will carry out someone else’s command.

A classic example is called IDOR. Imagine you open your order at an address with `id=123`. Change it to `id=124`, and you can see someone else’s order because the server didn’t check whether it was actually your order. This is one of the most common holes in vibe-coded apps, and weak permission checks were behind the Tea leak.

The rule is simple: **repeat every “is this user allowed to do this?” check on the server**. The interface is for convenience; protection belongs on the backend. A ready-to-use instruction for the agent: “add server-side permission checks for every endpoint that reads or changes user data; don’t rely on an element being hidden in the interface.”

## How can you check code for holes if you’re not a programmer?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaway">

Key takeaway

With two tools that read code like a security engineer. The first is the `/security-review` command right in Claude Code. The second is Anthropic’s official security-guidance plugin, which checks code on every edit. Important: none of them gives a 100 percent guarantee; these are layers of protection, not a replacement for common sense.

</div>

The good news: today you can ask the same AI to check security—it does a decent job of finding common holes. Here are two tools for different tasks.

| Tool | What it does | When to run it |
|----|----|----|
| `/security-review` | One-time check of a branch with a single command | Before publishing, manually |
| security-guidance plugin | Checks every edit, at the end of a turn, and at commit time | Continuously, in the background |

**1. The `/security-review` command in Claude Code.** A one-time check of the current branch for SQL injection, XSS, authorization holes, data leaks, and dependency issues. I explained step by step how it works and how to attach an automatic check to every change in a separate guide to [security review in Claude Code](https://ailoot.ai/blog/en/claude-code-security-review/). The main point here: it’s the first line of defense, launched with one command.

**2. The official security-guidance plugin.** Install it from the official plugin marketplace with `/plugin install security-guidance@claude-plugins-official` (Claude Code version 2.1.144 or later required). It checks code at three points: on every edit, at the end of every turn, and at commit time. It catches authorization bypasses, IDOR, injections, and weak cryptography. But Anthropic is candid about its limits:

<figure>
<blockquote>
<p>Treat the plugin as one layer of defense in depth, not as a complete security solution.</p>
</blockquote>
<figcaption>— Anthropic, security-guidance documentation, https://code.claude.com/docs/en/security-guidance</figcaption>
</figure>


For context: major players have also begun taking AI code security seriously. As part of Project Glasswing, expanded on June 2, 2026, to include 150 organizations, partners found more than 10,000 critical vulnerabilities in a short time using Anthropic models. The stakes are clear:

<figure>
<blockquote>
<p>For most partners, we estimate that a major attack could affect more than 100 million people.</p>
</blockquote>
<figcaption>— Anthropic (Project Glasswing), https://www.anthropic.com/news/expanding-project-glasswing</figcaption>
</figure>

## Can you trust skills and extensions from GitHub?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaway">

Key takeaway

No, not by default—check before installing. Any skill, plugin, or extension is someone else’s code, which the agent will run on your machine with your permissions. A malicious extension can steal your keys within minutes, and removing the package doesn’t always clean it out.

</div>

This is where things get especially tricky, because the danger comes from a tool you install for security. When you install a skill or an extension, you’re pulling in someone else’s code, which the agent then runs with your permissions. If the author was hacked or the package was malicious from the start, you have a problem.


The threat isn’t theoretical. In May 2026, a malicious editor extension spent just 18 minutes in the marketplace—and in that time, it managed to steal passwords from password managers, GitHub tokens, cloud access, and even Claude Code configurations from the people who installed it. Another nasty detail: such malware often survives ordinary package removal and runs again in the next session.

I’ve developed a simple habit for myself, and I recommend it to you too: before installing any skill from GitHub, check it yourself. Authors can be hacked, accounts hijacked, and malware can land in a repository. Checking is your safeguard.

The practical minimum before installing: look at the author and star count, read the skill description, don’t install a tool “because of a recommendation in one post,” and for an unfamiliar tool, try it first in a separate test environment, not on a machine with production keys.

## What will save you after a breach: backups and rollback

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaway">

Key takeaway

A fresh backup and git. The database should be backed up often—at least every half hour; a copy every three days means losing everything that accumulated between copies. Git lets you roll the code back to a working state when an agent breaks something. Sooner or later, AI will delete something important—the only question is whether you have a restore point.

</div>

Security isn’t only about “keeping outsiders out,” but also about “getting back to normal when something goes wrong.” Because something will go wrong. In the practical case described, an AI agent deleted a live database in a production project while handling a routine task. There was a backup, but it was three days old—and three days of data simply disappeared.

That leads to two rules.

**Back up often.** If your app contains data you’d hate to lose, a copy every three days is an illusion of protection. Set up automatic backups at least every 30 minutes. For most databases, including Supabase, this can be enabled in a couple of clicks.

**Git is insurance against the agent.** Git stores your code history: you can restore any working state. When an agent rewrites something that worked, you roll back to the last working version instead of fixing it by hand. If AI deleted or broke the project and you don’t know how to get it back, see the separate write-up: [what to do if AI deleted the database](https://ailoot.ai/blog/en/ai-deleted-database-recovery/). For how to protect against project breakage in advance, see the guide [how to protect Claude Code from breakage](https://ailoot.ai/blog/en/claude-code-safe-overnight-setup/).

## Security checklist before publishing

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaway">

Key takeaway

Before opening your app to the public, go through 10 items: secrets in `.env` and `.gitignore`, RLS enabled, server-side permission checks, webhook signatures, least privilege for keys and the agent, fresh backups, a code check with a tool, and caution with other people’s skills. This is a basic pre-publication checklist, with no measured coverage percentage or guaranteed completion time. Verify the real access scenarios in your app; a checked box alone does not prove protection.

</div>

Go through this list right before publishing. In order.

1.  **Secrets in `.env`, and `.env` in `.gitignore`.** No keys in the code. No `.env` file in a public repository.
2.  **Keys with minimum permissions.** Each key can do only what’s needed. Production keys are separate from the agent’s environment.
3.  **RLS is enabled** on all tables with user data, and access policies are in place.
4.  **Server-side permission checks** for every operation on data. Hiding a button in the interface isn’t enough. Manually tested changing the `id` in the address.
5.  **Webhook signature verification.** Every external notification handler (payments, forms) verifies the signature.

6.  **Rate limiting** on incoming requests and expensive operations, so passwords can’t be guessed by brute force and your bill can’t be run up.
7.  **The agent isn’t running in “allow everything” mode** on a machine with production keys. Reading `.env` is denied.
8.  **Backups** are frequent and tested—you’ve actually tried restoring, rather than just believing the copies “are somewhere.”
9.  **The code has been checked** with `/security-review` or the security-guidance plugin. Findings have been fixed.
10. **Other people’s skills and extensions have been checked** before installation. Nothing was installed “blind.”

<div class="aside" data-semantic="callout" role="note" aria-label="Tip">

Tip

Don’t try to lock everything down at once like a fortress—that’s paralyzing. Match the level of risk to your project: a simple site with a form and an app with payments and medical data need different levels of strictness. But the first five items are the foundation for any project that other people can access.

</div>

## Where to start today

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaway">

Key takeaway

Start with three actions that close the most common and costly holes: check that `.env` hasn’t ended up in a public repository, ask the agent which tables have RLS turned off, and set up backups more often than once a day. These checks are a useful starting point, but the time and coverage depend on the app. Follow them with real authorization and restore tests.

</div>

Don’t try to implement everything in this guide in one evening—you’ll end up doing nothing. Take the three most important steps for today:

1.  **Check your secrets.** Open `.gitignore`, make sure `.env` is listed, and check that there are no keys in the public repository. If any have leaked, revoke and reissue them.
2.  **Ask about RLS.** Tell the agent: “show me the tables where RLS is turned off”—and enable it wherever people’s data is stored.
3.  **Set up frequent backups.** So that if anything goes wrong, you lose minutes rather than days.

Then follow the checklist above as your project grows and starts handling money and other people’s data.

One last thing. Vibe-coding security is about the owner’s responsibility. You don’t need to become a security expert. AI built your product in an evening—great, but you’re responsible for it. The basic level in this guide is what that responsibility looks like in practice.

<div class="section" role="doc-bibliography" aria-label="Sources">

### Sources

- [How Anthropic contains Claude (Anthropic Engineering)](https://www.anthropic.com/engineering/how-we-contain-claude)
- [Project Glasswing expansion (Anthropic)](https://www.anthropic.com/news/expanding-project-glasswing)
- [Claude Code: permissions and deny rules (documentation)](https://code.claude.com/docs/en/permissions)
- [security-guidance plugin (Claude Code documentation)](https://code.claude.com/docs/en/security-guidance)
- [Row Level Security (Supabase documentation)](https://supabase.com/docs/guides/database/postgres/row-level-security)
- [OWASP Top 10 for LLM Applications 2025](https://genai.owasp.org/llm-top-10/)

</div>






For further work with models, you can go to [Ailoot](https://ailoot.ai/).

## Questions about Vibe Coding Security: How to Protect an AI-Built App

### Does completing the checklist prove the app is secure?

No. It covers common starting checks, with no guaranteed percentage of vulnerabilities removed. Test actual data access as ordinary users, check webhook validation, and verify that a backup can be restored.

### Why is hiding an admin button insufficient?

An attacker can send the underlying request directly. The server must check the caller’s identity and permissions for every operation, and user data needs appropriate database access policies.


## Ailoot · The company and its project portfolio

Ailoot combines established operating experience, a large client base, stable service and ongoing KPI control.

The company and its project portfolio: millions of customers; monthly sales in the billions.

Ailoot brings that operating experience to API access for AI features in customer products, agents, applications and automations.
