← Back to Blog
CURSOR March 25, 2026 5 min read

Your Cursor App is Probably Leaking API Keys

Cursor is an incredible tool. You describe a feature, and it writes the code. The problem is that AI coding assistants optimize for "does it work?" -- not "is it secure?" And one of the most common mistakes they make is putting API keys, database credentials, and secret tokens directly in client-side JavaScript.

We have scanned thousands of apps built with Cursor, and roughly 1 in 3 leak at least one secret in their JavaScript bundle. Here is how it happens, how to find it, and how to fix it.

How Cursor Leaks Your Secrets

When you ask Cursor to "add Stripe payments" or "connect to the database," it writes code that works. But it often puts the secret key right in the file that gets shipped to the browser. Here are the patterns we see most often:

Pattern 1: Stripe secret key in client code

// Cursor often generates this in a React component:
import Stripe from 'stripe'
const stripe = new Stripe('sk_live_51abc123...')  // SECRET KEY IN CLIENT CODE

// Anyone who views your page source can see this key
// and charge any amount to any customer

Pattern 2: The NEXT_PUBLIC_ prefix trap

// In Next.js, any env var starting with NEXT_PUBLIC_ is bundled
// into client JavaScript. Cursor sometimes suggests:
const apiKey = process.env.NEXT_PUBLIC_OPENAI_KEY
// This means your OpenAI key is visible to everyone

// Same problem in Vite apps with VITE_ prefix:
const secret = import.meta.env.VITE_DATABASE_URL

Pattern 3: Hardcoded credentials in utility files

// lib/firebase.ts -- Cursor writes this, you commit it
const firebaseConfig = {
  apiKey: "AIzaSyB...",
  authDomain: "myapp.firebaseapp.com",
  projectId: "myapp-prod",
  // The API key here is meant to be public, BUT
  // Cursor sometimes also adds:
  serviceAccountKey: "-----BEGIN PRIVATE KEY-----..."
  // ^ This is catastrophic if it ends up in client code
}

How to Find Leaked Keys in Your App

You can check manually right now. Open your deployed app in Chrome, press F12, go to the Sources tab, and search through your JavaScript files for these patterns:

# Strings to search for in your JS bundles:
sk_live_          # Stripe secret key
sk_test_          # Stripe test key (still dangerous)
AKIA              # AWS access key prefix
AIzaSy            # Google/Firebase API key
-----BEGIN        # Private keys (RSA, service accounts)
mongodb+srv://    # Database connection strings
postgres://       # PostgreSQL connection strings
redis://          # Redis connection strings
ghp_              # GitHub personal access token
xoxb-             # Slack bot token
sk-               # OpenAI API key

Or, more practically, use vibeAudit. Our scanner downloads and decompresses your JavaScript bundles, then runs pattern matching against 40+ known secret formats. It takes 30 seconds.

How to Fix It

The fix is simple in principle: secrets must never be in client-side code. They belong on the server.

Step 1: Move secrets to server-side API routes

// BEFORE: Secret in client component (DANGEROUS)
'use client'
import Stripe from 'stripe'
const stripe = new Stripe(process.env.NEXT_PUBLIC_STRIPE_SECRET!)

// AFTER: Secret in server-side API route (SAFE)
// app/api/create-checkout/route.ts
import Stripe from 'stripe'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!) // No NEXT_PUBLIC_ prefix

export async function POST(request: Request) {
  const session = await stripe.checkout.sessions.create({
    // ...checkout config
  })
  return Response.json({ url: session.url })
}

Step 2: Audit your environment variable names

# .env.local — audit every variable

# SAFE: These are server-only (no NEXT_PUBLIC_ prefix)
STRIPE_SECRET_KEY=sk_live_...
DATABASE_URL=postgres://...
OPENAI_API_KEY=sk-...

# PUBLIC: These are bundled into client JS
# Only put truly public values here
NEXT_PUBLIC_SUPABASE_URL=https://abc.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...

Step 3: Rotate any exposed keys immediately

If a secret was ever in your client code -- even briefly -- assume it has been compromised. Rotate it. This means generating a new key in the provider's dashboard and updating your server environment variables. The old key should be revoked.

Prevention Going Forward

When using Cursor or any AI coding assistant, always review the code it generates before committing. Specifically look for:

  • Any string that looks like a key or token in a client-side file
  • Environment variables with NEXT_PUBLIC_ or VITE_ prefix that contain secrets
  • Direct database or API instantiation in React components
  • Any file with 'use client' at the top that imports a backend SDK

Better yet, run vibeAudit after every deploy. Our JS bundle scanner catches these leaks automatically, and the free scan covers secret detection.

Is your app vulnerable?

Scan your app for free and find out in minutes.

Scan Your App Free