What's Working

Vibecoder
Security Review

AI-generated code has a specific security profile. It's predictable.

The code looks clean. Variable names are clear, structure is reasonable, it runs on the first try. But underneath, the security posture is a first draft — because the AI was optimizing for "works," not "resists attack." Credentials from training data end up hardcoded. Auth checks exist in the UI but not the API. Errors silently fail open. Packages that don't exist get installed.

Empirical testing shows 40–45% of AI-generated code contains OWASP Top 10 vulnerabilities. AI-assisted commits leak secrets at roughly twice the baseline rate. The patterns are predictable — which means they're findable.

This is a structured triage designed for that exact code. Two hours, nine phases, the specific holes that AI-built projects leave open.

The full review prompt is at the bottom. Copy it. Run it.

The Review

Nine Phases, Two Hours

Designed to complete in a single sitting. Each phase has a specific goal, search patterns, and clear criteria for what counts as a finding.

0

Recon

15 min

Understand what you're looking at before diving in. Identify the stack, find entry points, map the architecture. Check for AI-generated code markers — single contributor, bulk commits, boilerplate-heavy files, AI tool config.

1

Secrets & Keys

10 min

Find credentials that anyone with access to the code can steal. Hardcoded API keys, JWT secrets, database connection strings, OAuth secrets in client-side code, test credentials that work against production.

2

Auth & Identity

20 min

Trace how the app knows who you are and what you're allowed to do. Look for identity derived from things the user controls (URL params, request body), roles accepted without verification, admin panels hidden in the UI but open at the API level.

3

Data Access

15 min

Can you change an ID in the URL and see someone else's data? Check every route that accepts a record ID — does it verify the current user actually owns that record?

4

Injection & Execution

15 min

Five attack classes: SQL injection, cross-site scripting, prompt injection, remote code execution, and server-side request forgery. AI-generated code is particularly weak here because it optimizes for "works" not "resists attack."

5

Vibe-Code Pitfalls

15 min

The failures unique to AI-built code: hallucinated packages that attackers register, security checks that only exist in the UI, errors that silently fail open, placeholder values left in production, and overpermissioned cloud configs.

6

Infrastructure

10 min

CORS wide open, no rate limiting on login, file uploads with no validation, missing security headers, debug mode still on, Docker containers running as root.

7

Error Handling

5 min

Stack traces returned to users, passwords in log output, no monitoring endpoint, console.log as the only observability.

8

Documentation

15 min

Write it up. Every finding gets a location, an explanation, an attack scenario, and a specific fix.

The Pattern

Why AI Code Breaks Differently

Traditional code fails because developers make mistakes under pressure. AI code fails because it was optimized for a different goal: looking functional. These biases are systematic — once you know them, you see them everywhere.

The Happy-Path Bias

AI code works perfectly for the demo user. It breaks the moment a second user exists, an attacker probes an edge case, or a service returns an error. Ownership checks, rate limits, and input validation are absent because they're not needed to make the feature "work."

The Single-Context Illusion

Each file is generated in isolation. Cross-cutting concerns — authentication middleware, global error handling, consistent authorization — are the first casualties. The AI doesn't think "every route must check ownership." It thinks "this route should return the order."

The Confidence Problem

AI code looks authoritative. Clean names, helpful comments, clear structure. This creates false confidence — people trust it more than they should. The code reads like a senior engineer wrote it, even when it has the security posture of a first draft.

The Copy-Paste Provenance

AI training data includes tutorials and documentation that use placeholder credentials and simplified auth for teaching clarity. These patterns end up in production because the AI reproduces them faithfully and nobody knows to question them.

The Catalog

Vibe-Code Antipatterns

The specific things to look for. Each one shows up reliably in AI-built projects — not because the AI is bad, but because its incentives don't include "resist attack."

Hardcoded Example Creds

Look for

API keys from docs, password123, sk-proj-... in source files

Why

AI reproduces credentials from its training data — tutorials, Stack Overflow, documentation examples.

Frontend-Only Gates

Look for

Auth and role checks in UI components with no API equivalent

Why

AI builds features where the user sees them. It doesn't think about what happens when someone skips the UI.

Missing Ownership Checks

Look for

Database queries by ID without filtering to the current user

Why

AI doesn't model your access control requirements. It makes the feature work for one user, not against another.

Silent Error Swallowing

Look for

catch(e) { return defaultValue } — especially around auth

Why

AI treats errors as obstacles to remove. Its "safe" default is often to allow access when verification fails.

Hallucinated Packages

Look for

Dependencies that don't exist on npm/PyPI, or were just created

