Practical checklist from ShipSafeStack. Not a penetration test, compliance audit, score, or proof that an app is safe to launch. Informational only — no guarantees.
The frontend checklist is how to search the bundle after the app is built. This page is how to keep the secret out of the code an agent writes. They overlap — do both.
Hand the agent a name, not a value
Agents inline a key because the request fails without it. Put the value in the host’s secret store, or in a local file that git and the agent ignore, and tell the agent the variable name.
Vendor docs (as of the guide on ShipSafeStack):
- Cursor:
.envis ignored by default;.cursorignorecan block extra paths. A terminal command or MCP tool can still read an ignored file. - Lovable:
VITE_values belong in.envbecause they are public build-time values; Stripe / Resend / model keys belong in Secrets for Edge Functions. - Bolt: server functions read the secret; the value should not be plain text in the code.
Do not paste a live key into the chat to “make it work.” The transcript is another copy. A GitHub token pasted there is the same class of leak as Leaked a GitHub token?.
Sort the key before the agent touches it
| Key | Vendor’s instruction | Where it may live |
|---|---|---|
Stripe pk_ | Publishable; cannot create charges or read account data | Client |
Stripe sk_ / rk_ | Not safe to expose; prefer a restricted key with only the permissions that job needs | Server environment |
Stripe whsec_ | Webhook signing secret; verify the raw body with it | Server environment |
| Supabase publishable / legacy anon | Safe in the browser only with RLS and least-privilege grants | Client |
Supabase secret / service_role | Bypasses RLS; never in the client | Server environment |
| Firebase API key (restricted to Firebase APIs) | Public by design; Security Rules authorize data | Client config |
| Model, email, or DB URL with a password | Treat as secret unless that vendor’s docs call it publishable | Server environment |
Stripe’s docs also say not to put sensitive keys in source or committed files, and not to share them in chat or email.
Stop the file from being written
- Tell the agent which env var names to read and that secrets are already configured on the host.
- Keep secret files out of git (
.gitignore) and out of agent context where the tool supports it. - Prefer server routes / Edge Functions for privileged calls; the browser should call your backend, not hold the secret.
- Do not “hide” a key by encoding it, splitting the string, or fetching it from a public JSON file — that is the same leak with extra steps.
If the secret is already in the code
- Rotate it at the vendor before or as you redeploy. Assume the old value was copied.
- Remove it from the client, from committed
.envfiles, and from git history if it was pushed. Rotation without removal leaks the replacement. - Check the vendor’s usage or logs for calls you did not make.
- Re-scan the built JavaScript on the deployed URL, not only the latest source.
Related: API keys leaking into the frontend, Supabase service role key exposed, Pre-launch security checklist.
FAQ (condensed)
- Can I give Cursor my Stripe secret key so it can test payments? Prefer a restricted key with minimal permissions, in a server env the agent does not print back; do not paste unrestricted live secrets into chat.
- Is the Supabase anon key safe in generated client code? Only with RLS and tight grants — details in Is the Supabase anon key safe to expose?.
- What if the agent encoded the key? Still a leak if the browser can reverse it.
- Does ignoring
.envfix a key that was already committed? No — rotate, remove from history/deploy artifacts, then prevent the next write.
Check a public URL (passive, free): shipsafestack.com/scan — looks for secret-shaped strings in public scripts (masked), among other passive checks. Nothing stored. Informational only, not an audit.
