// docs / security guides / cursor checklist
Cursor sikkerhedstjekliste: 28 punkter før release
Building with Cursor? Cursor's autocomplete, Composer mode, and Agent features are exceptionally powerful — and create predictable security blind spots. This checklist targets Cursor-specific patterns: service-role key inlining, Composer-generated whole files without review, Agent-mode terminal commands, and the <code>.cursorrules</code> file as your first security guardrail. 28 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. Items reference FixVibe check IDs in category.check-id form where relevant.
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
.cursorrules. Add a line: "Never inlineSUPABASE_SERVICE_ROLE_KEY,sk_live_*, or any env var starting with a provider acronym into client-side code. Always use server-only imports." Cursor reads.cursorrulesand factors it into every suggestion. - 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.browser-storage. 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 — Force Cursor to generate migrations with RLS. In
.cursorrules: "EveryCREATE TABLE public.*migration must includeALTER TABLE ... ENABLE ROW LEVEL SECURITYandFORCE ROW LEVEL SECURITY." Then ask Composer 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
FORCE ROW LEVEL SECURITYis live. Open Supabase Studio, check each table's RLS toggle. If Composer's migration hadENABLEbut forgotFORCE, table owners (your migrations) bypass RLS. This is a real gap. - POST — Run the baas.supabase-rls active check. It tries a write with the anon key. If it succeeds, RLS isn't actually enforcing — likely missing the
FORCEkeyword.
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
.cursorrules. Add: "Generate asrc/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 headers.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 (44 items) and then step-by-step hardening. Also see the Claude Code checklist if you're mixing tools.
