All articles

Cursor code vulnerability check: how to secure what you just shipped

8 min read

Free website check

Is your website actually working right now?

Paste your URL. Uptime, checkout, login, SSL and API checked in 30 seconds.

Results in 30 secondsNo code access neededFree, no signup

Cursor is fast. You describe what you want, it writes the code, you hit deploy, and your app is live. That workflow is incredible for shipping, but it skips the part where someone actually reviews what got built. And that gap is where vulnerabilities live.

This is not a Cursor problem specifically. It happens with any tool that generates code faster than you can read it. The issue is that working code is not the same as secure code. Your app can function perfectly and still have your database credentials sitting in the browser.

A Cursor code vulnerability check catches these problems after you ship, by scanning your live deployment the way an attacker would. This guide walks through what to look for, why Cursor-generated code is prone to specific security gaps, and how to fix every one of them.

Why Cursor-generated code needs a vulnerability check

Cursor optimizes for getting your app to work. When you prompt it to "add Supabase authentication" or "connect to Stripe," it grabs the fastest path to a functioning result. That often means:

  • Hardcoding API keys directly in components. Cursor will drop a Stripe secret key or Supabase service role key right into a React component if that makes the code work. It doesn't think about whether that key ends up in the browser.
  • Skipping input validation entirely. Forms, API routes, server actions. Cursor writes them to accept data and process it. Validating that data against injection, overflow, or unexpected types is rarely part of the generated output.
  • Using permissive defaults. CORS set to *, no rate limiting, no authentication on API routes, no Content-Security-Policy header. These defaults make development easy, but in production they are open doors.
  • Ignoring the deployment context. Cursor writes code that works locally. It doesn't configure security headers, doesn't set up HTTPS enforcement, and doesn't disable source maps for production builds.

None of this is malicious. Cursor is doing exactly what you asked: build the feature. But features without security checks are liabilities, and the faster you ship, the more liabilities stack up.

The 7 most common vulnerabilities in Cursor-built apps

We've scanned thousands of apps built with Cursor and other code generation tools. These are the issues that show up again and again, in order of severity.

1. Exposed API keys in the JavaScript bundle

This is the most dangerous and the most common. When you tell Cursor to integrate Supabase, Stripe, OpenAI, or any third-party service, it often places the API key directly in client-side code. In Next.js, any variable prefixed with NEXT_PUBLIC_ gets baked into the JavaScript bundle that browsers download.

The result: anyone can open DevTools, search your JS files, and extract your keys. A Supabase service_role key gives full read/write access to every table, bypassing all row-level security. A Stripe sk_live_ key lets someone issue refunds or access customer data.

How to fix it:

  • Move all secret keys to server-side environment variables (no NEXT_PUBLIC_ prefix)
  • Access third-party services only from API routes or server components
  • Use NEXT_PUBLIC_SUPABASE_ANON_KEY on the client, never the service role key
  • After fixing, redeploy and rotate every key that was previously exposed

2. API routes with no authentication

