All Posts

Security Checklist for Apps Built with Cursor

February 22, 20263 min readVibeSafe Team
cursorsecuritychecklistai-coding

Why Cursor Apps Need a Security Checklist

Cursor is one of the best AI coding tools available. It writes clean, functional code fast. But it optimizes for "it works" — not "it's secure." Every Cursor-built app should go through a security checklist before shipping to production.

Here's the checklist we use at VibeSafe, based on scanning thousands of AI-generated apps.

The Checklist

API Keys & Secrets

  • [ ] No API keys hardcoded in client-side files
  • [ ] No NEXT_PUBLIC_ prefix on secret keys (Supabase service_role, Stripe secret, etc.)
  • [ ] .env file is in .gitignore
  • [ ] No secrets in git history (check with git log --all -p | grep -i "sk_live\|service_role\|secret")
  • [ ] Third-party API keys (OpenAI, Stripe, Resend) are only used in server-side code

Authentication

  • [ ] All API routes check for a valid session/token
  • [ ] Admin routes have role-based access control
  • [ ] Password reset tokens expire
  • [ ] OAuth redirect URIs are restricted to your domain
  • [ ] No user data returned in error messages

Security Headers

  • [ ] Strict-Transport-Security (HSTS) is set
  • [ ] X-Content-Type-Options: nosniff is set
  • [ ] X-Frame-Options: DENY prevents clickjacking
  • [ ] Content-Security-Policy restricts script sources
  • [ ] Referrer-Policy is set to strict-origin-when-cross-origin
  • [ ] Permissions-Policy restricts camera, microphone, geolocation

CORS

  • [ ] No wildcard Access-Control-Allow-Origin: *
  • [ ] Allowed origins are explicitly listed
  • [ ] Credentials are not allowed with wildcard origins

Database

  • [ ] Row Level Security (RLS) is enabled on all Supabase tables
  • [ ] No raw SQL queries with user input (use parameterized queries)
  • [ ] Database connection strings are not in client code
  • [ ] Supabase service_role key is server-only

Payments

  • [ ] Stripe secret key (sk_live_*) is never in client code
  • [ ] Webhook signatures are verified
  • [ ] Price IDs are validated server-side (not trusted from the client)
  • [ ] Subscription status is checked server-side before granting access

Infrastructure

  • [ ] HTTPS is enforced (HTTP redirects to HTTPS)
  • [ ] Rate limiting is configured on auth and API endpoints
  • [ ] Error pages don't leak stack traces or internal paths
  • [ ] File upload endpoints validate file type and size

Common Cursor Mistakes

1. Using the OpenAI key in a client component

Cursor often generates React components that call the OpenAI API directly:

// BAD — Cursor generated this in a client component
const response = await fetch("https://api.openai.com/v1/chat/completions", {
  headers: {
    Authorization: `Bearer ${process.env.NEXT_PUBLIC_OPENAI_API_KEY}`,
  },
});

Fix: Move API calls to a Next.js route handler (app/api/chat/route.ts) and call it from the client.

2. Missing auth checks on API routes

Cursor creates API routes that work, but it rarely adds auth checks:

// BAD — no auth check
export async function POST(req: Request) {
  const { userId, data } = await req.json();
  await db.update(userId, data); // Anyone can update any user
}

Fix: Always verify the session before processing the request.

3. CORS wildcard in middleware

// BAD — allows any origin
response.headers.set("Access-Control-Allow-Origin", "*");

Fix: Set specific allowed origins.

Automate It

Instead of manually checking everything, run a VibeSafe scan on your deployed URL. It checks all of the above in under 60 seconds and gives you a prioritized list of what to fix.

Is your app vulnerable?

Run a free security scan and find out in 60 seconds.

Scan Your App Free