Windsurf
Exposed API Keys

Exposed API Keys in Windsurf Projects

Windsurf's Cascade agent generates complete application stacks including third-party integrations. When it wires up services like OpenAI, Stripe, or database providers, it often embeds API keys directly in client-side code rather than using server-side environment variables.

Scan Your Windsurf App

How It Happens

Windsurf's Cascade agent generates integration code by producing the most direct path to a working feature. When a user prompts "add AI chat using OpenAI," Cascade writes a fetch call to the OpenAI API with the key inline because that is the simplest implementation that works. Because Windsurf generates multi-file projects, API keys can appear in unexpected locations. Cascade might place a key in a utility file, a configuration module, a React hook, and a test file all in the same generation pass. The developer may fix one location but miss the duplicates. Windsurf projects also suffer from the environment variable prefix problem. Cascade generates .env files and references keys with NEXT_PUBLIC_ or VITE_ prefixes, not understanding that these prefixes expose the values to the browser. The developer sees an environment variable and assumes it is safe, not realizing the prefix makes it public.

Impact

Exposed API keys in Windsurf projects give attackers free access to paid services. OpenAI and Anthropic keys are frequently abused for crypto mining prompts and spam generation, running up thousands of dollars in charges within hours of exposure. Stripe secret keys (sk_live_) allow attackers to view all customer data, issue arbitrary refunds, and create charges. Database connection strings provide direct SQL access that bypasses all application-level security. Because Windsurf generates Git-ready projects, developers often push the initial commit with keys embedded. Even if the keys are later removed from code, they persist in Git history and can be extracted by anyone with repository access.

How to Detect

Search all files in the Windsurf-generated project for common key patterns: sk- (Stripe/OpenAI), key-, AIza (Firebase), and any long alphanumeric strings in JavaScript or TypeScript files. Pay special attention to files in src/, lib/, utils/, and config/ directories. Check .env files for variables with NEXT_PUBLIC_ or VITE_ prefixes that contain secret values. These are exposed to the browser regardless of the .env file being gitignored. Vibe App Scanner analyzes the deployed application bundle and detects API keys using pattern matching and entropy analysis, catching secrets that manual review might miss.

How to Fix

Create server-side API routes for all third-party service calls. The frontend should call your own API endpoint, which holds the secret key and proxies the request. Windsurf generates Next.js API routes or Express endpoints that can serve this purpose. Audit every environment variable. Remove NEXT_PUBLIC_ or VITE_ prefixes from any variable containing a secret value. Only truly public values (Supabase anon key, Firebase API key) should have public prefixes. Run a secrets scanner like gitleaks on the repository before the first push. Add it as a pre-commit hook to prevent future key commits. Rotate any key that has appeared in a commit, even if subsequently removed. Create a .windsurfrules file instructing Cascade to always use server-side environment variables for API keys and never hardcode secrets.

Code Examples

AI integration generated by Windsurf

Vulnerable
// Windsurf-generated client-side code
const response = await fetch(
  'https://api.openai.com/v1/chat/completions',
  {
    headers: {
      'Authorization': 'Bearer sk-proj-abc123...',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ model: 'gpt-4', messages }),
  }
)
Secure
// Server-side API route (app/api/chat/route.ts)
export async function POST(req: Request) {
  const { messages } = await req.json()
  const response = await fetch(
    'https://api.openai.com/v1/chat/completions',
    {
      headers: {
        'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ model: 'gpt-4', messages }),
    }
  )
  return Response.json(await response.json())
}

Frequently Asked Questions

Does Windsurf store my API keys on its servers?

Windsurf sends file context to its AI model for code generation. If API keys are in open files, they may be included in that context. Always use environment variables rather than hardcoded keys to minimize this exposure.

Which API keys are safe to have in Windsurf-generated frontend code?

Supabase anon keys and Firebase API keys are designed to be public and are safe in frontend code. All other keys (OpenAI, Stripe secret, database passwords, service_role keys) must be kept server-side.

How do I prevent Cascade from hardcoding API keys?

Add a .windsurfrules file with explicit instructions: "Never hardcode API keys. Always use process.env for secrets. Only use NEXT_PUBLIC_ prefix for Supabase anon keys and Firebase API keys." Also have a server-side API route pattern in the project for Cascade to follow.

Is Your App Vulnerable?

VAS automatically scans for exposed api keys and other security issues in Windsurf apps. Get actionable results with step-by-step fixes.

Scans from $5, results in minutes.