Fairy
Resources

How to Avoid: Secrets Hardcoded in Source

July 19, 2026 · 6-minute read · Fairy

The short answer

Prevent hardcoded secrets in AI-generated code by moving all API keys and tokens to environment variables (process.env), adding .env files to .gitignore, and immediately rotating any credentials that were committed to version control. AI models frequently inline secrets from training examples, making automated scanning essential before merge.

How to Avoid: Secrets Hardcoded in Source

Hardcoded secrets in AI-generated code are a critical security vulnerability. When API keys, tokens, or credentials appear as string literals in source files, they get committed to version control and become permanently accessible to anyone with repository access—including attackers scanning public repositories.

The fix is straightforward: move all secrets to environment variables using process.env, ensure .env files are in .gitignore, and immediately rotate any credentials that were ever committed. But preventing this failure mode requires understanding why AI produces it and establishing systematic detection before code reaches production.

Why AI Models Hardcode Secrets

AI code generation models learn from vast datasets of existing code, including tutorials, documentation examples, and Stack Overflow answers. These sources frequently inline credentials for demonstration purposes:

// Tutorial-style code AI learns from
const stripe = require('stripe')('sk_live_abc123xyz789');

The model sees this pattern thousands of times and associates it with "working code." It doesn't understand that tutorials use placeholder or test keys, and that production applications require a different approach. When you ask for Stripe integration, the model replicates what it learned—including the inline secret pattern.

This isn't a bug in the model's reasoning; it's a fundamental limitation. AI optimizes for generating syntactically correct, functional code. Security considerations like credential management are contextual decisions the model cannot reliably make.

Common Patterns in AI-Generated Secret Exposure

Fairy's reviews consistently identify several secret exposure patterns in AI-generated code:

Direct API key assignment:

const STRIPE_KEY = 'sk_live_4eC39HqLyjWDarjtT1zdp7dc';
const client = new Stripe(STRIPE_KEY);

Secrets in configuration objects:

const config = {
  apiKey: 'AIzaSyD-9tSrke72PouQMnMX-a7eZSW0jkFMBWY',
  authDomain: 'myapp.firebaseapp.com',
  databaseURL: 'https://myapp.firebaseio.com'
};

Database connection strings with credentials:

const connectionString = 'postgres://admin:secretpassword@db.example.com:5432/production';

Each of these patterns represents a critical vulnerability. Once committed, these secrets exist in git history indefinitely—even if you delete them in a subsequent commit.

The Correct Pattern: Environment Variables

Every secret belongs in environment variables, accessed through process.env and never committed to version control.

Before: Hardcoded Secret

// CRITICAL VULNERABILITY: Secret exposed in source
const stripe = require('stripe')('sk_live_4eC39HqLyjWDarjtT1zdp7dc');

After: Environment Variable

// Secure: Secret loaded from environment
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

if (!process.env.STRIPE_SECRET_KEY) {
  throw new Error('STRIPE_SECRET_KEY environment variable is required');
}

The environment variable approach keeps secrets out of source code entirely. Your .env file contains the actual values:

STRIPE_SECRET_KEY=sk_live_4eC39HqLyjWDarjtT1zdp7dc
DATABASE_URL=postgres://admin:secretpassword@db.example.com:5432/production

And your .gitignore must include:

.env
.env.local
.env.*.local

Framework-Specific Pitfalls

Next.js and NEXT_PUBLIC_ Exposure

Next.js uses the NEXT_PUBLIC_ prefix to expose environment variables to client-side code. This creates a dangerous pattern where developers—or AI—accidentally prefix secrets with NEXT_PUBLIC_:

// CRITICAL: This secret is bundled into client JavaScript
const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL,
  process.env.NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY  // WRONG
);

Variables prefixed with NEXT_PUBLIC_ are embedded in the JavaScript bundle sent to browsers. Anyone can view them in browser developer tools. Service role keys, API secrets, and database credentials must never use this prefix.

Correct pattern:

// Client-side: Only use the anon key (designed for public use)
const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
);

// Server-side only: Service role key stays server-side
// In an API route or server component
const supabaseAdmin = createClient(
  process.env.SUPABASE_URL,
  process.env.SUPABASE_SERVICE_ROLE_KEY  // No NEXT_PUBLIC_ prefix
);

Supabase Service Role Keys

Supabase service role keys bypass Row Level Security (RLS) entirely. If this key appears in client-side code, attackers gain full database access regardless of your security policies.

