| name | security-review |
| description | Use this skill when adding authentication, handling user input, working with secrets, creating API endpoints, or implementing payment/sensitive features. |
Security Review Skill
Ensures all code follows security best practices and identifies potential vulnerabilities.
When to Activate
- Implementing authentication or authorization
- Handling user input or file uploads
- Creating new API endpoints
- Working with secrets or credentials
- Implementing payment features
- Storing or transmitting sensitive data
- Integrating third-party APIs
Security Checklist
1. Secrets Management
const apiKey = "sk-xxxxx"
const dbPassword = "password123"
function requireEnv(name: string): string {
const value = process.env[name]
if (value == null) {
throw new Error(`Missing required environment variable: ${name}`)
}
return value
}
const apiKey = requireEnv('API_KEY')
const dbUrl = requireEnv('DATABASE_URL')
Verification:
2. Input Validation
import { z } from 'zod'
const CreateUserSchema = z.object({
email: z.string().email(),
name: z.string().min(1).max(100),
age: z.number().int().min(0).max(150)
})
export async function createUser(input: unknown) {
const validated = CreateUserSchema.parse(input)
return await db.users.create(validated)
}
File Upload Validation:
function validateFileUpload(file: File) {
const MAX_SIZE = 5 * 1024 * 1024
if (file.size > MAX_SIZE) {
throw new Error('File too large (max 5MB)')
}
const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/webp']
if (!ALLOWED_TYPES.includes(file.type)) {
throw new Error('Invalid file type')
}
return true
}
Verification:
3. SQL Injection Prevention
const query = `SELECT * FROM users WHERE email = '${userEmail}'`
const user = await db.query(
'SELECT * FROM users WHERE email = $1',
[userEmail]
)
const user = await db.users.findFirst({
where: { email: userEmail }
})
4. Authentication & Authorization
res.setHeader('Set-Cookie',
`token=${token}; HttpOnly; Secure; SameSite=Strict; Max-Age=3600`)
export async function DELETE(req: Request) {
const user = await requireAuth(req)
if (user.id !== targetUserId && user.role !== 'admin') {
return Response.json({ error: 'Forbidden' }, { status: 403 })
}
}
Verification:
5. XSS Prevention
element.innerHTML = userInput
element.textContent = userInput
import DOMPurify from 'dompurify'
element.innerHTML = DOMPurify.sanitize(userInput, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'p'],
ALLOWED_ATTR: []
})
CSP Headers:
const securityHeaders = [
{
key: 'Content-Security-Policy',
value: "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;"
}
]
6. CSRF Protection
res.setHeader('Set-Cookie',
`session=${sessionId}; HttpOnly; Secure; SameSite=Strict`)
export async function POST(req: Request) {
const token = req.headers.get('X-CSRF-Token')
if (!csrfService.verify(token)) {
return Response.json({ error: 'Invalid CSRF token' }, { status: 403 })
}
}
7. Rate Limiting
import rateLimit from 'express-rate-limit'
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
message: 'Too many requests'
})
const searchLimiter = rateLimit({
windowMs: 60 * 1000,
max: 10,
message: 'Too many search requests'
})
8. Sensitive Data Exposure
console.log('Login:', { email, password })
console.log('Payment:', { cardNumber, cvv })
console.log('Login:', { email, userId })
console.log('Payment:', { last4: card.last4, userId })
catch (error) {
return Response.json({ error: error.message, stack: error.stack })
}
catch (error) {
console.error('Internal error:', error)
return Response.json({ error: 'An error occurred' }, { status: 500 })
}
9. Dependency Security
npm audit
npm audit fix
npm outdated
npm ci
Security Testing
test('requires authentication', async () => {
const response = await fetch('/api/protected')
expect(response.status).toBe(401)
})
test('requires admin role', async () => {
const response = await fetch('/api/admin', {
headers: { Authorization: `Bearer ${userToken}` }
})
expect(response.status).toBe(403)
})
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('enforces rate limits', async () => {
const requests = Array(101).fill(null).map(() => fetch('/api/endpoint'))
const responses = await Promise.all(requests)
const rateLimited = responses.filter(r => r.status === 429)
expect(rateLimited.length).toBeGreaterThan(0)
})
Pre-Deployment Checklist