Why

AI invents plausible package names. Attackers register them. ~20% of AI-generated code recommends non-existent packages.

Placeholder Endpoints

Look for

localhost, example.com, undefined env vars in production code

Why

AI generates structurally complete code with fake values. Nobody replaces them all.

No Validation

Look for

Request bodies passed directly to the database without any checks

Why

AI doesn't add friction to the happy path. Validation isn't needed to make the demo work.

Overscoped Permissions

Look for

Firebase allow: true, IAM policies with *, RLS disabled

Why

AI uses the most permissive config that works. Locking it down would break the demo.

No Tests

Look for

Zero test files in the entire project

Why

AI generates features, not verification. "It looks right in the browser" replaces automated testing.

// TODO: add auth

Look for

Deferred security comments throughout the codebase

Why

AI acknowledges the gap but doesn't fill it. The TODO becomes permanent documentation of missing security.

The Prompt

Copy It. Run It.

Paste this into Claude, ChatGPT, or any agent with codebase access. Point it at your project. It'll find the holes and tell you exactly how to close them.

vibecoder-security-review.md
# Vibecoder Security Review

A structured, time-boxed security triage for codebases built at speed — with AI tools, rapid prototyping frameworks, or under "ship it now" pressure. Focuses on the vulnerabilities that matter most in these contexts: the ones that are trivial to exploit and catastrophic when found.

AI-assisted commits leak secrets at roughly twice the baseline rate, and empirical testing shows 40–45% of AI-generated code samples contain OWASP Top 10 vulnerabilities. The patterns are predictable. This skill finds them.

## When to Use

**Good fit:** Initial security triage of unfamiliar codebases, AI-generated or rapidly prototyped applications, MVP/startup security health checks, pre-launch security sweeps, scoping work before a deeper audit, reviewing code produced by vibe-coding tools (Cursor, Replit, Bolt, Lovable, Claude Code, Copilot, etc.).

**Not designed for:** Mature security-hardened codebases, deep vulnerability validation or exploitation, formal compliance audits, complex cryptographic analysis.

## Review Workflow

Execute the checks below in order. The entire review is designed to complete in roughly two hours. Timings are guidelines — adapt to codebase size.

| Phase | Time | Focus |
|-------|------|-------|
| Recon | 15 min | Understand the stack, find entry points, map the architecture |
| Secrets | 10 min | Find exposed credentials |
| Auth & identity | 20 min | Trace identity and authorization flows |
| Data access | 15 min | Test ownership enforcement on user data |
| Injection & execution | 15 min | SQL, XSS, prompt injection, RCE, SSRF |
| Vibe-code pitfalls | 15 min | AI-specific failure patterns, hallucinated deps, silent failures |
| Infrastructure & config | 10 min | BaaS rules, uploads, CORS, headers |
| Error handling & logging | 5 min | Swallowed errors, leaked internals, missing observability |
| Documentation | 15 min | Write up findings |

### Phase 0 — Recon

Understand what you're looking at before diving in.

```bash
# Identify the stack
ls -la
cat package.json requirements.txt Gemfile pom.xml go.mod 2>/dev/null
cat README.md

# Find entry points
find . -name "main.*" -o -name "app.*" -o -name "server.*" -o -name "index.*" | head -20

# Check environment and config
ls -la .env* config/ 2>/dev/null

# Detect AI-generated code markers
grep -rn "Generated by\|copilot\|cursor\|AI-generated\|vibe" --include="*.{js,ts,py,md}" | head -20

# Check git history for AI patterns (bulk commits, single-author everything)
git log --oneline -20 2>/dev/null
git shortlog -sn 2>/dev/null
```

During recon, note whether this appears to be a vibe-coded project. Indicators: single contributor, large commits with many files, boilerplate-heavy code, AI tool config files (`.cursorrules`, `.claude/`, `.github/copilot`), and a ratio of feature code to security/test code that skews heavily toward features.

---

## Core Checks

### 1. Secrets & Keys

**Goal:** Find credentials that anyone with repo or bundle access can steal.

**Search patterns:**
```bash
grep -rn "api_key\|API_KEY\|secret\|SECRET\|password\|PASSWORD\|token\|TOKEN" \
  --include="*.{js,ts,py,java,go,rb,php,env*,yml,yaml,json,config}" \
  | grep -v node_modules | grep -v ".git"

# Check for high-entropy strings (potential keys)
grep -rn "sk-\|sk_live\|pk_live\|AKIA\|eyJhbG\|ghp_\|glpat-\|xox[bsap]-" \
  --include="*.{js,ts,py,json,yml,yaml,env*}" | grep -v node_modules
```