AI frequently generates this vulnerability because example code in tutorials often uses the service role key for simplicity. The model doesn't distinguish between server-side admin operations and client-side user interactions.

Detecting Hardcoded Secrets

Manual Review Signals

When reviewing AI-generated code, search for these patterns:

Automated Scanning

Pre-commit hooks catch secrets before they enter version control:

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.0
    hooks:
      - id: gitleaks

CI/CD pipelines should include secret scanning as a blocking check. If secrets are detected, the build fails and the PR cannot merge.

For AI-generated code specifically, Fairy Scout provides free AI code review that flags hardcoded secrets alongside other security and correctness issues before code reaches your repository.

Immediate Response: What to Do When Secrets Are Exposed

If you discover a committed secret, assume it's compromised. Git history is permanent, and secrets can be extracted even from deleted commits.

Step 1: Rotate the Credential Immediately

Go to the provider's dashboard (Stripe, AWS, Supabase, etc.) and generate a new key. Revoke the old one. This is the most critical step—without rotation, attackers who already harvested the key retain access.

Step 2: Update Environment Variables

Deploy the new credential through your environment variable management system. Verify the application works with the new key.

Step 3: Remove from Source Code

Fix the code to use process.env properly:

// Replace this
const stripe = require('stripe')('sk_live_EXPOSED_KEY');

// With this
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

Step 4: Consider History Cleanup

For private repositories, tools like git-filter-repo can rewrite history to remove the secret. This is optional—the credential is already compromised and rotated—but reduces the window for discovery.

For public repositories, assume the secret was scraped within minutes of the commit. Bots continuously scan GitHub for exposed credentials.

Building Systematic Prevention

One-time fixes aren't enough. AI will continue generating hardcoded secrets because the pattern exists in its training data. Prevention requires systematic controls.

Review Checkpoints

Every AI-generated code change should pass through verification that specifically checks for credential exposure. Fairy for Code provides expert verification of AI-generated code before it reaches production, catching secrets and other critical issues that automated tools miss.

Developer Education

Teams using AI code generation need to understand this failure mode. Treat AI output like code from a new junior developer—capable but needing review for security patterns they haven't yet internalized.

Environment Variable Infrastructure

Make the secure path the easy path. Provide developers with:

When using environment variables is as easy as hardcoding, developers (and AI) are less likely to take shortcuts.

The Stakes Are Higher Than You Think

Hardcoded secrets in AI-generated code aren't just a theoretical concern. Fairy's reviews consistently flag live production keys—sk_live_* Stripe keys, service role database credentials, and API secrets—hardcoded directly in source files destined for version control.

Each of these represents a potential breach. A single committed Stripe key can lead to fraudulent charges. An exposed database credential can mean complete data exfiltration. A leaked service role key can bypass every access control in your application.

The solution isn't to avoid AI-generated code. It's to implement verification that catches what AI doesn't understand. Environment variables, .gitignore configuration, and credential rotation are straightforward patterns. The challenge is ensuring they're applied consistently, especially when AI generates code that looks correct but carries critical security flaws.

For teams deploying AI-generated code at scale, Fairy Intelligence provides grounded Q&A on your codebase, helping identify credential management patterns and potential exposure points across your entire repository. Combined with expert verification through Fairy for Code, you maintain the productivity benefits of AI generation while ensuring secrets never reach production.

Frequently asked questions

Why does AI hardcode secrets in generated code?

AI models learn from training data that includes tutorial code and Stack Overflow examples where secrets are often inlined for simplicity. The model replicates this pattern without understanding that production code requires environment variables. It optimizes for 'working code' without security context.

How do I detect hardcoded secrets before committing?

Use pre-commit hooks with secret scanning tools that check for patterns like 'sk_live_', 'api_key', and 'secret'. Review AI-generated code specifically for string literals containing keys. Automated scanners should run on every pull request before merge.

What should I do if a secret was already committed to git?

Rotate the exposed credential immediately in the provider's dashboard—the secret is compromised regardless of whether you delete the commit. Remove the secret from code, add proper environment variable handling, and consider using git-filter-repo to purge history if the repository is private.

Are NEXT_PUBLIC_ environment variables safe for secrets?

No. Variables prefixed with NEXT_PUBLIC_ are bundled into client-side JavaScript and exposed to all users. Secret keys must never use this prefix. Only use NEXT_PUBLIC_ for truly public values like analytics IDs or public API endpoints.


Have AI-generated work you’d want verified? Connect with a Fairy → or run a free check with Scout.

More resources