security-review

在添加身份验证、处理用户输入、处理机密信息、创建API端点或实现支付/敏感功能时,请使用此技能。提供全面的安全检查清单和模式。

查看详情
name:security-reviewdescription: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.author:affaan-mversion:"1.0"

Security Review Skill

This 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

    ❌ NEVER Do This

    const apiKey = "sk-proj-xxxxx"  // Hardcoded secret
    const dbPassword = "password123" // In source code

    ✅ ALWAYS Do This

    const apiKey = process.env.OPENAI_API_KEY
    const dbUrl = process.env.DATABASE_URL

    // Verify secrets exist
    if (!apiKey) {
    throw new Error('OPENAI_API_KEY not configured')
    }

    Verification Steps

  • [ ] No hardcoded API keys, tokens, or passwords

  • [ ] All secrets in environment variables

  • [ ] .env.local in .gitignore

  • [ ] No secrets in git history

  • [ ] Production secrets in hosting platform (Vercel, Railway)
  • 2. Input Validation

    Always Validate User Input

    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 Steps

  • [ ] All user inputs validated with schemas

  • [ ] File uploads restricted (size, type, extension)

  • [ ] No direct use of user input in queries

  • [ ] Whitelist validation (not blacklist)

  • [ ] Error messages don't leak sensitive info
  • 3. SQL Injection Prevention

    ❌ NEVER Concatenate SQL

    // DANGEROUS - SQL Injection vulnerability
    const query = SELECT FROM users WHERE email = '${userEmail}'
    await db.query(query)

    ✅ ALWAYS Use Parameterized Queries

    // Safe - parameterized query
    const { data } = await supabase
    .from('users')
    .select('
    ')
    .eq('email', userEmail)

    // Or with raw SQL
    await db.query(
    'SELECT FROM users WHERE email = $1',
    [userEmail]
    )

    Verification Steps

  • [ ] All database queries use parameterized queries

  • [ ] No string concatenation in SQL

  • [ ] ORM/query builder used correctly

  • [ ] Supabase queries properly sanitized
  • 4. Authentication & Authorization

    JWT Token Handling

    // ❌ WRONG: localStorage (vulnerable to XSS)
    localStorage.setItem('token', token)

    // ✅ CORRECT: httpOnly cookies
    res.setHeader('Set-Cookie',
    token=${token}; HttpOnly; Secure; SameSite=Strict; Max-Age=3600)

    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 Steps

  • [ ] Tokens stored in httpOnly cookies (not localStorage)

  • [ ] Authorization checks before sensitive operations

  • [ ] Row Level Security enabled in Supabase

  • [ ] Role-based access control implemented

  • [ ] Session management secure
  • 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 Steps

  • [ ] User-provided HTML sanitized

  • [ ] CSP headers configured

  • [ ] No unvalidated dynamic content rendering

  • [ ] React's built-in XSS protection used
  • 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 Steps

  • [ ] CSRF tokens on state-changing operations

  • [ ] SameSite=Strict on all cookies

  • [ ] Double-submit cookie pattern implemented
  • 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 Steps

  • [ ] Rate limiting on all API endpoints

  • [ ] Stricter limits on expensive operations

  • [ ] IP-based rate limiting

  • [ ] User-based rate limiting (authenticated)
  • 8. Sensitive Data Exposure

    Logging

    // ❌ WRONG: Logging sensitive data
    console.log('User login:', { email, password })
    console.log('Payment:', { cardNumber, cvv })

    // ✅ CORRECT: Redact sensitive data
    console.log('User login:', { email, userId })
    console.log('Payment:', { last4: card.last4, userId })

    Error Messages

    // ❌ WRONG: Exposing internal details
    catch (error) {
    return NextResponse.json(
    { error: error.message, stack: error.stack },
    { status: 500 }
    )
    }

    // ✅ CORRECT: Generic error messages
    catch (error) {
    console.error('Internal error:', error)
    return NextResponse.json(
    { error: 'An error occurred. Please try again.' },
    { status: 500 }
    )
    }

    Verification Steps

  • [ ] No passwords, tokens, or secrets in logs

  • [ ] Error messages generic for users

  • [ ] Detailed errors only in server logs

  • [ ] No stack traces exposed to users
  • 9. Blockchain Security (Solana)

    Wallet Verification

    import { verify } from '@solana/web3.js'

    async function verifyWalletOwnership(
    publicKey: string,
    signature: string,
    message: string
    ) {
    try {
    const isValid = verify(
    Buffer.from(message),
    Buffer.from(signature, 'base64'),
    Buffer.from(publicKey, 'base64')
    )
    return isValid
    } catch (error) {
    return false
    }
    }

    Transaction Verification

    async function verifyTransaction(transaction: Transaction) {
    // Verify recipient
    if (transaction.to !== expectedRecipient) {
    throw new Error('Invalid recipient')
    }

    // Verify amount
    if (transaction.amount > maxAmount) {
    throw new Error('Amount exceeds limit')
    }

    // Verify user has sufficient balance
    const balance = await getBalance(transaction.from)
    if (balance < transaction.amount) {
    throw new Error('Insufficient balance')
    }

    return true
    }

    Verification Steps

  • [ ] Wallet signatures verified

  • [ ] Transaction details validated

  • [ ] Balance checks before transactions

  • [ ] No blind transaction signing
  • 10. 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 Steps

  • [ ] Dependencies up to date

  • [ ] No known vulnerabilities (npm audit clean)

  • [ ] Lock files committed

  • [ ] Dependabot enabled on GitHub

  • [ ] Regular security updates
  • 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 Security Checklist

    Before ANY production deployment:

  • [ ] Secrets: No hardcoded secrets, all in env vars

  • [ ] Input Validation: All user inputs validated

  • [ ] SQL Injection: All queries parameterized

  • [ ] XSS: User content sanitized

  • [ ] CSRF: Protection enabled

  • [ ] Authentication: Proper token handling

  • [ ] Authorization: Role checks in place

  • [ ] Rate Limiting: Enabled on all endpoints

  • [ ] HTTPS: Enforced in production

  • [ ] Security Headers: CSP, X-Frame-Options configured

  • [ ] Error Handling: No sensitive data in errors

  • [ ] Logging: No sensitive data logged

  • [ ] Dependencies: Up to date, no vulnerabilities

  • [ ] Row Level Security: Enabled in Supabase

  • [ ] CORS: Properly configured

  • [ ] File Uploads: Validated (size, type)

  • [ ] Wallet Signatures: Verified (if blockchain)
  • Resources

  • OWASP Top 10

  • Next.js Security

  • Supabase Security

  • Web Security Academy

  • Remember: Security is not optional. One vulnerability can compromise the entire platform. When in doubt, err on the side of caution.

      security-review - Agent Skills