**Priority files:** `.env`, `.env.local`, `config/*.{yml,yaml,json}`, `src/config/*`, `**/constants.{js,ts,py}`, `docker-compose*.yml`

**What constitutes a finding:**
- Hardcoded API keys (Stripe, OpenAI, AWS, database connection strings)
- JWT secrets, session keys, or encryption keys in source
- OAuth client secrets in client-side code
- Credentials in comments (e.g., `// TODO: remove test key`)
- Secrets bundled into frontend builds
- Test credentials that resolve against production
- Supabase `service_role` keys exposed to the client (should be `anon` key only)

**Vulnerable example:**
```javascript
// Frontend bundle — extractable by anyone
const OPENAI_API_KEY = "sk-proj-abc123...";

// Backend — committed to version control
DATABASE_URL = "postgresql://admin:password123@db.prod.com/app"

// Supabase service key in client (bypasses RLS entirely)
const supabase = createClient(URL, "eyJhbGciOiJIUzI1NiIs...");
```

**Expected remediation:** Environment variables (`process.env`, `os.getenv`), secret managers (AWS Secrets Manager, HashiCorp Vault), CI/CD secret injection, `.env.example` with placeholders only. For BaaS: use the public/anon key on the client; restrict the service role key to server-side only.

---

### 2. Authentication & Authorization

**Goal:** Find paths to impersonate another user or escalate to admin.

**Search patterns:**
```bash
grep -rn "login\|signup\|authenticate\|session\|jwt\|token\|oauth" \
  --include="*.{js,ts,py,java,go,rb,php}"
grep -rn "is_admin\|isAdmin\|role\|permission\|authorize" \
  --include="*.{js,ts,py,java,go,rb,php}"
```

**What constitutes a finding:**
- User identity derived from client-controlled parameters (URL params, request body) instead of server-side session
- Role or admin status accepted from the request without verification
- Client-side-only auth gates (UI hides admin panel, but API remains open)
- JWTs accepted without signature verification or expiration enforcement
- Non-expiring tokens or magic links
- Session cookies missing `httpOnly`, `secure`, or `sameSite` flags
- Unprotected admin routes
- Password reset flows using predictable or sequential tokens
- Paywall or feature-gating enforced only in frontend (trivially bypassed via browser console or direct API call)

**Vulnerable example:**
```javascript
// Identity from query param — attacker controls this
app.get('/api/profile', (req, res) => {
  const userId = req.query.userId;
  return res.json(db.getProfile(userId));
});

// Role from request body — attacker controls this
app.post('/api/admin/users', (req, res) => {
  if (req.body.isAdmin === true) { /* admin operations */ }
});

// Client-side paywall — user flips a boolean and bypasses payment
function PremiumFeature() {
  const { user } = useAuth();
  if (!user.isPro) return <UpgradePrompt />;  // Only checked in UI!
  return <PremiumContent />;  // API has no payment check
}
```

**Expected remediation:** Derive user identity exclusively from authenticated sessions. Enforce role checks server-side before any privileged operation. Set cookie flags: `httpOnly=true; secure=true; sameSite=strict`. Validate JWT signatures and enforce expiration. Implement CSRF tokens for state-changing operations. Gate paid features in the API layer, not the component tree.

---

### 3. User Data & Access Control

**Goal:** Determine whether changing a record ID in a request leaks another user's data.

**Search patterns:**
```bash
grep -rn "GET.*user\|profile\|account\|order\|payment" \
  --include="*.{js,ts,py,java,go,rb,php}"
grep -rn "WHERE.*user\|filter.*user\|findOne\|findById" \
  --include="*.{js,ts,py,java,go,rb,php}"
```

**What constitutes a finding:**
- API routes accepting record IDs without ownership validation
- ORM/database queries that fetch by ID without filtering to the authenticated user
- GraphQL resolvers returning data for any requested user
- List endpoints that don't scope results to the current user
- Enumerable sequential IDs exposing the full dataset
- BaaS (Supabase, Firebase) with missing or permissive Row-Level Security / Security Rules

**Vulnerable example:**
```python
# No ownership check — returns ANY order
@app.get("/api/orders/{order_id}")
def get_order(order_id: int):
    return db.query(Order).filter(Order.id == order_id).first()
```

**Expected remediation:**
```python
# Ownership enforced
@app.get("/api/orders/{order_id}")
def get_order(order_id: int, current_user: User = Depends(get_current_user)):
    order = db.query(Order).filter(
        Order.id == order_id,
        Order.user_id == current_user.id
    ).first()
    if not order:
        raise NotFoundError()
    return order
```

