In May 2026, we scanned a Cursor-built SaaS platform handling real customer data. The app looked polished. The onboarding flow worked. Stripe was integrated. The founder had shipped fast — exactly what Cursor is designed for. The Launch Readiness Score came back at 61/100.

The top finding: the /api/auth/login endpoint had no rate limiting. Zero. Our scanner sent 10,000 requests in under 60 seconds and the server responded to every single one. A credential-stuffing attack — where an attacker cycles through a list of known email/password pairs stolen from other breaches — could run undetected, indefinitely, until a real user got locked out of their own account.

This is not a Cursor problem. Cursor is an exceptional tool — the fastest path from idea to deployed product that exists right now. The problem is that security controls do not generate themselves. Cursor generates code. It does not generate a threat model. It does not configure rate limiting middleware. It does not enable Supabase Row Level Security on your tables. It does not inject HTTP security headers into your server responses. These are decisions that sit outside the code-generation loop — and most founders do not know they are missing until something goes wrong.

This guide covers the five most common vulnerability classes we see in Cursor-built apps, how each one manifests in generated code, what the real-world risk is, and exactly what to do to fix it. At the end, there is a free tool that runs the entire audit in under two minutes.

Scan your Cursor-built app — free
No code access. No sign-up. Results in under 2 minutes.
Scan your Cursor-built app — free →

