Towering mountain peaks piercing through clouds
Vibe Coding

Securing AI-Generated Code: A Practical Checklist

DomAIn Labs Team
January 8, 2025
9 min read

Securing AI-Generated Code: A Practical Checklist

Research shows that 40% of AI-generated code contains security vulnerabilities. If you've vibe-coded your application, here's exactly how to secure it.

Why AI Code Is Often Insecure

AI models learn from existing code on the internet. Unfortunately, they also learn from insecure code:

  1. Pattern replication - If vulnerable code appears frequently in training data, the AI will copy it
  2. No security context - AI doesn't understand the implications of security choices
  3. Convenient but dangerous - AI often chooses the easiest solution, not the most secure
  4. Missing edge cases - AI tests happy paths, not malicious inputs

A March 2025 incident proved this painfully: a vibe-coded payment gateway approved $2M in fraudulent transactions because the AI had copied insecure input validation patterns.

Don't let that be you.

Security Audit Framework

Phase 1: Automated Scanning (15 minutes)

Start with automated tools to catch low-hanging fruit:

1. Dependency Vulnerabilities

npm/Node.js:

npm audit --production
npm audit fix

Python:

pip-audit
safety check

Additional tools:

  • Snyk: npx snyk test - Comprehensive vulnerability database
  • OWASP Dependency Check: Checks against National Vulnerability Database

2. Static Code Analysis

JavaScript/TypeScript:

npm install -D eslint-plugin-security
npx eslint . --ext .js,.jsx,.ts,.tsx

Python:

pip install bandit
bandit -r .

Go:

gosec ./...

3. Secret Detection

Find accidentally committed secrets:

# Install truffleHog
pip install truffleHog

# Scan entire Git history
truffleHog --regex --entropy=True .

Check for:

  • API keys in code
  • Hardcoded passwords
  • JWT secrets
  • Database credentials
  • AWS access keys

Phase 2: Manual Code Review (2-4 hours)

Automated tools miss context. Here's what to review manually:

1. Authentication & Authorization

Check for:

// ❌ BAD: No authentication
app.get('/api/user/:id', async (req, res) => {
  const user = await db.users.findOne(req.params.id)
  res.json(user) // Anyone can access any user!
})

// ✅ GOOD: Verify user owns resource
app.get('/api/user/:id', authenticateUser, async (req, res) => {
  if (req.user.id !== req.params.id && !req.user.isAdmin) {
    return res.status(403).json({ error: 'Unauthorized' })
  }
  const user = await db.users.findOne(req.params.id)
  res.json(user)
})

Checklist:

  • All endpoints require authentication
  • Authorization checks before data access
  • Session tokens expire appropriately
  • Password reset tokens are time-limited
  • Multi-factor authentication for sensitive operations

2. Input Validation

Check for:

// ❌ BAD: SQL Injection vulnerability
app.get('/search', async (req, res) => {
  const query = `SELECT * FROM products WHERE name LIKE '%${req.query.search}%'`
  const results = await db.query(query) // SQL injection!
})

// ✅ GOOD: Parameterized queries
app.get('/search', async (req, res) => {
  const results = await db.query(
    'SELECT * FROM products WHERE name LIKE $1',
    [`%${req.query.search}%`]
  )
})

Checklist:

  • All user inputs are validated
  • SQL queries use parameterization
  • NoSQL queries sanitized
  • File uploads validated (type, size)
  • Email addresses properly validated
  • Phone numbers sanitized

3. XSS Prevention

Check for:

// ❌ BAD: XSS vulnerability
function displayComment(comment) {
  document.getElementById('comments').innerHTML += comment // Dangerous!
}

// ✅ GOOD: Escape user content
function displayComment(comment) {
  const div = document.createElement('div')
  div.textContent = comment // Automatically escaped
  document.getElementById('comments').appendChild(div)
}

Checklist:

  • User content is escaped/sanitized
  • Using framework escaping (React, Vue auto-escape)
  • Content Security Policy (CSP) headers set
  • No dangerouslySetInnerHTML with user data
  • URL parameters sanitized

4. API Security

Check for:

// ❌ BAD: No rate limiting
app.post('/api/login', async (req, res) => {
  // Attacker can brute force passwords
})