---

### 4. Injection & Code Execution

This check covers five attack classes. Search for all five.

#### SQL Injection
```bash
grep -rn "SELECT.*+\|query.*%.*s\|execute.*format\|raw.*sql" \
  --include="*.{js,ts,py,java,go,rb,php}"
```
Flag: String concatenation or interpolation in SQL queries, `.raw()` / `.execute()` with user-controlled strings, NoSQL `$where` with user input. Remediation: Parameterized queries or ORM safe methods.

#### XSS (Cross-Site Scripting)
```bash
grep -rn "innerHTML\|dangerouslySetInnerHTML\|html.*safe\|raw.*html\|document\.write" \
  --include="*.{js,ts,jsx,tsx,py,rb,php}"
```
Flag: `innerHTML`, `document.write()`, `dangerouslySetInnerHTML`, or template `|safe` / `|raw` filters applied to user-controlled content. Also flag Markdown renderers processing user input without sanitization. Remediation: `textContent`, framework auto-escaping, DOMPurify for HTML.

#### Prompt Injection (LLM/AI)
```bash
grep -rn "openai\|anthropic\|completion\|chat\|prompt\|llm\|gemini\|claude" \
  --include="*.{js,ts,py,java,go,rb,php}"
grep -rn "system.*message\|role.*system\|system.*prompt" \
  --include="*.{js,ts,py,java,go,rb,php}"
```
Flag: User input concatenated into system prompts, no boundary between system and user messages, LLM output used directly in SQL/shell/code execution, tool/function calls without output validation, system prompts containing secrets or internal data the user shouldn't see. Remediation: Strict message role separation, output validation and sanitization, boundary instructions in system prompts, treat LLM output as untrusted.

#### Remote Code Execution (RCE)
```bash
grep -rn "eval\|exec\|system\|popen\|subprocess\|shell\|spawn\|Function(" \
  --include="*.{js,ts,py,java,go,rb,php}"
grep -rn "pickle\|unserialize\|deserialize\|yaml.load\|Marshal" \
  --include="*.{py,rb,php,java}"
```
Flag: `eval`/`exec`/`Function()` with user input, `subprocess` with `shell=True` and user input, unsafe deserialization (`pickle`, `yaml.load`), template rendering from user-provided strings. Remediation: Eliminate `eval`/`exec`, use parameterized subprocess calls (no `shell=True`), safe deserialization formats (JSON), pre-defined templates only.

#### Server-Side Request Forgery (SSRF)
```bash
grep -rn "fetch\|axios\|requests\.get\|urllib\|http\.get\|curl" \
  --include="*.{js,ts,py,java,go,rb,php}" | grep -i "url\|host\|endpoint"
```
Flag: User-supplied URLs passed directly to server-side HTTP clients (e.g., `fetch(req.body.url)`). Attacker can reach internal services, cloud metadata endpoints (`169.254.169.254`), or private network resources. Remediation: Allowlist permitted domains or URL patterns, resolve DNS and validate the IP is not private/internal before making the request, block access to metadata endpoints.

---

### 5. Vibe-Code Pitfalls

This section covers failure patterns unique to or significantly amplified by AI-assisted development. These checks are the differentiator between a generic security review and one calibrated for how modern vibe-coded applications actually break.

#### 5a. Hallucinated Dependencies (Slopsquatting)

AI code generators sometimes recommend packages that don't exist. Attackers monitor these hallucination patterns and register the fake package names with malicious payloads. Research shows ~20% of AI-generated code samples recommend non-existent packages, and 43% of hallucinated names recur consistently across repeated prompts.

**Search patterns:**
```bash
# Extract all dependencies
cat package.json | jq '.dependencies, .devDependencies' 2>/dev/null
cat requirements.txt Pipfile 2>/dev/null

# Flag packages with very few downloads or recent creation dates
# (Manual step: spot-check unfamiliar package names on npm/PyPI)
```

**What constitutes a finding:**
- Packages that don't appear on the official registry or have zero/minimal download history
- Packages with names that look like plausible mashups of popular libraries (e.g., `express-mongoose`, `react-auth-helper`) but are not well-known
- Recently published packages (<6 months old) with no GitHub repo, no README, or suspiciously minimal code
- Dependencies that serve no clear purpose in the application

**How to check:** For each unfamiliar dependency, verify it exists on npm/PyPI/etc., check its download count, inspect its publish date, and confirm it has a legitimate maintainer and source repo. Pay special attention to packages with names that combine two popular library names — this is the most common hallucination pattern (38% of hallucinated names are mashups like this).

