Use this skill when adding authentication, handling user input, working with secrets, creating API endpoints, or implementing payment/sensitive features. Provides comprehensive security checklist and patterns.
Add this skill
npx mdskills install sickn33/cc-skill-security-reviewComprehensive security checklist with actionable examples covering authentication, input validation, and common vulnerabilities
1---2name: security-review3description: Use this skill when adding authentication, handling user input, working with secrets, creating API endpoints, or implementing payment/sensitive features. Provides comprehensive security checklist and patterns.4author: affaan-m5version: "1.0"6---78# Security Review Skill910This skill ensures all code follows security best practices and identifies potential vulnerabilities.1112## When to Activate1314- Implementing authentication or authorization15- Handling user input or file uploads16- Creating new API endpoints17- Working with secrets or credentials18- Implementing payment features19- Storing or transmitting sensitive data20- Integrating third-party APIs2122## Security Checklist2324### 1. Secrets Management2526#### ❌ NEVER Do This27```typescript28const apiKey = "sk-proj-xxxxx" // Hardcoded secret29const dbPassword = "password123" // In source code30```3132#### ✅ ALWAYS Do This33```typescript34const apiKey = process.env.OPENAI_API_KEY35const dbUrl = process.env.DATABASE_URL3637// Verify secrets exist38if (!apiKey) {39 throw new Error('OPENAI_API_KEY not configured')40}41```4243#### Verification Steps44- [ ] No hardcoded API keys, tokens, or passwords45- [ ] All secrets in environment variables46- [ ] `.env.local` in .gitignore47- [ ] No secrets in git history48- [ ] Production secrets in hosting platform (Vercel, Railway)4950### 2. Input Validation5152#### Always Validate User Input53```typescript54import { z } from 'zod'5556// Define validation schema57const CreateUserSchema = z.object({58 email: z.string().email(),59 name: z.string().min(1).max(100),60 age: z.number().int().min(0).max(150)61})6263// Validate before processing64export async function createUser(input: unknown) {65 try {66 const validated = CreateUserSchema.parse(input)67 return await db.users.create(validated)68 } catch (error) {69 if (error instanceof z.ZodError) {70 return { success: false, errors: error.errors }71 }72 throw error73 }74}75```7677#### File Upload Validation78```typescript79function validateFileUpload(file: File) {80 // Size check (5MB max)81 const maxSize = 5 * 1024 * 102482 if (file.size > maxSize) {83 throw new Error('File too large (max 5MB)')84 }8586 // Type check87 const allowedTypes = ['image/jpeg', 'image/png', 'image/gif']88 if (!allowedTypes.includes(file.type)) {89 throw new Error('Invalid file type')90 }9192 // Extension check93 const allowedExtensions = ['.jpg', '.jpeg', '.png', '.gif']94 const extension = file.name.toLowerCase().match(/\.[^.]+$/)?.[0]95 if (!extension || !allowedExtensions.includes(extension)) {96 throw new Error('Invalid file extension')97 }9899 return true100}101```102103#### Verification Steps104- [ ] All user inputs validated with schemas105- [ ] File uploads restricted (size, type, extension)106- [ ] No direct use of user input in queries107- [ ] Whitelist validation (not blacklist)108- [ ] Error messages don't leak sensitive info109110### 3. SQL Injection Prevention111112#### ❌ NEVER Concatenate SQL113```typescript114// DANGEROUS - SQL Injection vulnerability115const query = `SELECT * FROM users WHERE email = '${userEmail}'`116await db.query(query)117```118119#### ✅ ALWAYS Use Parameterized Queries120```typescript121// Safe - parameterized query122const { data } = await supabase123 .from('users')124 .select('*')125 .eq('email', userEmail)126127// Or with raw SQL128await db.query(129 'SELECT * FROM users WHERE email = $1',130 [userEmail]131)132```133134#### Verification Steps135- [ ] All database queries use parameterized queries136- [ ] No string concatenation in SQL137- [ ] ORM/query builder used correctly138- [ ] Supabase queries properly sanitized139140### 4. Authentication & Authorization141142#### JWT Token Handling143```typescript144// ❌ WRONG: localStorage (vulnerable to XSS)145localStorage.setItem('token', token)146147// ✅ CORRECT: httpOnly cookies148res.setHeader('Set-Cookie',149 `token=${token}; HttpOnly; Secure; SameSite=Strict; Max-Age=3600`)150```151152#### Authorization Checks153```typescript154export async function deleteUser(userId: string, requesterId: string) {155 // ALWAYS verify authorization first156 const requester = await db.users.findUnique({157 where: { id: requesterId }158 })159160 if (requester.role !== 'admin') {161 return NextResponse.json(162 { error: 'Unauthorized' },163 { status: 403 }164 )165 }166167 // Proceed with deletion168 await db.users.delete({ where: { id: userId } })169}170```171172#### Row Level Security (Supabase)173```sql174-- Enable RLS on all tables175ALTER TABLE users ENABLE ROW LEVEL SECURITY;176177-- Users can only view their own data178CREATE POLICY "Users view own data"179 ON users FOR SELECT180 USING (auth.uid() = id);181182-- Users can only update their own data183CREATE POLICY "Users update own data"184 ON users FOR UPDATE185 USING (auth.uid() = id);186```187188#### Verification Steps189- [ ] Tokens stored in httpOnly cookies (not localStorage)190- [ ] Authorization checks before sensitive operations191- [ ] Row Level Security enabled in Supabase192- [ ] Role-based access control implemented193- [ ] Session management secure194195### 5. XSS Prevention196197#### Sanitize HTML198```typescript199import DOMPurify from 'isomorphic-dompurify'200201// ALWAYS sanitize user-provided HTML202function renderUserContent(html: string) {203 const clean = DOMPurify.sanitize(html, {204 ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'p'],205 ALLOWED_ATTR: []206 })207 return <div dangerouslySetInnerHTML={{ __html: clean }} />208}209```210211#### Content Security Policy212```typescript213// next.config.js214const securityHeaders = [215 {216 key: 'Content-Security-Policy',217 value: `218 default-src 'self';219 script-src 'self' 'unsafe-eval' 'unsafe-inline';220 style-src 'self' 'unsafe-inline';221 img-src 'self' data: https:;222 font-src 'self';223 connect-src 'self' https://api.example.com;224 `.replace(/\s{2,}/g, ' ').trim()225 }226]227```228229#### Verification Steps230- [ ] User-provided HTML sanitized231- [ ] CSP headers configured232- [ ] No unvalidated dynamic content rendering233- [ ] React's built-in XSS protection used234235### 6. CSRF Protection236237#### CSRF Tokens238```typescript239import { csrf } from '@/lib/csrf'240241export async function POST(request: Request) {242 const token = request.headers.get('X-CSRF-Token')243244 if (!csrf.verify(token)) {245 return NextResponse.json(246 { error: 'Invalid CSRF token' },247 { status: 403 }248 )249 }250251 // Process request252}253```254255#### SameSite Cookies256```typescript257res.setHeader('Set-Cookie',258 `session=${sessionId}; HttpOnly; Secure; SameSite=Strict`)259```260261#### Verification Steps262- [ ] CSRF tokens on state-changing operations263- [ ] SameSite=Strict on all cookies264- [ ] Double-submit cookie pattern implemented265266### 7. Rate Limiting267268#### API Rate Limiting269```typescript270import rateLimit from 'express-rate-limit'271272const limiter = rateLimit({273 windowMs: 15 * 60 * 1000, // 15 minutes274 max: 100, // 100 requests per window275 message: 'Too many requests'276})277278// Apply to routes279app.use('/api/', limiter)280```281282#### Expensive Operations283```typescript284// Aggressive rate limiting for searches285const searchLimiter = rateLimit({286 windowMs: 60 * 1000, // 1 minute287 max: 10, // 10 requests per minute288 message: 'Too many search requests'289})290291app.use('/api/search', searchLimiter)292```293294#### Verification Steps295- [ ] Rate limiting on all API endpoints296- [ ] Stricter limits on expensive operations297- [ ] IP-based rate limiting298- [ ] User-based rate limiting (authenticated)299300### 8. Sensitive Data Exposure301302#### Logging303```typescript304// ❌ WRONG: Logging sensitive data305console.log('User login:', { email, password })306console.log('Payment:', { cardNumber, cvv })307308// ✅ CORRECT: Redact sensitive data309console.log('User login:', { email, userId })310console.log('Payment:', { last4: card.last4, userId })311```312313#### Error Messages314```typescript315// ❌ WRONG: Exposing internal details316catch (error) {317 return NextResponse.json(318 { error: error.message, stack: error.stack },319 { status: 500 }320 )321}322323// ✅ CORRECT: Generic error messages324catch (error) {325 console.error('Internal error:', error)326 return NextResponse.json(327 { error: 'An error occurred. Please try again.' },328 { status: 500 }329 )330}331```332333#### Verification Steps334- [ ] No passwords, tokens, or secrets in logs335- [ ] Error messages generic for users336- [ ] Detailed errors only in server logs337- [ ] No stack traces exposed to users338339### 9. Blockchain Security (Solana)340341#### Wallet Verification342```typescript343import { verify } from '@solana/web3.js'344345async function verifyWalletOwnership(346 publicKey: string,347 signature: string,348 message: string349) {350 try {351 const isValid = verify(352 Buffer.from(message),353 Buffer.from(signature, 'base64'),354 Buffer.from(publicKey, 'base64')355 )356 return isValid357 } catch (error) {358 return false359 }360}361```362363#### Transaction Verification364```typescript365async function verifyTransaction(transaction: Transaction) {366 // Verify recipient367 if (transaction.to !== expectedRecipient) {368 throw new Error('Invalid recipient')369 }370371 // Verify amount372 if (transaction.amount > maxAmount) {373 throw new Error('Amount exceeds limit')374 }375376 // Verify user has sufficient balance377 const balance = await getBalance(transaction.from)378 if (balance < transaction.amount) {379 throw new Error('Insufficient balance')380 }381382 return true383}384```385386#### Verification Steps387- [ ] Wallet signatures verified388- [ ] Transaction details validated389- [ ] Balance checks before transactions390- [ ] No blind transaction signing391392### 10. Dependency Security393394#### Regular Updates395```bash396# Check for vulnerabilities397npm audit398399# Fix automatically fixable issues400npm audit fix401402# Update dependencies403npm update404405# Check for outdated packages406npm outdated407```408409#### Lock Files410```bash411# ALWAYS commit lock files412git add package-lock.json413414# Use in CI/CD for reproducible builds415npm ci # Instead of npm install416```417418#### Verification Steps419- [ ] Dependencies up to date420- [ ] No known vulnerabilities (npm audit clean)421- [ ] Lock files committed422- [ ] Dependabot enabled on GitHub423- [ ] Regular security updates424425## Security Testing426427### Automated Security Tests428```typescript429// Test authentication430test('requires authentication', async () => {431 const response = await fetch('/api/protected')432 expect(response.status).toBe(401)433})434435// Test authorization436test('requires admin role', async () => {437 const response = await fetch('/api/admin', {438 headers: { Authorization: `Bearer ${userToken}` }439 })440 expect(response.status).toBe(403)441})442443// Test input validation444test('rejects invalid input', async () => {445 const response = await fetch('/api/users', {446 method: 'POST',447 body: JSON.stringify({ email: 'not-an-email' })448 })449 expect(response.status).toBe(400)450})451452// Test rate limiting453test('enforces rate limits', async () => {454 const requests = Array(101).fill(null).map(() =>455 fetch('/api/endpoint')456 )457458 const responses = await Promise.all(requests)459 const tooManyRequests = responses.filter(r => r.status === 429)460461 expect(tooManyRequests.length).toBeGreaterThan(0)462})463```464465## Pre-Deployment Security Checklist466467Before ANY production deployment:468469- [ ] **Secrets**: No hardcoded secrets, all in env vars470- [ ] **Input Validation**: All user inputs validated471- [ ] **SQL Injection**: All queries parameterized472- [ ] **XSS**: User content sanitized473- [ ] **CSRF**: Protection enabled474- [ ] **Authentication**: Proper token handling475- [ ] **Authorization**: Role checks in place476- [ ] **Rate Limiting**: Enabled on all endpoints477- [ ] **HTTPS**: Enforced in production478- [ ] **Security Headers**: CSP, X-Frame-Options configured479- [ ] **Error Handling**: No sensitive data in errors480- [ ] **Logging**: No sensitive data logged481- [ ] **Dependencies**: Up to date, no vulnerabilities482- [ ] **Row Level Security**: Enabled in Supabase483- [ ] **CORS**: Properly configured484- [ ] **File Uploads**: Validated (size, type)485- [ ] **Wallet Signatures**: Verified (if blockchain)486487## Resources488489- [OWASP Top 10](https://owasp.org/www-project-top-ten/)490- [Next.js Security](https://nextjs.org/docs/security)491- [Supabase Security](https://supabase.com/docs/guides/auth)492- [Web Security Academy](https://portswigger.net/web-security)493494---495496**Remember**: Security is not optional. One vulnerability can compromise the entire platform. When in doubt, err on the side of caution.
Full transparency — inspect the skill content before installing.