Cursor creates API routes at /api/* that handle your business logic. By default, these routes are publicly accessible. Anyone who knows the URL can call them. If you have an /api/users endpoint that returns user data or an /api/admin/delete endpoint that removes records, those are exposed to the entire internet.

How to fix it:

// Example: protect an API route
import { getServerSession } from 'next-auth';
import { NextResponse } from 'next/server';

export async function GET() {
  const session = await getServerSession();
  if (!session) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
  }
  // ... your protected logic
}

3. Missing security headers

Cursor never adds security headers. Not once, across any of the apps we've scanned. That means no Content-Security-Policy (your biggest defense against XSS), no Strict-Transport-Security (forces HTTPS), no X-Frame-Options (prevents clickjacking), and no Permissions-Policy (restricts browser API access).

How to fix it: add a headers() function in your next.config.ts:

// next.config.ts
export default {
  async headers() {
    return [{
      source: '/(.*)',
      headers: [
        { key: 'Content-Security-Policy', value: "default-src 'self'; script-src 'self' 'unsafe-inline';" },
        { key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubDomains; preload' },
        { key: 'X-Frame-Options', value: 'DENY' },
        { key: 'X-Content-Type-Options', value: 'nosniff' },
        { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
        { key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },
      ],
    }];
  },
};

4. Database credentials in the client

When Cursor sets up a database connection, it sometimes initializes the client in a shared file that gets imported by both server and client components. If that file contains your database URL or connection string, it leaks to the browser.

How to fix it: create a dedicated lib/db.ts file that is only imported in server components, API routes, or server actions. Add the "server-only" package to enforce this at build time:

// lib/db.ts
import 'server-only';
import { createClient } from '@supabase/supabase-js';

export const supabaseAdmin = createClient(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_SERVICE_ROLE_KEY!
);

5. Unvalidated form inputs and server actions

Cursor generates forms that send data directly to server actions or API routes without validation. This opens the door to SQL injection, XSS payloads stored in your database, and type confusion bugs that crash your app.

How to fix it: validate every input with a schema library like Zod:

import { z } from 'zod';

const contactSchema = z.object({
  name: z.string().min(1).max(200),
  email: z.string().email().max(320),
  message: z.string().min(10).max(5000),
});

export async function submitContact(formData: FormData) {
  'use server';
  const result = contactSchema.safeParse({
    name: formData.get('name'),
    email: formData.get('email'),
    message: formData.get('message'),
  });
  if (!result.success) {
    return { error: 'Invalid input' };
  }
  // ... process validated data
}

6. Source maps enabled in production

Source maps let anyone reconstruct your original TypeScript source code from the compiled JavaScript. If productionBrowserSourceMaps is set to true in your config (or if a build tool enables it), your entire codebase is readable. Attackers use this to find hardcoded secrets, understand your business logic, and identify vulnerable patterns.

How to fix it: verify that productionBrowserSourceMaps is false (the Next.js default). Check your next.config.ts and make sure no build plugin overrides this.

7. Exposed .env and .git files

On some hosting configurations, your .env file or .git directory can be served as static files. Navigate to yourdomain.com/.env and if you see your environment variables, every secret in your stack is compromised.

How to fix it:

  • Verify .env* is in your .gitignore
  • Test by visiting yourdomain.com/.env and yourdomain.com/.git/config. Both should return 404.
  • On Vercel, this is handled automatically. On custom servers or Docker deployments, configure your web server to block these paths.

How to run a Cursor code vulnerability check

You could go through each of these manually. Open DevTools, search your JS bundles for key patterns, test every API route, check every header. That works, but it takes hours and you'll miss things.

A faster approach: run an automated Cursor code vulnerability check against your deployed app. Here's how it works:

  1. Paste your URL. The scanner takes your deployed app URL. No code access, no repo connection, no setup.
  2. Wait 30 seconds. It runs a non-invasive scan that mirrors what an attacker would do: analyze your JavaScript bundles, probe for exposed files, check your HTTP headers, test your API endpoints.
  3. Review the report. Every vulnerability gets a severity rating (critical, high, medium, low) and a plain-English explanation with the exact fix.

The scan covers 150+ vulnerability patterns. It catches exposed API keys, missing headers, open database endpoints, leaked source maps, and misconfigured auth. If something is wrong, you know exactly what and exactly how to fix it.

Vulnerability check vs. code review

A code review looks at your source files for bad patterns. That's valuable, but it misses deployment-level issues: headers your hosting provider strips, environment variables that leak through build configuration, API routes that behave differently in production than locally.

A vulnerability check scans your live, deployed app. It sees what users see. It sees what attackers see. The two approaches complement each other, but if you only do one, scan the live deployment. That's where the real risk is.

What to do after the scan

Your scan report will prioritize issues by severity. Start with the critical ones. Here is a typical workflow:

  1. Fix critical issues immediately. Exposed API keys and open database access are emergencies. Move keys to server-side env vars, rotate every exposed key, and redeploy.
  2. Add security headers. This is a single config change that protects against entire categories of attacks.
  3. Lock down API routes. Add authentication middleware to every route that should not be public.
  4. Add input validation. Install Zod or a similar library and validate every form submission and server action.
  5. Re-scan. After your fixes are deployed, run the scan again to verify everything is resolved.

Security checklist for Cursor projects

Keep this list handy. Run through it before every deployment:

  • No API keys in client-side code or NEXT_PUBLIC_ variables
  • All API routes require authentication
  • Security headers configured in next.config.ts
  • Database connections use server-only imports
  • All form inputs validated with Zod or similar
  • Source maps disabled in production
  • .env and .git not accessible via browser
  • CORS configured with explicit origins, not *
  • Rate limiting on authentication endpoints
  • All previously exposed keys have been rotated

Or skip the checklist and run an automated scan. It checks everything on this list and the things you didn't think to look for.

Build fast, verify faster

Cursor lets you build in hours what used to take weeks. That speed is a competitive advantage, but only if your app stays up. One leaked database key, one open admin endpoint, and you go from "just launched" to "just breached."

A Cursor code vulnerability check takes 30 seconds. It costs nothing. And it tells you exactly what needs fixing before anyone else finds it.

Your code works. Now make sure it's safe.

Don't lose revenue silently

Downtime doesn't announce itself.

Broken checkout, expired SSL, failing API. Get alerted in minutes, not days.

Continuous monitoringAlerts in minutesFree plan available