Checklist

Vibe-coded app security checklist

Fifteen checks for an app you shipped with an AI builder. The list follows what Deploy Safely looks for on a public URL, plus the dashboard steps a scan cannot do for you. Every item is a check on your own app.

Free check

Check a URL against this list

Paste the public address. You get a private report with a fix prompt for each issue the check can actually see.

Free, one time, and limited to what a stranger can already see. No account required.

Critical

A stranger who opens the page can copy these. Rotate the key after you remove it.

  1. Critical1 of 15

    No service role key in the browser

    A Supabase service role key skips row-level security. Anyone who loads the page can copy it and read or change every row.

    Check it yourself

    Open the live app, view source, and search for service_role. Search the JavaScript files from your domain too. The anon key is supposed to be there.

    The free check looks for a service role token in the page and its scripts, and it does not use the key.

    Run the free check

  2. Critical2 of 15

    No provider secret in the page or the scripts

    OpenAI, Anthropic, Stripe secret keys, AWS, GitHub, Slack, SendGrid, Resend, and a PEM private key can all spend money or open an account that is yours.

    Check it yourself

    Search the live HTML and JavaScript for sk-, sk_live, rk_, AKIA, ghp_, xoxb-, and -----BEGIN. A Stripe publishable key starts with pk_ and is a different item, lower on this list.

    The free check searches the page and up to a handful of scripts for these patterns, and stores only a short masked hint.

    Run the free check

  3. Critical3 of 15

    No database URL with a password in the client

    A postgres, MySQL, or MongoDB address that includes a password opens the database directly, past the app.

    Check it yourself

    Search the published files for postgres://, mysql://, and mongodb://. The browser should not receive a connection string.

    The free check flags a database URL that contains a password. It does not save the password or connect with it.

    Run the free check

  4. Critical4 of 15

    The .env files are not public URLs

    A host that serves /.env is handing out the names and values you set aside for the server.

    Check it yourself

    Visit /.env, /.env.local, and /.env.production on the live domain. You want a not-found page, not a file.

    The free check requests those three paths and reports them only when the body looks like an env file.

    Run the free check

  5. Critical5 of 15

    The .git folder is not public

    A readable /.git directory can let someone rebuild the source, including a key you deleted in a later commit.

    Check it yourself

    Open /.git/HEAD and /.git/config on the live domain. Both should be missing.

    The free check requests those two paths and looks for a Git HEAD or a config section.

    Run the free check

High

Private data or a powerful token is one request away. Fix these before you invite users.

  1. High6 of 15

    Supabase tables do not answer a signed-out visitor

    The anon key is public by design. A table with row-level security off, or a policy of true, returns real rows to that key.

    Check it yourself

    In the Supabase dashboard, turn on row-level security for every table in the public schema. Read each policy. Private rows should require a signed-in user.

    The free check does this only when the page already includes a supabase.co address and the anon key. It asks for one row per table and does not keep the row.

    Run the free check

  2. High7 of 15

    Firebase is not still in test mode

    Test-mode rules allow reads and writes without a login. The web API key in the page is normal. The rules are the lock.

    Check it yourself

    In Firebase, Realtime Database rules should not set .read or .write to true. Firestore rules should require request.auth, then a tighter match on the user.

    The free check calls Firebase only when the public config is already in the page. It does not sign in, and it does not save records.

    Run the free check

  3. High8 of 15

    No .npmrc token on the website

    An npm auth token in /.npmrc can publish packages or download private ones.

    Check it yourself

    Open /.npmrc on the live domain. A not-found page is what you want. If a token was exposed, revoke it at the registry.

    The free check reports /.npmrc only when the file contains an _authToken setting. The token value is not saved.

    Run the free check

  4. High9 of 15

    The Stripe webhook secret stays on the server

    A whsec_ value proves that a webhook came from Stripe. If it is public, someone can send fake payment events.

    Check it yourself

    Search the live JavaScript for whsec_. It should appear only in the server route that verifies the webhook.

    The free check flags a webhook secret in the page or a script.

    Run the free check

Medium

These make the rest of the app easier to read. Clean them up in the same pass if you can.

  1. Medium10 of 15

    Production source maps do not include your original code

    A public map makes leftover keys, hidden routes, and comments much easier to find.

    Check it yourself

    In developer tools, open a script from your domain and look for sourceMappingURL. If the map includes original files, turn maps off and delete the .map files.

    The free check fetches a map only when a script already points at one, and only reports it when original source is included.

    Run the free check

  2. Medium11 of 15

    No signed-in user token baked into the files

    A Supabase user JWT should be created when someone signs in. Shipped inside the bundle, it can impersonate that user until it expires.

    Check it yourself

    Search the repo and the live bundle for a long-lived session token. Sign that user out if the token was real, and remove it from source.

    The free check notes a JWT whose role is authenticated when it appears in the page or a script.

    Run the free check

Low

Worth doing after the leaks above are closed. They are not the same kind of emergency.

  1. Low12 of 15

    The main page sends basic browser headers

    Content-Security-Policy, Strict-Transport-Security, X-Content-Type-Options, a frame rule, and Referrer-Policy tell the browser how to treat the site. They do not replace a lock on the database.

    Check it yourself

    Open the live page response headers. Add the missing ones at the host. Start with a simple policy so you do not block your own app.

    The free check mentions headers when two or more of those are missing. One missing header stays quiet.

    Run the free check

  2. Low13 of 15

    A Google API key in the page is restricted

    Firebase and Maps apps often include a Google key in the browser. An unrestricted key can be copied and used from another site.

    Check it yourself

    In Google Cloud, restrict the key to your domain and to the APIs you use. If the key was meant for a server, move it out of client code and rotate it.

    The free check notes a Google API key in the page as a low finding, not as a leaked secret key.

    Run the free check

Usually fine

These show up in a healthy app. Leave them in place.

  1. Usually fine14 of 15

    The Supabase anon key is allowed in the page

    That key identifies your project to the client. Hiding it does not secure the data. Row-level security does.

    Check it yourself

    Leave the anon key in the browser. Confirm you did not replace it with the service role key, and that the tables are locked.

    The free check explains an anon key when it sees one. It does not treat that key as a leak.

    Run the free check

  2. Usually fine15 of 15

    A Stripe publishable key is allowed in the page

    Keys that start with pk_ are designed for the browser. They cannot issue refunds or see secret customer details on their own.

    Check it yourself

    Keep pk_ in the client. Confirm sk_ and rk_ are absent from the same files.

    The free check labels a publishable key as expected.

    Run the free check

What a clear check still leaves out

The scan reads the public page, the scripts that page loads, and a short list of paths (.env, .git, .npmrc). It asks Supabase or Firebase for data only when that public config is already in the page. It does not log in, guess passwords, or use a key it finds. A quiet result is an early warning that came back clean, not a promise that the whole app is safe.

Read the guide for your builder · Read the blog

Email me this list

We send the checklist to that address. We don't save it for a mailing list, and the page stays free to read either way.