What Cursor Does (And Doesn't) Secure by Default

Cursor is an AI-powered IDE, not a security framework. Understanding that distinction precisely is the first step to shipping a safe product.

What Cursor does well: it generates structurally correct code, follows common library conventions, and produces patterns that work. When you ask it to build an authentication system using a popular library like NextAuth or Supabase Auth, it generates code that authenticates users correctly. The login flow works. Sessions persist. Passwords are hashed. These functional correctness properties are solid.

What Cursor does not do is harden that code against adversarial conditions. There is a significant difference between code that works for legitimate users and code that holds up against an attacker. The distinction shows up in four areas consistently:

Rate limiting is an infrastructure concern, not an application concern. Cursor generates application-layer code. Whether your server applies a request rate limit before that code runs depends on your hosting configuration, your middleware stack, or an API gateway rule. Cursor does not configure those. It does not know whether you are deploying to Vercel, Railway, or a bare EC2 instance. It does not add rate limiting middleware unless you explicitly ask for it — and even then, the configuration needs to be tested.

HTTP security headers require server-level configuration. Headers like Content-Security-Policy, X-Frame-Options, and Strict-Transport-Security are set by the web server or a framework middleware layer, not by application logic. Cursor will generate a Next.js app. It will not automatically add a next.config.js headers block that configures CSP. That step is invisible unless you know to look for it.

Database access control defaults are permissive. Supabase ships with Row Level Security disabled by default on new tables. Cursor-generated code that creates a table and queries it will work — but unless RLS is explicitly enabled and policies are written, any authenticated user can query any row in that table. Cursor writes the queries. It does not write the policies.

Secrets appear in source code during development and can stay there. Cursor assists with code that references environment variables, but during rapid prototyping it is common to hardcode API keys temporarily. Those keys end up in git history, in bundled JavaScript files, or in committed .env files. Cursor does not scan for secrets or warn you when a key appears where it should not.

None of this means Cursor-built apps are inherently unsafe. It means they require a deliberate security pass before they handle production data. The rest of this guide shows you exactly what that pass needs to cover.

Top 5 Vulnerability Classes in Cursor-Built Apps

These figures come from our scan dataset across apps submitted to Launch Ready Code's State of Vibe Code Security report, June 2026. The percentages reflect the share of Cursor-built apps where each issue was detected.

P0 Critical

No Rate Limiting on Auth Endpoints

73% of scanned Cursor-built apps

What it is. Rate limiting restricts how many requests a single client can make to an endpoint within a time window. Without it, an attacker can make unlimited attempts against your login, password reset, or OTP verification endpoint.

Why Cursor code misses it. Cursor generates the route handler — the function that receives a login request, checks the password, and returns a session token. That function is complete and correct. But rate limiting sits upstream of the route handler, at the middleware layer. It requires choosing a storage backend (usually Redis or an in-memory store), configuring window size and request limits, and wiring the middleware to the right routes. None of that is implied by the route handler itself, and Cursor will not add it unless you ask.

The real risk. Credential stuffing. Attackers buy breach databases containing millions of email/password pairs and run automated scripts against login endpoints. Without rate limiting, a Cursor-built app will happily respond to every single request. A list of 100,000 credentials can be tested in under an hour. Real user accounts get compromised. You are liable.

VULNERABLE — /api/auth/login (Cursor-generated pattern, no throttle)
// app/api/auth/login/route.ts
export async function POST(req: Request) {
  const { email, password } = await req.json();
  const user = await db.users.findUnique({ where: { email } });
  if (!user || !await bcrypt.compare(password, user.passwordHash)) {
    return Response.json({ error: 'Invalid credentials' }, { status: 401 });
  }
  // ⚠ No rate limiting — 10,000 requests/min accepted silently
  return Response.json({ token: await createSession(user.id) });
}
P1 High

Missing HTTP Security Headers

84% of scanned Cursor-built apps

What it is. HTTP security headers are server-sent instructions that tell browsers how to handle your page content. The critical ones: Content-Security-Policy (blocks XSS by restricting script sources), X-Frame-Options (prevents clickjacking), Strict-Transport-Security (enforces HTTPS), X-Content-Type-Options (prevents MIME sniffing), and Referrer-Policy (controls information leakage).

Why Cursor code misses it. Cursor generates application code, not server configuration. A Next.js app generated by Cursor will have pages, API routes, and components — but the next.config.js file will typically be minimal. Security headers live in the headers() function inside next.config.js, or in a hosting-level configuration. Neither is in scope for most Cursor prompts.

The real risk. Without CSP, a single XSS vulnerability — perhaps in a third-party script you load — can steal session cookies and take over user accounts. Without X-Frame-Options, your login page can be embedded inside an attacker's page and used for clickjacking. Without HSTS, a user on a public Wi-Fi network is vulnerable to SSL stripping attacks.

FIX — next.config.js security headers block
// next.config.js — missing from most Cursor-generated projects
const securityHeaders = [
  { key: 'X-Frame-Options',        value: 'DENY' },
  { key: 'X-Content-Type-Options',  value: 'nosniff' },
  { key: 'Referrer-Policy',         value: 'strict-origin-when-cross-origin' },
  { key: 'Strict-Transport-Security',value: 'max-age=63072000; includeSubDomains' },
  { key: 'Content-Security-Policy',  value: "default-src 'self'; script-src 'self'" },
];
P0 Critical

Supabase RLS Disabled on User-Facing Tables

47% of scanned Cursor-built apps

What it is. Supabase Row Level Security (RLS) is a PostgreSQL feature that restricts which rows a database user can read, insert, update, or delete. Without RLS enabled and policies defined, every authenticated user in your app can — in principle — read every row in every table, including other users' data.

Why Cursor code misses it. Cursor generates the Supabase client calls that query your tables. It does not generate the SQL migration that enables RLS and defines access policies. When Cursor writes supabase.from('profiles').select('*'), it trusts that the database layer will enforce access control. Supabase does not enforce RLS unless you explicitly turn it on, table by table, in the Supabase dashboard or via a migration file.

The real risk. A user who knows your table names (often guessable from your API responses) can query your entire user table, extract emails and profile data, and read other users' private records — all using their own valid session token. This is an IDOR (Insecure Direct Object Reference) at the database layer, and it is the single most common critical finding we see in Supabase-backed apps.

VULNERABLE — no RLS means any user can read all rows
-- Supabase default state after Cursor-generated table creation
CREATE TABLE user_documents (
  id        uuid DEFAULT gen_random_uuid() PRIMARY KEY,
  user_id   uuid REFERENCES auth.users(id),
  content   text,
  created_at timestamptz DEFAULT now()
);
-- ⚠ RLS not enabled. Any authenticated user can SELECT * FROM user_documents.
P0 Critical

Exposed API Keys in Client-Side JS Bundles

23% of scanned Cursor-built apps

What it is. When you build a frontend application, your JavaScript is compiled into a bundle and sent to every browser that loads your page. If any secret API keys — for OpenAI, Stripe, SendGrid, Twilio, or any other service — are referenced in client-side code or in environment variables prefixed with NEXT_PUBLIC_, they end up visible in that bundle. Anyone with DevTools open can extract them.

Why Cursor code misses it. During rapid development, Cursor often generates code that calls third-party APIs directly from the frontend, especially for AI features, email integrations, or payment flows. This works locally. The mistake is using a server-side API key in that client code, or storing the key in an environment variable intended for the browser. The NEXT_PUBLIC_ prefix in Next.js is particularly dangerous because it explicitly opts a variable into the client bundle.

The real risk. An exposed OpenAI key can be used by anyone who finds it, running API calls billed to your account. An exposed Stripe secret key allows someone to create charges, read customer payment data, and issue refunds. An exposed database connection string gives direct write access to your production data. These are account-takeover and financial-loss scenarios, not theoretical risks.

VULNERABLE — server secret in client-side call
// components/AiFeature.tsx — visible in the JS bundle
const openai = new OpenAI({
  apiKey: process.env.NEXT_PUBLIC_OPENAI_KEY, // ⚠ shipped to every browser
  dangerouslyAllowBrowser: true,
});
// This key is now in your compiled bundle at /_next/static/chunks/...
P1 High

CORS Misconfiguration on Auth Routes

31% of scanned Cursor-built apps

What it is. Cross-Origin Resource Sharing (CORS) is a browser security mechanism that controls which domains can make requests to your API. When a CORS policy uses a wildcard origin (Access-Control-Allow-Origin: *), it allows any website in the world to make credentialed requests to your endpoints.

Why Cursor code misses it. Cursor-generated Express or Fastify APIs frequently include a cors() middleware call with default or wildcard settings, because that is the fastest way to stop the browser from blocking requests during development. The wildcard setting solves the CORS error immediately. It also removes the protection that CORS is supposed to provide — and it stays in place when the code ships to production.

The real risk. A wildcard CORS policy on an authentication route means a malicious website can perform authenticated requests to your API using a visitor's existing session cookies. This enables CSRF-style attacks even when you have CSRF tokens, because the attacker's origin is trusted. On an API that returns user data in response to a credentialed request, it enables cross-origin data theft.

VULNERABLE — wildcard CORS on auth endpoint
// server.ts — Cursor-generated Express setup
import cors from 'cors';

app.use(cors({
  origin: '*',          // ⚠ allows any origin — never on auth routes
  credentials: true,   // ⚠ credentials: true + wildcard = cross-origin theft
}));

app.post('/api/auth/token', issueToken); // now accessible from any site

Security Comparison: Cursor vs Lovable vs Bolt

Cursor is not alone in leaving these controls unconfigured. Lovable and Bolt share the same gaps — because all three tools are code generators, not security platforms. The table below reflects our scan data across all three tools as of June 2026.

Security Feature Cursor Lovable Bolt
Rate limiting auto-configured No No No
Supabase RLS enabled by default Manual Manual Manual
HTTP security headers auto-set No No No
Secrets management enforced Manual Manual Manual
Auth hardening (hashing, session) Partial Partial No
CORS configured to known origins No No No
Dependency vulnerability scanning No No No

The pattern is consistent: every AI code generator leaves security configuration as a manual step. The difference between a safe app and a vulnerable one is whether the founder knows those steps exist.

How a URL-Based Security Audit Works

The standard assumption about security audits is that they require code access — you hand over your repository, someone reads through it for two weeks, and you get a report. That model works for enterprise security reviews. It does not work for a founder who shipped last week and needs to know if they are safe today.

A URL-based security audit works differently. It inspects your running application — the same surface an attacker would target — and surfaces what is detectable without ever touching your source code.

Here is what our scanner checks from a URL alone. When you submit your app's URL to Launch Ready Code, our agent makes a series of controlled HTTP requests to your domain, your API endpoints, and any subdomains it can discover. It inspects every response header to check for the presence and correct configuration of CSP, HSTS, X-Frame-Options, and other security headers. It probes authentication endpoints for rate limiting by sending calibrated request sequences and measuring response patterns. It checks whether your JavaScript bundles contain strings matching known API key formats — OpenAI keys, Stripe keys, AWS access keys, and others. It tests CORS policy by sending requests from synthetic attacker origins and observing the Access-Control-Allow-Origin response.

What it cannot check from a URL: your database RLS configuration (that requires a database query), internal business logic errors, or vulnerabilities that only manifest after authentication with a specific user role. The audit covers your external attack surface — which is where 73% of real-world exploits start.

The result is a scored report across four dimensions — Security, Reliability, Performance, and Monitoring — delivered in under two minutes. The free tier returns your overall score. The full Launch Readiness Audit Report at $499 returns every finding with severity, file reference where detectable, recommended fix, and a prioritized remediation roadmap. No code access. No onboarding call. No waiting period.

How to Fix Each Vulnerability Class

Fix 1 — Rate Limiting

For Next.js apps, add @upstash/ratelimit with a Redis backend (Upstash has a free tier). Wrap your auth route handler with a rate limit check: maximum 5 attempts per IP per 15-minute window on login, 3 per hour on password reset. For Express apps, use express-rate-limit with a Redis store. Apply the middleware specifically to /api/auth/* routes — not globally, which can affect legitimate high-frequency API consumers. Test the configuration with a local script before deploying.

Fix 2 — HTTP Security Headers

In Next.js, add a headers() export to your next.config.js that returns an array of header objects for all routes. Start with conservative values: X-Frame-Options: DENY, X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin, Strict-Transport-Security: max-age=63072000; includeSubDomains. For CSP, start with default-src 'self' and expand as needed — CSP breaks things if you set it too aggressively. Verify headers are present using the Network tab in Chrome DevTools after deploying.

Fix 3 — Supabase RLS

In the Supabase dashboard, go to Authentication → Policies. For every table that stores user data, enable RLS and create a SELECT policy with condition auth.uid() = user_id. Do the same for INSERT, UPDATE, and DELETE. Write a migration file so the policy is version-controlled: ALTER TABLE user_documents ENABLE ROW LEVEL SECURITY; followed by CREATE POLICY "Users see own docs" ON user_documents FOR SELECT USING (auth.uid() = user_id);. Test by signing in as two different users and confirming each can only read their own rows.

Fix 4 — Exposed API Keys

Move all third-party API calls that use secret keys to server-side API routes. In Next.js, this means /app/api/ routes or Server Actions. Remove NEXT_PUBLIC_ prefix from any key that should not be in the browser. Run grep -r "NEXT_PUBLIC_" .env* to audit your environment files. Run strings .next/static/chunks/*.js | grep -E "sk-|rk_live_|AKIA" on your build output to check for leaked keys. Rotate any key that has been exposed in a deployed build — assume it was already found.

Fix 5 — CORS Configuration

Replace the wildcard origin with an explicit allowlist. For production, this should be only your frontend domain: origin: ['https://yourdomain.com']. For local development, use an environment variable: origin: process.env.ALLOWED_ORIGINS?.split(',') || []. Never combine credentials: true with origin: '*' — this combination is rejected by browsers for cross-origin credentialed requests and gives a false sense of security. Apply the strictest CORS policy to auth routes, and consider a separate, slightly more permissive policy for public API routes if needed.

Real Finding: Cursor SaaS Scanned May 2026

LRC Editorial Pass — May 2026
61/100

App type: B2B SaaS, Cursor-built, Next.js + Supabase, live with paying customers.

Top finding: No rate limiting on /api/auth/login — accepting 10,000+ requests per minute with no throttle. Our scanner completed a full credential-stuffing simulation sequence in under 4 minutes with zero resistance. The endpoint returned valid session tokens for matching credentials without triggering any alert.

Additional findings: Missing CSP and HSTS headers (P1). Supabase projects table with RLS disabled — any authenticated user could enumerate all project records across all accounts (P0). The NEXT_PUBLIC_OPENAI_KEY variable present in the production JS bundle (P0).

Founder action taken: Added Upstash rate limiting within 48 hours. RLS enabled on all tables within 72 hours. OpenAI key rotated immediately. Security headers added on next deploy. Score after remediation: 88/100.

This is not an unusual case. The 61/100 score is close to the median for Cursor-built apps in our dataset. The vulnerabilities were not caused by bad engineering decisions — they were caused by gaps between what Cursor generates and what production security requires. Closing those gaps took this founder less than a week.

Frequently Asked Questions

Is Cursor safe for building production apps?
Cursor is a powerful IDE that accelerates development significantly, but it does not automatically configure security controls. Apps built with Cursor are only as secure as the developer's security knowledge and the post-build security hardening applied. Common gaps include missing rate limiting, absent HTTP security headers, and exposed API keys in client-side bundles. Cursor-built apps can absolutely be production-safe — they just require a deliberate security pass before handling real user data. Our scanner can tell you where you stand in under two minutes.
What security vulnerabilities does Cursor code typically have?
The most common vulnerabilities in Cursor-built apps are: no rate limiting on authentication endpoints (found in 73% of scanned apps), missing HTTP security headers such as CSP, X-Frame-Options, and HSTS (84%), Supabase Row Level Security disabled on user-facing tables (47%), API keys exposed in client-side JavaScript bundles (23%), and CORS misconfiguration with wildcard origins on auth routes (31%). These figures come from Launch Ready Code's scan dataset as of June 2026. All five are fixable — none require rewriting your application.
How do I audit a Cursor-built app?
You can audit a Cursor-built app in two ways. Manually: check your rate limiting middleware, review HTTP response headers in Chrome DevTools under the Network tab, inspect your Supabase RLS policies in the dashboard, search your production JS bundle for API key patterns, and review your CORS configuration. Automatically: use Launch Ready Code's free scan at launchreadycode.com — paste your URL and get a scored security report in under 2 minutes with no code access required. The full Launch Readiness Audit Report at $499 includes every finding with severity, file references, and a prioritized fix roadmap.
How much does a Cursor security audit cost?
A basic Launch Readiness Score scan is free at launchreadycode.com. The full Launch Readiness Audit Report — which covers all four dimensions: security, reliability, performance, and monitoring — is $499 one-time and is delivered in under 2 minutes. For ongoing monitoring, subscriptions start at $149 per month. If you need hands-on remediation by a Fractional CTO who implements the fixes as reviewed pull requests, the DFY Technical Setup starts at $1,999 and includes Month 1 of the Growth Retainer. See launchreadycode.com/pricing for the full breakdown.
Does Cursor write secure authentication code?
Cursor can generate authentication code that follows common patterns and uses standard libraries correctly — passwords are hashed, sessions are managed, JWTs are signed. What it does not automatically add: rate limiting to login endpoints, CORS restrictions to auth routes, session expiry and rotation policies, or account lockout after repeated failures. The authentication logic is functionally correct. The hardening layer is missing. Before going live with an auth system generated by Cursor, review every endpoint in your /api/auth/ path against the checklist in this guide, or run a free scan at launchreadycode.com to get a scored assessment of your auth surface.

Know your score before an attacker does

Paste your URL. Get your Launch Readiness Score in under 2 minutes.
No code access. No sign-up required. Free.

Scan your Cursor-built app — free →