#### 5b. Client-Side Security Theater

AI tools optimize for "it works in the demo." They generate code that appears functional when there's a single user and no adversary. Security logic ends up in the component tree rather than the API layer, because that's where the AI sees the feature being used.

**Search patterns:**
```bash
# Frontend auth/permission checks without backend equivalents
grep -rn "isAdmin\|isPro\|role\|permission\|canAccess\|isAuthenticated" \
  --include="*.{jsx,tsx,vue,svelte}"

# Then check: do the corresponding API routes enforce the same checks?
grep -rn "isAdmin\|isPro\|role\|permission\|canAccess\|isAuthenticated" \
  --include="*.{js,ts,py}" --include="*route*" --include="*controller*" --include="*middleware*"
```

**What constitutes a finding:**
- Authorization checks that exist only in React/Vue/Svelte components with no server-side equivalent
- Feature flags or subscription tiers enforced in the UI but not validated at the API layer
- Admin panels hidden via conditional rendering while the admin API endpoints remain unprotected
- Price, discount, or billing logic computed on the client and trusted by the server
- Form validation that exists only in the frontend (the backend accepts anything)

**The test:** For every permission check you find in a frontend component, trace it to the corresponding API route. If the API doesn't independently verify the same condition, flag it.

#### 5c. Error Swallowing & Silent Failures

AI-generated code systematically prioritizes code that runs over code that fails correctly. When an AI encounters an error during generation, it wraps the problem in a try/catch that suppresses it rather than handling it. The result: security checks that silently fail open.

**Search patterns:**
```bash
# Empty or minimal catch blocks
grep -rn "catch.*{" --include="*.{js,ts,py,java,go,rb}" -A 2 | grep -B 1 "console\.\|pass\|//\|{}"

# AI-generated fallback values that mask failures
grep -rn "catch.*=>\|\.catch\|except:" --include="*.{js,ts,py}" -A 3
```

**What constitutes a finding:**
- Empty `catch` blocks or catch blocks that only `console.log` the error
- Auth/permission checks wrapped in try/catch where the catch block defaults to "allow"
- API calls where failure silently returns mock data or a default "success" response (hallucinated bypass)
- Error boundaries that swallow security-critical failures and render a fallback component instead of blocking access
- Database connection failures that fall back to in-memory/mock data without alerting

**Vulnerable example:**
```javascript
// Auth check silently fails open — catastrophic
async function checkPermission(userId) {
  try {
    const result = await authService.verify(userId);
    return result.authorized;
  } catch (err) {
    console.log("Auth check failed:", err);
    return true;  // AI's "safe" default
  }
}
```

#### 5d. Placeholder & Hallucinated Logic

AI tools sometimes generate code that looks correct but contains placeholder values, fake API endpoints, hallucinated environment variables, or logic that works only with hardcoded test data.

**Search patterns:**
```bash
# Placeholder/example values left in production
grep -rn "example\.com\|placeholder\|CHANGEME\|your-.*-here\|xxx\|TODO\|FIXME\|HACK" \
  --include="*.{js,ts,py,java,go,rb,php,yml,yaml,json}"

# Fake/hardcoded API endpoints
grep -rn "localhost\|127\.0\.0\.1\|0\.0\.0\.0" \
  --include="*.{js,ts,py,yml,yaml,json}" | grep -v "test\|spec\|__test__"

# Hallucinated environment variables (referenced but never defined)
grep -rn "process\.env\.\|os\.getenv\|os\.environ" --include="*.{js,ts,py}" \
  | sed 's/.*process\.env\.\([A-Z_]*\).*/\1/;s/.*getenv.*"\([^"]*\)".*/\1/' \
  | sort -u > /tmp/env_refs.txt
cat .env .env.local .env.production 2>/dev/null | grep -oP '^[A-Z_]+' | sort -u > /tmp/env_defs.txt
comm -23 /tmp/env_refs.txt /tmp/env_defs.txt  # Shows referenced but undefined vars
```

**What constitutes a finding:**
- API endpoints pointing to `localhost` or `example.com` in production code paths
- Environment variables referenced in code but never defined in any `.env` file or deployment config
- Hardcoded test data used as fallback when real data sources are unavailable
- Magic numbers or configuration values copied from AI training data / documentation examples

#### 5e. Overpermissioned BaaS & Cloud Configuration

Vibe-coded apps disproportionately rely on Backend-as-a-Service platforms (Supabase, Firebase, AWS Amplify) because they eliminate the need to write a backend. The AI often generates configurations that work but leave data wide open.

