// docs / security guides / cursor checklist
Cursor security checklist: 25 items before ship
Building with Cursor? Cursor's autocomplete and Agent features are exceptionally powerful — and create predictable security blind spots. This checklist targets Cursor-specific patterns: service-role key inlining, generated multi-file edits merged without review, Agent-mode terminal commands, and project rules (.cursor/rules) as your first security guardrail. 25 items across secrets, database, auth, headers, deployment, and Cursor-specific gotchas.
PRE = pre-deploy (audit your source). DEPLOY = at deploy time. POST = post-deploy verification.
Secrets and API keys (5 items)
Cursor's autocomplete is trained on open-source code where secrets are common. The model suggests them freely, especially after a failed auth attempt.
- PRE — Write security rules into your project rules. Add a rule in
.cursor/rules(Cursor docs): "Never inlineSUPABASE_SERVICE_ROLE_KEY,sk_live_*, or any server secret into client-side code. Always use server-only imports." Cursor applies project rules as context for Agent and chat. - PRE — Audit Composer-generated files. When Cursor's Composer creates a whole file (especially auth handlers), review it line-by-line. Composer sometimes inlines env vars that should stay server-only. Look for
NEXT_PUBLIC_or direct references to service keys in component imports. - PRE — Reject auto-imports of service clients into client components. If Composer imports
import { supabase } from '@/lib/supabase/service'into a React file, delete it immediately and route through an API endpoint instead. Server-only imports are explicitly marked — don't skip them. - PRE — Scan Agent-mode commits. Agent mode runs terminal commands and can commit directly. Audit
git log --oneline -20andgit diff HEAD~5to ensure no secret-looking strings were committed during an Agent run. - POST — Run Secrets in JavaScript Bundles. Passive scan on the deployed URL. If a service key appears in the JS bundle, rotate it immediately — Cursor's autocomplete probably inlined it.
Database access control (4 items)
Composer often generates working auth code but skips RLS — the "it works" moment blinds people to the missing policy enforcement.
- PRE — Make Cursor generate migrations with RLS. In your project rules: "Every
CREATE TABLE public.*migration must includeALTER TABLE ... ENABLE ROW LEVEL SECURITYand a policy per command scoped toauth.uid()." Then ask Cursor to generate the migration. - PRE — Review Composer-generated policies. Composer sometimes writes policies without checking
auth.uid(). Policies likeallow select on public.itemswithout ausingclause are dangerously wide. Require user_id matching. - DEPLOY — Confirm RLS and policies are live. Open Supabase Studio and check that every table's RLS toggle is on and that each table has policies. A table with RLS on and no policies denies everything; one with
USING (true)allows everyone. - POST — Run a FixVibe scan on the deployed app. Check the Supabase Row-Level Security result: it shows any table an anonymous visitor can read with your public key.
Authentication and sessions (4 items)
Cursor generates auth flows quickly but often misses the subtle server-side validation that keeps tokens safe.
- PRE — Ensure all auth routes use
getUser(). Search forgetSession()in your API routes and replace withawait supabase.auth.getUser().getSession()reads an unverified cookie;getUser()validates with Supabase backend. - PRE — Check Composer auth handlers for token expiry. Magic-link tokens need server-enforced
expires_at. Default Supabase is 1 hour — don't ask Cursor to override it without a real reason. - PRE — Audit the sign-in redirect guard. The
nextquery param redirect after sign-in must be validated: must start with/, never//. Composer sometimes skips this. Add it manually if missing. - POST — Test logout server-side state destruction. Sign in, sign out, inspect cookies (DevTools → Application → Cookies). The session cookie must be cleared immediately. If it persists, the logout handler isn't destroying state.
HTTP security headers and CSP (3 items)
Cursor rarely generates middleware by default. If you don't ask explicitly, CSP and HSTS usually aren't there.
- PRE — Demand CSP in your project rules. Add: "Generate a
src/middleware.tswith Content-Security-Policy. Use nonce for script-src, no unsafe-inline." Then ask Cursor to generate it. Without this hint, middleware is skipped. - PRE — Verify
src/middleware.tsexists. With thesrc/directory layout, Next.js only picks upsrc/middleware.ts. A root-levelmiddleware.tsis silently ignored. If CSP isn't landing, check the file is in the right place. - POST — Run HTTP Security Headers. Passive scan reports missing CSP, HSTS, X-Frame-Options, X-Content-Type-Options. Open the report and follow the fix guidance for your deployment platform.
Deployment hygiene (5 items)
Cursor apps often land on Vercel, which has good defaults but needs explicit hardening for the build/deploy boundary.
- DEPLOY — Check Vercel env-var scoping. Settings → Environment Variables → each secret should be scoped to Production only. Never share
sk_live_*with Preview or Development. - DEPLOY — Disable build-log secret echo. If your
vercel.jsonor GitHub Actions workflow hasecho $SECRET, remove it. Build logs are archived publicly; secrets in logs are compromised. - DEPLOY — Use Vercel's managed secrets, not inline workflow vars. Vercel's Settings → Environment Variables is encrypted at rest. GitHub Actions' secrets are better than nothing but are designed for CI, not deployment platform integration.
- POST — Verify CSP nonce on the deployed preview. Open a Vercel Preview link in the browser, open DevTools → Network → the root HTML response. CSP header must be present and include
'strict-dynamic'with a unique nonce per request. - POST — Rotate any key that ever shipped, even to Preview. If a key reached the production bundle for even 10 minutes, it's compromised. Rotate immediately.
Cursor-specific gotchas (4 items)
Patterns unique to Cursor's workflow that create security risks:
- Agent mode auto-fixes propagate old patterns. If you ask Agent to "fix auth errors", it may regenerate the same auth file multiple times, each time inlining the same service key if it's in the codebase context. Clean the original first, then ask Agent to fix.
- Cursor Index leaks intent. Cursor's
@codebaseindexing is powerful but if your.cursordirectory is ever exposed (misconfigured S3, git history), the index reveals your architecture and secret patterns. Keep.cursorlocal. - Composer mode loses context between files. Each file Composer generates is fresh. If you ask it to generate a client file, then an API route, they may use different Supabase client configs. Review both and ensure they match your architecture.
- Autocomplete bias toward "working" over "secure". Cursor suggests the fastest code that passes your current context. If your test has
NEXT_PUBLIC_SERVICE_KEY, autocomplete remembers it and re-suggests it. Clean test fixtures before sharing code with the model.
Next steps
Once you've locked down the Cursor-specific patterns, cross-check against the general vibe coding security checklist (51 items) and then step-by-step hardening. Also see the Claude Code checklist if you're mixing tools.
