This guide ensures all code follows security best practices and identifies potential vulnerabilities before they reach production.
When to Apply
Authentication & Authorization Implementing login, user roles, permissions
User Input & File Uploads Handling forms, file uploads, API parameters
API Endpoints Creating REST APIs, webhooks, external integrations
Sensitive Data Working with secrets, payments, personal information
Pre-Commit Security Checklist
Before ANY commit, verify:
1. Secrets Management
Never Hardcode Secrets
const apiKey = "sk-proj-xxxxx" // Hardcoded secret
const dbPassword = "password123" // In source code
Environment Variables
Create .env.local
OPENAI_API_KEY = sk-proj-xxxxx
DATABASE_URL = postgresql://...
STRIPE_SECRET_KEY = sk_live_xxxxx
Validate at Startup
const requiredEnvVars = [
'OPENAI_API_KEY' ,
'DATABASE_URL' ,
'STRIPE_SECRET_KEY'
]
for ( const envVar of requiredEnvVars ) {
if ( ! process . env [ envVar ]) {
throw new Error ( `Missing ${ envVar } ` )
}
}
Deploy to Hosting Platform
Add secrets to:
Vercel: Project Settings → Environment Variables
Railway: Project → Variables
Render: Environment → Environment Variables
Verification Checklist
import { z } from 'zod'
// Define validation schema
const CreateUserSchema = z . object ({
email: z . string (). email (),
name: z . string (). min ( 1 ). max ( 100 ),
age: z . number (). int (). min ( 0 ). max ( 150 )
})
// Validate before processing
export async function createUser ( input : unknown ) {
try {
const validated = CreateUserSchema . parse ( input )
return await db . users . create ( validated )
} catch ( error ) {
if ( error instanceof z . ZodError ) {
return { success: false , errors: error . errors }
}
throw error
}
}
File Upload Validation
function validateFileUpload ( file : File ) {
// Size check (5MB max)
const maxSize = 5 * 1024 * 1024
if ( file . size > maxSize ) {
throw new Error ( 'File too large (max 5MB)' )
}
// Type check
const allowedTypes = [ 'image/jpeg' , 'image/png' , 'image/gif' ]
if ( ! allowedTypes . includes ( file . type )) {
throw new Error ( 'Invalid file type' )
}
// Extension check
const allowedExtensions = [ '.jpg' , '.jpeg' , '.png' , '.gif' ]
const extension = file . name . toLowerCase (). match ( / \. [ ^ . ] + $ / )?.[ 0 ]
if ( ! extension || ! allowedExtensions . includes ( extension )) {
throw new Error ( 'Invalid file extension' )
}
return true
}
Verification Checklist
3. SQL Injection Prevention
Never concatenate SQL queries with user input.
❌ DANGEROUS
✅ SAFE - Parameterized Query
// SQL Injection vulnerability
const query = `SELECT * FROM users WHERE email = ' ${ userEmail } '`
await db . query ( query )
Verification Checklist
4. Authentication & Authorization
JWT Token Handling
❌ WRONG - localStorage (XSS vulnerable)
✅ CORRECT - httpOnly cookies
localStorage . setItem ( 'token' , token )
Authorization Checks
export async function deleteUser ( userId : string , requesterId : string ) {
// ALWAYS verify authorization first
const requester = await db . users . findUnique ({
where: { id: requesterId }
})
if ( requester . role !== 'admin' ) {
return NextResponse . json (
{ error: 'Unauthorized' },
{ status: 403 }
)
}
// Proceed with deletion
await db . users . delete ({ where: { id: userId } })
}
Row Level Security (Supabase)
-- Enable RLS on all tables
ALTER TABLE users ENABLE ROW LEVEL SECURITY ;
-- Users can only view their own data
CREATE POLICY "Users view own data"
ON users FOR SELECT
USING ( auth . uid () = id);
-- Users can only update their own data
CREATE POLICY "Users update own data"
ON users FOR UPDATE
USING ( auth . uid () = id);
Verification Checklist
5. XSS Prevention
Sanitize HTML
import DOMPurify from 'isomorphic-dompurify'
// ALWAYS sanitize user-provided HTML
function renderUserContent ( html : string ) {
const clean = DOMPurify . sanitize ( html , {
ALLOWED_TAGS: [ 'b' , 'i' , 'em' , 'strong' , 'p' ],
ALLOWED_ATTR: []
})
return < div dangerouslySetInnerHTML = {{ __html : clean }} />
}
Content Security Policy
// next.config.js
const securityHeaders = [
{
key: 'Content-Security-Policy' ,
value: `
default-src 'self';
script-src 'self' 'unsafe-eval' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self';
connect-src 'self' https://api.example.com;
` . replace ( / \s {2,} / g , ' ' ). trim ()
}
]
Verification Checklist
6. CSRF Protection
CSRF Tokens
import { csrf } from '@/lib/csrf'
export async function POST ( request : Request ) {
const token = request . headers . get ( 'X-CSRF-Token' )
if ( ! csrf . verify ( token )) {
return NextResponse . json (
{ error: 'Invalid CSRF token' },
{ status: 403 }
)
}
// Process request
}
SameSite Cookies
res . setHeader ( 'Set-Cookie' ,
`session= ${ sessionId } ; HttpOnly; Secure; SameSite=Strict` )
Verification Checklist
7. Rate Limiting
API Rate Limiting
import rateLimit from 'express-rate-limit'
const limiter = rateLimit ({
windowMs: 15 * 60 * 1000 , // 15 minutes
max: 100 , // 100 requests per window
message: 'Too many requests'
})
// Apply to routes
app . use ( '/api/' , limiter )
Expensive Operations
// Aggressive rate limiting for searches
const searchLimiter = rateLimit ({
windowMs: 60 * 1000 , // 1 minute
max: 10 , // 10 requests per minute
message: 'Too many search requests'
})
app . use ( '/api/search' , searchLimiter )
Verification Checklist
8. Sensitive Data Exposure
Logging
❌ WRONG - Logging sensitive data
✅ CORRECT - Redact sensitive data
console . log ( 'User login:' , { email , password })
console . log ( 'Payment:' , { cardNumber , cvv })
Error Messages
❌ WRONG - Exposing internal details
✅ CORRECT - Generic error messages
catch ( error ) {
return NextResponse . json (
{ error: error . message , stack: error . stack },
{ status: 500 }
)
}
Verification Checklist
9. Dependency Security
Regular Updates
# Check for vulnerabilities
npm audit
# Fix automatically fixable issues
npm audit fix
# Update dependencies
npm update
# Check for outdated packages
npm outdated
Lock Files
# ALWAYS commit lock files
git add package-lock.json
# Use in CI/CD for reproducible builds
npm ci # Instead of npm install
Verification Checklist
Security Testing
Automated Security Tests
// Test authentication
test ( 'requires authentication' , async () => {
const response = await fetch ( '/api/protected' )
expect ( response . status ). toBe ( 401 )
})
// Test authorization
test ( 'requires admin role' , async () => {
const response = await fetch ( '/api/admin' , {
headers: { Authorization: `Bearer ${ userToken } ` }
})
expect ( response . status ). toBe ( 403 )
})
// Test input validation
test ( 'rejects invalid input' , async () => {
const response = await fetch ( '/api/users' , {
method: 'POST' ,
body: JSON . stringify ({ email: 'not-an-email' })
})
expect ( response . status ). toBe ( 400 )
})
// Test rate limiting
test ( 'enforces rate limits' , async () => {
const requests = Array ( 101 ). fill ( null ). map (() =>
fetch ( '/api/endpoint' )
)
const responses = await Promise . all ( requests )
const tooManyRequests = responses . filter ( r => r . status === 429 )
expect ( tooManyRequests . length ). toBeGreaterThan ( 0 )
})
Pre-Deployment Checklist
Before ANY production deployment:
Security Response Protocol
STOP Immediately
If security issue found, stop all work.
Use security-reviewer Agent
Or delegate to security-reviewer agent.
Fix CRITICAL Issues
Address all critical vulnerabilities before continuing.
Rotate Exposed Secrets
If any secrets were exposed, rotate immediately.
Review Entire Codebase
Search for similar issues across the project.
security-reviewer Agent Specialized agent for vulnerability detection and security audits.
/security-scan Command Run AgentShield security auditor (1282 tests, 102 rules).
security-review Skill Comprehensive security checklist and patterns.
AgentShield External tool: npx ecc-agentshield scan --fix
Resources
Security is not optional. One vulnerability can compromise the entire platform. When in doubt, err on the side of caution.