**Search patterns:**
```bash
# Supabase
find . -name "*.sql" | xargs grep -l "RLS\|row level\|policy" 2>/dev/null
grep -rn "supabaseClient\|createClient" --include="*.{js,ts}" | head -10

# Firebase
find . -name "firestore.rules" -o -name "database.rules.json" -o -name "storage.rules"
grep -rn "allow read, write: if true\|allow read, write;" \
  --include="*.rules" --include="*.json"

# AWS
find . -name "*.tf" -o -name "serverless.yml" -o -name "template.yaml" | head -10
grep -rn "Principal.*\*\|Effect.*Allow" --include="*.{tf,yml,yaml,json}"
```

**What constitutes a finding:**
- Supabase tables with Row-Level Security disabled or policies that grant access to `public` / `anon` role without conditions
- Firebase rules set to `allow read, write: if true` (the default for dev — often shipped to prod)
- S3 buckets with public ACLs or overly permissive bucket policies
- Serverless function permissions using `*` for resources
- Database publicly accessible from the internet (check connection strings for public hosts and no IP allowlisting)

#### 5f. Missing or Broken Input Validation

AI-generated code typically handles the happy path. It accepts what you give it and processes it. Validation — type checking, length limits, format enforcement, business rule constraints — is systematically absent.

**Search patterns:**
```bash
# API route handlers — check for validation libraries or manual checks
grep -rn "app\.\(get\|post\|put\|patch\|delete\)" --include="*.{js,ts}" -A 10 \
  | grep -c "validate\|schema\|zod\|joi\|yup\|celebrate\|class-validator"

# Python endpoints
grep -rn "@app\.\(route\|get\|post\|put\)" --include="*.py" -A 10 \
  | grep -c "pydantic\|marshmallow\|validate\|wtforms"
```

**What constitutes a finding:**
- API endpoints that accept request bodies and pass them directly to database operations without schema validation
- No validation library in the dependency tree (Zod, Joi, Yup, Pydantic, Marshmallow, etc.)
- String fields without length limits (DoS via oversized payloads)
- Numeric fields without range checks (negative prices, quantities of -1, etc.)
- Email/URL fields without format validation
- Missing Content-Type enforcement on request handlers

---

### 6. Dependencies & Supply Chain

**Goal:** Identify vulnerable, outdated, suspicious, or hallucinated packages.

**Inspect:** `package.json`, `requirements.txt`, `Gemfile`, `pom.xml`, `go.mod`, `Cargo.toml` and their lockfiles.

**What constitutes a finding:**
- Packages multiple major versions behind current
- Known CVEs in installed versions (cross-reference GitHub Security Advisories)
- Deprecated or abandoned packages (check last publish date)
- Suspicious package names suggesting typosquatting or slopsquatting
- SDKs with overly broad permissions (e.g., AWS SDK instantiated with hardcoded admin credentials)
- Packages with zero or near-zero downloads, no source repo, or recently created by unknown maintainers
- Lock files missing or not committed (builds are not reproducible)

**Quick validation:** Run `npm audit`, `pip-audit`, `bundle audit`, or equivalent. Check package publish dates and advisory pages. For each unfamiliar package, verify its registry listing, download count, and source repository.

---

### 7. Infrastructure & Configuration

**Goal:** Catch missing baseline protections and dangerous deployment configs.

```bash
grep -rn "cors\|CORS\|helmet\|security.*header" --include="*.{js,ts,py,java,go,rb,php}"
grep -rn "rate.*limit\|throttle\|ratelimit" --include="*.{js,ts,py,java,go,rb,php}"
```

**What constitutes a finding:**
- CORS set to `Access-Control-Allow-Origin: *` with credentials enabled
- No CSRF protection on state-changing endpoints
- Login/auth endpoints without rate limiting
- HTTP (not HTTPS) in production URLs
- Missing security headers (CSP, X-Frame-Options, Strict-Transport-Security)
- File upload endpoints with no type validation, size limits, or filename sanitization
- Uploaded files stored in web-accessible executable directories
- Docker containers running as root without necessity
- Debug ports or services exposed in production `docker-compose.yml`

---

### 8. Error Handling, Logging & Observability

**Goal:** Find error-handling patterns that leak data or hide failures, and assess whether the application has any observability.

**Search patterns:**
```bash
# Verbose error responses
grep -rn "stack\|stackTrace\|traceback\|err\.message\|error\.message" \
  --include="*.{js,ts,py,java,go,rb,php}" | grep -i "res\.\|response\|return\|send\|json"

# Sensitive data in logs
grep -rn "console\.log\|logger\.\|logging\.\|print(" --include="*.{js,ts,py}" \
  | grep -i "password\|token\|secret\|key\|credit\|ssn\|authorization"
```

