← Back to blog

How to Find Hardcoded API Keys in Your Cursor-Built App

If you built your app with Cursor or any AI assistant there is a real chance you have hardcoded secrets in plain sight.

Asenvra

You built your app with Cursor. It's fast. The code is clean. Everything works.

But there's a problem you probably haven't noticed: Cursor (and every AI code assistant) has a habit of putting your API keys directly in your source code.

Not on purpose. Not maliciously. It's just that when the AI needs to call an API, the simplest thing is to reference the key right there in the code. And since it "works," neither you nor the AI thinks to move it.

Why This Is Dangerous

Hardcoded secrets are the #1 security issue in vibe-coded apps. Here's why:

  1. Anyone who sees your code has your keys. If your repo is public on GitHub, your keys are public. If it's private, any collaborator has access.

  2. GitHub is constantly scanned. Automated tools crawl public repositories looking for API keys, tokens, and passwords. They find thousands every day.

  3. Keys in Git history are permanent. Even if you remove the key from your code and commit, it's still in the Git history. Anyone who clones the repo can find it.

  4. The consequences are real. A stolen Stripe key means surprise charges. A stolen AWS key means someone spinning up expensive instances. A stolen database key means all your user data is exposed.

The Most Common Patterns

Here are the specific patterns we see over and over in Cursor-built apps:

Stripe Keys

// Cursor often does this:
const stripe = new Stripe('sk_live_abc123...')

// Instead of this:
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY)

OpenAI Keys

// AI assistants love to put this directly in the code:
const openai = new OpenAI({ apiKey: 'sk-proj-abc123...' })

// The safe way:
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY })

Supabase Service Role Keys

// This is the most dangerous one — service role keys bypass all RLS:
const supabase = createClient(url, 'eyJhbGciOi...')

// Never put service role keys in client code. Use the anon key for client:
const supabase = createClient(url, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY)

Database Connection Strings

# Cursor often hardcodes these:
DATABASE_URL = "postgresql://user:password@host:5432/db"

# Use environment variables instead:
DATABASE_URL = os.environ.get("DATABASE_URL")

Generic Pattern: Variable Assignment

// Any of these patterns is a red flag:
const API_KEY = "abc123..."
const SECRET_KEY = "sk-..."
const password = "mypassword123"
const db_password = "admin"

How to Find Them

You have three options:

Option 1: Manual Search (Slow but Thorough)

Open your codebase and search for these patterns:

  • sk_live_, sk_test_ (Stripe)
  • sk- followed by long strings (OpenAI)
  • AKIA (AWS Access Key IDs)
  • AIza (Google API keys)
  • ghp_ (GitHub tokens)
  • eyJ (JWT tokens)
  • Any variable named api_key, secret_key, password, token with a string value

Option 2: Git History Check

Check if secrets were ever committed:

git log --all -p | grep -i "sk_live\|sk_test\|AKIA\|AIza\|ghp_\|password.*="

Option 3: Use Asenvra (Fast and Complete)

Asenvra's ShipCheck scanner runs 19 checks on your codebase, including a comprehensive hardcoded secret detection that covers:

  • OpenAI-style API keys
  • Stripe live and test keys
  • AWS Access Key IDs
  • Google API keys
  • GitHub personal access tokens
  • JWT tokens
  • Supabase service role keys
  • Generic variable assignment patterns

It finds them using both regex scanning and AST analysis — so it catches patterns that simple text search misses.

What to Do When You Find One

  1. Rotate the key immediately. Generate a new key in the provider's dashboard. The old key is compromised — even if you remove it from code, it's still in Git history.

  2. Move it to an environment variable. Create a .env file (never commit this to Git) and reference it with process.env.YOUR_KEY_NAME.

  3. Add it to .env.example. List the variable name (not the value) so other developers know they need to set it.

  4. Add .env to .gitignore. Make sure .env, .env.local, and .env*.local are all in your .gitignore.

  5. Clean Git history if needed. If the key was ever committed, use git filter-branch or BFG Repo-Cleaner to remove it from history. Then rotate the key.

The Cursor-Specific Problem

Here's why this is especially common in Cursor-built apps:

  • Cursor optimizes for "it works now." When you ask it to add Stripe, it puts the key inline because that's the fastest path to a working implementation.

  • Cursor doesn't know about your deployment setup. It doesn't know whether you're using Vercel env vars, Railway env vars, or a local .env file. So it uses the simplest approach: put it in the code.

  • Cursor generates code in isolation. Each file is generated independently. Cursor might create a Stripe integration in one file, an auth system in another, and neither knows the other exists. There's no global "move all secrets to env vars" step.

Preventing It Going Forward

  1. Set up your .env file before you start coding. Add all the keys you know you'll need (Stripe, OpenAI, Supabase, etc.) before asking Cursor to implement anything.

  2. Add a rule to Cursor's instructions. Tell it: "Never put API keys, secrets, or tokens directly in source code. Always use process.env.VARIABLE_NAME."

  3. Run Asenvra before every deploy. ShipCheck catches hardcoded secrets, mock data, broken configs, and 16 other issues that AI assistants leave behind.

  4. Use .env.example as documentation. Keep it updated as you add new integrations. It's a living document of what your app needs.

The Bottom Line

If you built your app with Cursor (or any AI assistant), there is a real chance you have hardcoded secrets in plain sight. The AI put them there because it was the fastest way to make things work. But "fastest to make it work" and "safe to ship to production" are very different things.

Don't ship with hardcoded secrets. It takes minutes to move them to environment variables. It takes months to recover from a compromised key.

Scan your own code free — no credit card needed

Scan your own code free

Upload your project or paste a GitHub URL. Get a plain-English report of exactly what to fix before you ship.

Run Your First Scan Free →

No credit card needed · 3 free scans/month

Check your app's VibeScore →