Is your Windsurf app safe?
Is Windsurf safe for a live app? The generated code is usually fine. What slips through is the change nobody read, in a file nobody opened.

Logos are the property of their respective owners, shown to indicate compatibility.
In short
- A Windsurf app is usually safe on the code side. What slips through is the change nobody read, so check outcomes rather than diffs.
- Search your built output for service_role and sk_live_. That covers every file the assistant touched, whether or not you opened it.
- Row Level Security is the one check that survives code you never read, because every request has to pass the database.
Windsurf can change a lot of your project in one step: several files, a migration, a config, all at once and all working. The output is usually good. The problem is the review: a change spread across many files does not get read the way a change touching one does, and the two things worth catching are both one line long.
Here is the part that guide after guide gets wrong: the answer is not to read more carefully. At the speed these tools work, careful reading does not scale. What scales is checking the two outcomes that matter, from outside, after the fact.
The half of your app that is public no matter what
The whole front of it.
Think of it as a shop. Everything Windsurf builds into your interface is the shopfront, and the shopfront is handed in full to every visitor: a browser cannot display a page it was not sent. Behind it is the stockroom, your database, a separate building on the internet with its own address and its own lock.
The distinction matters because reviewing code tells you about intent, and intent is not what ships. What ships is the built output: one bundle of JavaScript, assembled from every edited file, handed to anyone who loads your site. That bundle is the thing worth inspecting, and it is much smaller to search than the source it came from.
Is a key in your Windsurf app's bundle a problem?
Usually not. It depends on which key, and the two look nearly identical.
A publishable key names your project and does nothing else:
sb_publishable_… in new Supabase projects, anon in older ones. It is meant
to be readable and finding it is not a finding. A secret key, sb_secret_…
or the older service_role, bypasses every rule you have written and reads and
writes every row in every table.
The way the second one arrives is worth knowing, because it is not carelessness.
An assistant asked to make a browser component talk to your database writes code
that works, and if that code needs a secret key, the key goes where the code
runs. Vite states outright that a VITE_-prefixed value is bundled into your
source at build time; Next.js does the same with NEXT_PUBLIC_. Nothing warns
you, because from the tool's point of view you asked for exactly this.
Reading which key you have
takes about a minute.
The one rule that survives code you have not read
Row Level Security, if your data is in Supabase: a per-table switch that decides row by row who may read what.
This is why it is the check worth doing. Every request reaches the database, whichever file made it and whoever wrote that file. A rule enforced there applies to all of them at once, including the code from the change you skimmed. No amount of unreviewed code changes what an unauthenticated request gets back.
Two things decide whether you have it. Supabase enables Row Level Security by default for tables created in the dashboard's Table Editor, and not for tables created by running SQL, which is how a generated migration creates them. And the switch is only half of it: a policy that permits everyone leaves the table open while the dashboard reports it as secured.
How to check your own Windsurf project in about ten minutes
Search the build, not the source. Build the project, then search the output
files for service_role, sb_secret_ and sk_live_. One command, and it
covers every file the assistant touched whether or not you opened it.
Read the prefixed variables. Anything named VITE_… or NEXT_PUBLIC_… is
published. For each, ask whether you would put it on your homepage.
Open Authentication → Policies in Supabase. Any table listed with Row Level Security disabled answers anyone holding your project address, and that address is in your app.
Then look from outside. The three checks above tell you what is configured. What a stranger can actually reach is a different question, and it is the one our free scan answers. It reads your live site as a visitor and gives you a grade in about 20 seconds, no account: scan your app.
What to do
- Review by outcome, not by diff. At this speed, reading every changed line is not a plan.
- The built bundle is what ships. Searching it is faster and more truthful than reading the source it came from.
- A publishable key in the browser is correct.
sb_secret_…andservice_roleare the ones to rotate. - An assistant puts a key where the code it wrote needs it. Keeping it off the browser was never part of the request.
- Row Level Security is the check that survives code you never read, because every request has to pass the database.
Build your project and search the output for service_role and sk_live_: it
takes a minute and gives an unambiguous answer about the change you did not
read. Then the 10-minute security checklist covers the rest.
What can actually go wrong with a Windsurf-built app
None of these mean you did anything wrong. They come with an agent writing a lot of code quickly. Here's what's worth checking:
A secret key the agent wired in for you
To get an integration working in one pass, the agent sometimes writes the key straight into the code, and if that file runs in the browser, anyone can read it. Some keys are meant to be public and that's fine; Reeve reads your app's loaded code, finds any keys, and tells you which are safe and which need to move to the server.
A committed .env or exposed .git folder
Keys belong in a .env file that never ships. But a change touching dozens of files is easy to approve without reading every path in it, so .env gets committed, or the hidden .git folder deployed, putting your whole history, keys included, one download away. Reeve checks whether either is reachable from outside.
Your database left open (RLS off)
If your app stores data (usually in Supabase or another Postgres), Row Level Security decides who may read or change each row. Switching it off is a quick way to make a feature work while you're building, and it tends to stay off. Reeve checks by counting rows, never by reading them.
Public file storage
Uploads land in storage "buckets," and a public bucket lets anyone list and download what's inside: invoices, IDs, private photos. Reeve checks whether your buckets are listable; it never downloads anyone's files.
Source maps left on
A source map hands anyone who looks a readable copy of your original code. Useful while you're building, a gift to strangers once you're live, because it makes every other gap easier to find. Reeve checks whether yours went out with the build.
Missing safety headers & open endpoints
A handful of settings tell browsers how to protect your visitors; separately, there's the question of whether your data endpoints answer to anyone, from any website. Each is small on its own, but together they decide how much a stranger can do with what they find. Reeve flags what's missing.
What Reeve is, and what it isn't
Reeve is a free, read-only check from the outside, like an inspector trying the doors without going in. It catches the common, high-impact mistakes fast. It is not a full security audit, and a clean grade isn't a guarantee; it means the obvious doors are shut.
Windsurf is an editor, not a host: it writes and runs code with you, but it doesn't deploy your app or stand guard over it afterwards. That part is on you and on whatever the agent produced. Reading a large change before you accept it is the best habit here, and if you use Supabase its own advisor flags database problems. What Reeve adds is the outside view: what your shipped app actually exposes, in plain words you can act on. And if you'd rather the watching wasn't your job, make it ours.
Want it handled, not just checked?
Reeve Care keeps watching your app, backs up your data, and helps you fix what breaks, so you can keep building instead of worrying.
Learn about Reeve CareFAQ
Is code written by Windsurf's agent secure?
It's often good code, but "the agent wrote it" doesn't mean "someone checked it for safety." Agentic edits optimise for a working result, which sometimes means a key in the wrong place or a database rule switched off to get past an error. The only way to know is to look at what's exposed. Reeve does that free in about 20 seconds.
Cascade changed a lot of files at once. How do I know what it exposed?
Mostly you can't, by reading. That's the honest answer, and it's why an outside check helps: instead of auditing a diff, Reeve looks at the app you actually deployed and reports what a stranger can reach: keys in the browser, an open database, downloadable files, an exposed .env.
How do I know if my Windsurf app is leaking API keys?
The usual cause is a key written into code that runs in the browser, where "hidden" doesn't exist. Reeve reads your app's loaded code, finds any keys, and tells you which are safe to be public and which need to move to the server, without ever storing the real values.
Will scanning my Windsurf app change anything?
No. Reeve only looks at what's already public from the outside. It never logs in, never changes anything, and never downloads your files. Read-only, like checking whether a door is locked without stepping inside.
An agent changed files I never opened. How am I supposed to review that?
Not line by line, which is the honest answer. Review by outcome instead: build the project and search the output for secret-key shapes, then check what your database returns to a request with no login. Both take minutes and both catch the two failures that actually matter, regardless of how many files changed.
Does an AI assistant put secrets in my frontend on purpose?
It puts them wherever the code it wrote needs them. If a component that runs in the browser was written to call something requiring a secret key, the key has to be in the browser for that code to work, so the assistant makes it work. The instruction to keep it on a server was never in the request.
Which single check tells me the most about my app?
Whether Row Level Security is on for every table, and whether the policies actually restrict anyone. It is the one rule that applies to every request no matter which file made it, so it survives any amount of code you have not read.