**What constitutes a finding:**
- Stack traces, SQL errors, or internal paths returned to end users in production
- Passwords, tokens, API keys, or PII written to log output
- No structured error handling (the entire app is one big try/catch or has no error handling at all)
- No health check or monitoring endpoint
- No logging framework — only `console.log` / `print` statements
- Test accounts, debug flags, or auth bypass headers still active in production

---

### 9. Test & Production Hygiene

**Goal:** Find test backdoors, debug features, and development shortcuts left in production.

**Search patterns:**
```bash
grep -rn "NODE_ENV\|DEBUG\|ENVIRONMENT" --include="*.{js,ts,py,env*,yml,yaml}"
grep -rn "test.*user\|admin.*test\|debug\|FIXME\|TODO.*production\|HACK" \
  --include="*.{js,ts,py,java,go,rb,php}"
```

**What constitutes a finding:**
- Test accounts functional in production (`admin@test.com` / `test123`)
- Debug mode enabled (exposes stack traces, SQL queries, secrets)
- Auth bypass headers or flags still active (e.g., `X-Test-Auth: bypass`)
- Verbose error messages exposing internals to end users
- Shared databases or infrastructure between test and production
- Environment detection that defaults to `development`
- No test suite at all — particularly telling for vibe-coded projects where "it looks right in the browser" replaces automated verification

---

## Report Template

Use this structure for all findings. Prioritize clarity and actionability — the audience is developers, not auditors.

```markdown
# Security Review: [Project Name]

**Date:** YYYY-MM-DD
**Stack:** [e.g., React, Express, PostgreSQL, Supabase]
**Auth pattern:** [e.g., JWT, session cookies, Supabase Auth]
**AI-assisted indicators:** [e.g., single contributor, .cursorrules present, bulk commits]

## Summary

Found X critical, Y high, Z medium issues.
[One-paragraph executive summary of the most important risks.]

## Findings

### [CRITICAL] Title — concise description of the vulnerability

**Location:** `path/to/file.ts:line`

**Issue:** One-paragraph explanation of what's wrong.

**Vulnerable code:**
[Minimal code snippet demonstrating the issue]

**Impact:** What an attacker gains. Be specific — "access any user's data"
is better than "potential data exposure."

**Attack scenario:**
1. Attacker does X
2. Which causes Y
3. Resulting in Z

**Remediation:** Specific fix for this codebase and stack. Include a code
example if it helps.

---

[Repeat for each finding]

## Vibe-Code Risk Assessment

[Dedicated section summarizing AI-specific risks found: hallucinated dependencies,
client-side security theater, silent error handling, placeholder logic, BaaS
misconfigurations. Rate overall vibe-code hygiene as RED / AMBER / GREEN.]

## Quick Wins

Numbered list of highest-leverage fixes, ordered by effort-to-impact ratio.

## Recommendations for Ongoing Security

[Suggest CI/CD integrations: secret scanning (GitGuardian, truffleHog),
dependency auditing (npm audit, pip-audit, Dependabot), SAST tooling
(Semgrep, CodeQL), and pre-commit hooks for credential detection.]
```

---

## AI-Assisted Code: Patterns and Pathology

This section summarizes the recurring failure modes of AI-generated code based on empirical research and field observations. Use it to calibrate your threat model during review.

### Why AI Code Breaks Differently

Traditional codebases fail because developers make mistakes under time pressure. AI-generated codebases fail because the code was optimized for a different objective: *looking functional*. The AI's training signal rewards code that runs, not code that resists attack. This creates systematic biases:

**The happy-path bias.** AI-generated code works perfectly for the demo user. It breaks the moment a second user exists, an attacker probes an edge case, or a service returns an error. Ownership checks, rate limits, and input validation are absent because they're not needed to make the feature "work."

**The single-context illusion.** The AI generates each file, function, or component in relative isolation. Cross-cutting concerns — authentication middleware, global error handling, consistent authorization enforcement — are the first casualties. The AI doesn't think in terms of "every route in this application must check ownership." It thinks in terms of "this route should return the order."

**The confidence problem.** AI-generated code looks authoritative. Variable names are clear, comments are helpful, structure is clean. This creates a false sense of security — developers (and especially non-developer founders) trust it more than they should. The code *reads* like a senior engineer wrote it, even when it has the security posture of a first draft.

