Back to Security Academy
AI CODE SMELLSBEGINNER8 min readCWE-1188ZX-AI-SMELL-001ZX-SECRETS-001

The Developer's Guide to Auditing AI-Generated Web Applications

How AI code assistants systematically introduce security vulnerabilities, and a practical pre-deployment verification workflow.

Why AI coding tools introduce this vulnerability

Generative AI models prioritize "making it work quickly" over security boundaries. They frequently hardcode placeholder credentials in client components, disable CORS checks, bypass authentication in server actions, and use unescaped template literals for database queries.

Technical Vulnerability Analysis

When prompting tools like Cursor, Lovable, or v0 to build a feature (e.g. "Create a profile edit modal that uploads an avatar to S3"), models frequently generate client-side code containing raw API keys, unauthenticated endpoints, or mock auth fallbacks. In production, these flaws lead to credential harvesting and data breaches.

VULNERABLE IMPLEMENTATION (DO NOT USE)
// AI-Generated Component (DANGEROUS)
export function PaymentProcessor() {
  // AI placed private secret in client bundle!
  const STRIPE_SECRET_KEY = "sk_live_51M0..."; 
  
  async function chargeUser(amount: number) {
    await fetch("https://api.stripe.com/v1/charges", {
      headers: { Authorization: `Bearer ${STRIPE_SECRET_KEY}` },
      body: JSON.stringify({ amount })
    });
  }
}
The AI tool inlined a live secret API key directly into a client-side React component, making it visible to anyone inspecting page source or network traffic.

Remediation Playbook & Code Fixes

SECURE HARDENED PATCH (src/app/api/charge/route.ts)
import { NextRequest, NextResponse } from 'next/server';
import { requireAuth } from '@/core/rbac/authorization-guard';

export async function POST(req: NextRequest) {
  // 1. Authenticate requester server-side
  const auth = await requireAuth(req);
  const { amount } = await req.json();

  // 2. Secret remains exclusively in server environment
  const secretKey = process.env.STRIPE_SECRET_KEY;
  if (!secretKey) throw new Error("Stripe secret unconfigured");

  // Perform secure backend transaction...
  return NextResponse.json({ success: true });
}

Why this fix works: Keep all secrets server-side in secure API routes or server actions. Never prefix sensitive keys with NEXT_PUBLIC_.

Local CLI Verification Commands

Check production JavaScript bundles for leaked secret tokens.
curl -sI https://example.com/_next/static/chunks/app/page-*.js | grep -iE 'sk_live|private_key|token'
Expected result: (empty response - zero secrets exposed in frontend bundles)

Pre-Deployment Self-Audit Checklist

Knowledge Check: Verify Your Understanding

What is the primary architectural cause of the developer's guide to auditing ai-generated web applications?

Test your website for ZX-AI-SMELL-001

Run our 14 deterministic engines to inspect your live deployment.

Launch scan for this flaw