// ✅ GOOD: Rate limiting
import rateLimit from 'express-rate-limit'

const loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 5, // 5 attempts
  message: 'Too many login attempts'
})

app.post('/api/login', loginLimiter, async (req, res) => {
  // Protected against brute force
})

Checklist:

  • Rate limiting on sensitive endpoints
  • API keys required for external access
  • CORS configured correctly
  • Request size limits enforced
  • Timeout values set appropriately

5. Data Exposure

Check for:

// ❌ BAD: Exposing sensitive data
app.get('/api/user', async (req, res) => {
  const user = await db.users.findOne(req.user.id)
  res.json(user) // Sends password hash, internal IDs, etc!
})

// ✅ GOOD: Selective data exposure
app.get('/api/user', async (req, res) => {
  const user = await db.users.findOne(req.user.id)
  res.json({
    id: user.id,
    username: user.username,
    email: user.email,
    // Only public fields
  })
})

Checklist:

  • API responses don't leak sensitive data
  • Error messages don't reveal system details
  • Stack traces disabled in production
  • Internal IDs not exposed in URLs
  • Proper data masking for PII

Phase 3: Security Testing (1-2 hours)

1. Penetration Testing Basics

SQL Injection Test:

# Try these in form inputs:
' OR '1'='1
'; DROP TABLE users--
1' UNION SELECT * FROM users--

XSS Test:

# Try these in text inputs:
<script>alert('XSS')</script>
<img src=x onerror=alert('XSS')>
javascript:alert('XSS')

Authentication Test:

  • Try accessing endpoints without login
  • Try accessing other users' data
  • Try replaying old authentication tokens
  • Try brute forcing passwords

2. Automated Penetration Testing

OWASP ZAP (free):

docker run -t owasp/zap2docker-stable zap-baseline.py \
  -t https://yourapp.com

Burp Suite Community (free):

  • Intercept and modify requests
  • Test for common vulnerabilities
  • Automated scanning

3. Load Testing Security

Test under stress:

# 1000 concurrent users
k6 run --vus 1000 --duration 30s load-test.js

Security issues often appear under load:

  • Rate limiting stops working
  • Caching exposes other users' data
  • Memory leaks reveal sensitive info
  • Error messages become too verbose

Common AI-Generated Vulnerabilities

1. Hardcoded Secrets

The Problem:

// AI often does this:
const stripe = require('stripe')('sk_live_abc123...')
const db = mongoose.connect('mongodb://admin:password@localhost:27017')

The Fix:

const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY)
const db = mongoose.connect(process.env.DATABASE_URL)

Remediation:

  1. Move all secrets to .env file
  2. Add .env to .gitignore
  3. Use platform environment variables in production
  4. Rotate any secrets that were committed

2. Missing Input Validation

The Problem:

// AI assumes inputs are valid
app.post('/api/transfer', async (req, res) => {
  await db.transfer(req.body.amount, req.body.to)
  // What if amount is negative? Or a huge number?
})

The Fix:

app.post('/api/transfer', async (req, res) => {
  const { amount, to } = req.body

  if (!amount || amount <= 0 || amount > 10000) {
    return res.status(400).json({ error: 'Invalid amount' })
  }

  if (!isValidAccountNumber(to)) {
    return res.status(400).json({ error: 'Invalid recipient' })
  }

  await db.transfer(amount, to)
})

3. Insecure Direct Object References

The Problem:

// Anyone can access any order
app.get('/api/order/:id', async (req, res) => {
  const order = await db.orders.findOne(req.params.id)
  res.json(order) // No ownership check!
})

The Fix:

app.get('/api/order/:id', authenticateUser, async (req, res) => {
  const order = await db.orders.findOne(req.params.id)

  if (order.userId !== req.user.id) {
    return res.status(403).json({ error: 'Access denied' })
  }

  res.json(order)
})

4. Weak Password Requirements

The Problem:

// AI rarely enforces strong passwords
app.post('/register', async (req, res) => {
  const user = await db.users.create({
    email: req.body.email,
    password: hashPassword(req.body.password) // No strength check
  })
})

The Fix:

const passwordSchema = new PasswordValidator()
  .is().min(12)
  .has().uppercase()
  .has().lowercase()
  .has().digits()
  .has().symbols()

app.post('/register', async (req, res) => {
  if (!passwordSchema.validate(req.body.password)) {
    return res.status(400).json({
      error: 'Password must be 12+ characters with upper, lower, numbers, and symbols'
    })
  }

  const user = await db.users.create({
    email: req.body.email,
    password: await bcrypt.hash(req.body.password, 10)
  })
})

5. Misconfigured CORS

The Problem:

// AI often does this:
app.use(cors({ origin: '*' })) // Allows ANY domain!

The Fix:

app.use(cors({
  origin: process.env.ALLOWED_ORIGINS.split(','),
  credentials: true,
  optionsSuccessStatus: 200
}))

Security Headers Checklist

Add these to every response:

app.use((req, res, next) => {
  // Prevent clickjacking
  res.setHeader('X-Frame-Options', 'SAMEORIGIN')

  // Prevent MIME type sniffing
  res.setHeader('X-Content-Type-Options', 'nosniff')

  // XSS protection
  res.setHeader('X-XSS-Protection', '1; mode=block')

  // HTTPS only
  res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains')

  // Content Security Policy
  res.setHeader('Content-Security-Policy',
    "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"
  )

  next()
})

Environment Security

Development

NODE_ENV=development
DATABASE_URL=postgresql://localhost:5432/myapp_dev
API_KEY=test_key_abc123
DEBUG=true

Production

NODE_ENV=production
DATABASE_URL=postgresql://user:pass@prod-server:5432/myapp
API_KEY=live_key_xyz789
DEBUG=false
ALLOWED_ORIGINS=https://myapp.com

Never use development credentials in production.

Incident Response Plan

When you find a security issue:

1. Severity Assessment

  • Critical: Data breach, authentication bypass, RCE
  • High: XSS, SQL injection, authorization issues
  • Medium: Information disclosure, weak encryption
  • Low: Missing headers, verbose errors

2. Immediate Actions

  • Critical: Take system offline if necessary
  • High: Deploy hotfix within hours
  • Medium: Deploy fix within days
  • Low: Fix in next release

3. Fix & Verify

  • Write a test that exploits the vulnerability
  • Fix the code
  • Verify the test now fails (exploit blocked)
  • Deploy fix

4. Post-Incident

  • Audit logs for exploitation attempts
  • Notify affected users if data was exposed
  • Document the incident and fix
  • Update security checklist to prevent recurrence

Ongoing Security Maintenance

Weekly:

  • Review dependency updates
  • Check security advisories
  • Review access logs for anomalies

Monthly:

  • Run full security scan
  • Review and rotate API keys
  • Update security documentation

Quarterly:

  • Professional penetration test
  • Security training for team
  • Review and update security policies

Tools & Resources

Free Tools:

  • npm audit - Dependency vulnerabilities
  • Snyk - Security scanning (free tier)
  • OWASP ZAP - Penetration testing
  • SSL Labs - Test HTTPS configuration
  • SecurityHeaders.com - Check your headers

Paid Tools:

  • Snyk Pro ($0-129/month) - Advanced scanning
  • Burp Suite Pro ($449/year) - Professional pentesting
  • HackerOne (Bounties) - Crowd-sourced security testing

Learning Resources:

  • OWASP Top 10 - owasp.org
  • Web Security Academy - portswigger.net
  • Hack The Box - hackthebox.com

Conclusion

Securing AI-generated code isn't optional—it's essential. The good news? Most vulnerabilities are preventable with systematic checking.

Your security checklist:

  1. Run automated scans immediately
  2. Manual review of authentication, inputs, and API security
  3. Test with malicious inputs
  4. Set proper security headers
  5. Create incident response plan
  6. Schedule ongoing audits

Don't wait for a breach to take security seriously.


Need a professional security audit? We'll review your vibe-coded app and provide a detailed security report with fixes. Get a security assessment.

Tags:securityvibe codingvulnerabilitiesbest practices

About the Author

DomAIn Labs Team

The DomAIn Labs team consists of AI engineers, strategists, and educators passionate about demystifying AI for small businesses.