**The copy-paste provenance problem.** AI training data includes documentation examples, Stack Overflow answers, and tutorial code — all of which use placeholder credentials, simplified auth, and insecure defaults for pedagogical clarity. These patterns end up in production code because the AI reproduces them faithfully and the developer doesn't know to question them.

**The refactoring collapse.** As features accumulate, AI agents make increasingly fragile changes. Code duplication rises, refactoring drops, and the codebase becomes a patchwork of isolated fixes. Security controls applied to early routes may not appear in later ones. Each AI-generated iteration drifts further from consistent architectural enforcement.

### Catalog of Vibe-Code Antipatterns

| Pattern | What to look for | Why AI does this |
|---------|-----------------|------------------|
| **Hardcoded example creds** | `sk-proj-...`, `password123`, keys from docs | Reproduced from training data |
| **Frontend-only gates** | Auth/role checks in JSX with no API equivalent | AI builds features where the user sees them |
| **Missing ownership checks** | DB queries by ID without `WHERE user_id = ...` | AI doesn't model your access control requirements |
| **Silent error swallowing** | `catch(e) { return defaultValue }` | AI optimizes for "doesn't crash" |
| **Hallucinated fallbacks** | Auth failure → return `true`; API error → show mock data | AI treats errors as obstacles to remove |
| **Placeholder endpoints** | `localhost`, `example.com`, undefined env vars | AI generates structurally complete code with fake values |
| **Boilerplate defaults** | Framework shipped with `DEBUG=true`, `CORS: *` | AI copies setup code from docs without hardening |
| **No validation** | Request bodies passed directly to DB/ORM | AI doesn't add friction to the happy path |
| **Hallucinated packages** | Dependencies that don't exist on the registry | AI invents plausible package names (~20% rate) |
| **`// TODO: add auth`** | Deferred security comments | AI acknowledges the gap but doesn't fill it |
| **Overscoped permissions** | `*` in IAM policies, Firebase `allow: true` | AI uses the most permissive config that works |
| **No test suite** | Zero test files in the entire project | AI generates features, not verification |
| **Duplicated logic** | Same business rule implemented differently across routes | AI doesn't maintain cross-file consistency |
| **Fake API keys in prompts** | Hallucinated env vars or placeholder API keys | AI needs *something* in the slot, so it invents a value |

---

## Severity Classification

Use this quick-reference guide for consistent severity ratings across findings.

| Severity | Criteria | Examples |
|----------|----------|----------|
| **CRITICAL** | Exploitable now, no authentication needed, leads to full data breach or system compromise | Exposed service-role key, unauthenticated admin API, RCE via eval, Firebase rules `allow: true` on user data |
| **HIGH** | Exploitable by authenticated user or requires minimal effort, leads to significant data access or privilege escalation | IDOR on user records, client-side-only auth, JWT without signature check, slopsquatted dependency installed |
| **MEDIUM** | Exploitable under specific conditions, limited blast radius, or defense-in-depth failure | Missing rate limiting on login, CORS misconfiguration, XSS in non-critical page, silent auth failure |
| **LOW** | Best-practice violation, no immediate exploit path, or informational | Missing security headers with edge protection, outdated dependency with unexploitable CVE, verbose error in staging |

---

## Avoiding False Positives

Do not flag these without further investigation:

- `.env.example` files containing placeholder values (not real credentials)
- Mock credentials in test fixtures (`tests/fixtures/*`) — but verify they don't work in production
- Dependency CVEs that are not reachable in the application's usage path — but note them as informational
- Missing security headers when the deployment platform (e.g., Cloudflare, Vercel) adds them at the edge — but verify the platform config
- `localhost` references in development-only config files (`docker-compose.dev.yml`, test configs)
- Packages flagged as "unfamiliar" that turn out to be legitimate niche libraries — always verify before reporting

---

## Relationship to Other Security Skills

This skill provides **initial triage**, not comprehensive assessment. For deeper work, hand off to specialized skills: reconnaissance for attack surface mapping, deep-dive analysis for data flow validation, formal assessment for severity classification and compliance reporting.

## Success Criteria

A thorough vibecoder review of a typical project surfaces 3–5 critical/high findings and 5–10 medium findings, each with specific file locations, reproducible attack scenarios, and stack-appropriate remediation guidance. If you find nothing, either the codebase is unusually mature or the review needs to go deeper — vibe-coded projects with zero findings are exceptionally rare.

Want a professional review?

The prompt catches the predictable stuff. A human catches the rest. Book a conversation and we'll run a full security triage on your project with findings, attack scenarios, and prioritized fixes.