Browse the blog
Sentrint Blog
Bolt · Checklist

A Bolt security checklist for the hour before you share the link

Turn on Bolt's security review first, because it is free and off by default. Then check the five things it cannot see: cross-account reads, keys in your Vite bundle, Supabase grants left behind after enabling RLS, a paywall that only hides, and routes that are protected in the component instead of the handler.

This is the list for the hour before you post the link somewhere public. It assumes a Bolt project with a Supabase database behind it, which is the common shape, and it is ordered by what an attacker finds first rather than by what is easiest to fix.

Work down it and stop at the first failure. A confirmed cross-account read is worth more of your attention than the other seven items put together, and fixing things in parallel is how you end up half-finished on all of them.

0. Turn on the security review

Bolt ships an automatic security check that runs before publish, and it is opt-in. If you have never opened that setting, nothing has ever run against anything you shipped.

How to check: open your project settings and look for the security review toggle. It is either on or it has never run.

Why it is item zero rather than item one: it costs one click, it is included, and there is no argument against it. It is also a configuration check rather than a read of your logic, so the eight items below are the ones it will not raise.

1. Can a second account read the first one's data?

Why it leads: this is the flaw that shows up most often in generated apps and the one that looks perfect during testing. You test as yourself, and as yourself every permission is correct.

How to check: sign up twice, in two browsers or one plus a private window. As account A, note a URL with an id in it, something like /projects/17. As account B, paste it. Then repeat with the API calls you can see in the network tab, because the page can redirect while the request underneath it still answers.

A failure looks like: account A's data rendering normally for account B. Not an error, not a blank page. The data.

2. Are there live keys in your shipped bundle?

Why Bolt specifically: Bolt builds in the browser and deploys the result, and a Vite project inlines anything named VITE_ into that build. The variable feels like configuration in the editor and is public in the output.

How to check: open your deployed site, open developer tools, press Ctrl+Shift+F or Cmd+Shift+F to search every loaded file, and look for sk-, sk_live, secret and service_role.

A failure looks like: a match inside a .js file. A Supabase publishable key there is expected and fine. An OpenAI key, a Stripe secret key or a Supabase service role key is an emergency, and the first move is rotating it rather than editing the code.

3. Is RLS on, and were the grants revoked?

Why both halves: row-level security is off by default on a new Supabase table, and the half that checklists miss is that switching it on does not take back the privileges the table was created with. Supabase documents this and it is a separate statement nobody runs.

How to check: Supabase dashboard, table list, read the RLS column. Then run revoke all on table <name> from anon, authenticated; and grant back only what the app actually uses.

A failure looks like: a table marked disabled, or a table marked enabled that still answers an anonymous query. The second is the one people are surprised by.

4. Does your paywall exist on the server?

How to check: as a free account, open the network tab, find the request a paid feature makes, and replay it. Then delete a display:none in the elements panel and see whether premium UI appears with real data in it.

A failure looks like: the endpoint answering. Hiding a feature is presentation; checking a plan is authorisation, and only one of those survives somebody opening developer tools.

5. Is every protected route protected on the way in?

How to check: log out completely, then paste every admin, settings and dashboard URL you know into the address bar. Include the ones you normally reach by clicking rather than typing, because those are the ones that only got a check in the component.

A failure looks like: the page loading. Also worth catching: the page bouncing you to login while the fetch behind it returns a full payload. Read the network tab, not the screen.

6. Does a script survive a form field?

How to check: find a field whose contents another person will see, a display name, a comment, a bio. Enter <script>alert(1)</script>, save it, and view it from the other account.

A failure looks like: an alert box. If one appears, anything that field can hold runs in your users' browsers, including code that reads their session.

7. Is anything rate limited?

How to check: try your own login with a wrong password twenty times in a row, quickly. Then submit your signup form ten times.

A failure looks like: nothing slowing down. Without a limit an attacker can try tens of thousands of passwords an hour against your users; with one the same attack takes years, which is the entire difference.

8. Do your errors say too much?

How to check: break things deliberately. Submit garbage to a form, request a URL that does not exist, and log in with an email address that has no account.

A failure looks like: a stack trace, a file path, a database error, or a login that distinguishes "wrong password" from "no such user". The last one tells an attacker which addresses are worth attacking.

What this checklist does not cover

Eight checks find eight classes of problem, and they are the common ones because they are the ones people know to look for. They do not touch your business logic, and business logic is where the interesting failures live: a discount code that applies twice, an order total calculated in the browser, a webhook that trusts its own payload, a file upload that writes outside the folder it was meant to.

Those need somebody to read the code, and a repository has more surface than anyone gets through in an evening. That is the job Sentrint does: it reads the whole thing, tells you what an attacker would reach first in plain English, and hands you a fix you can paste straight back into Bolt. It is a code scan and not a pentest, so it reads what you wrote rather than attacking your live site, and the first scan is free with no card.

Run the eight anyway. They take under an hour and they cover what actually gets people, which is more than most shipped apps can say.