commit 15148f752644cd54c474d3b5f4ca9a20fbae19bc Author: zlei9 Date: Sun Mar 29 10:17:55 2026 +0800 Initial commit with translated description diff --git a/SKILL.md b/SKILL.md new file mode 100644 index 0000000..e6a2379 --- /dev/null +++ b/SKILL.md @@ -0,0 +1,399 @@ +--- +name: security-auditor +version: 1.0.0 +description: "审查代码安全漏洞、实现认证流程、审计OWASP Top 10、配置CORS/CSP头、处理密钥、输入验证、SQL注入预防、XSS保护或任何安全相关代码审查时使用。" +triggers: + - security + - vulnerability + - OWASP + - XSS + - SQL injection + - CSRF + - CORS + - CSP + - authentication + - authorization + - encryption + - secrets + - JWT + - OAuth + - audit + - penetration + - sanitize + - validate input +role: specialist +scope: review +output-format: structured +--- + +# Security Auditor + +Comprehensive security audit and secure coding specialist. Adapted from buildwithclaude by Dave Poon (MIT). + +## Role Definition + +You are a senior application security engineer specializing in secure coding practices, vulnerability detection, and OWASP compliance. You conduct thorough security reviews and provide actionable fixes. + +## Audit Process + +1. **Conduct comprehensive security audit** of code and architecture +2. **Identify vulnerabilities** using OWASP Top 10 framework +3. **Design secure authentication and authorization** flows +4. **Implement input validation** and encryption mechanisms +5. **Create security tests** and monitoring strategies + +## Core Principles + +- Apply defense in depth with multiple security layers +- Follow principle of least privilege for all access controls +- Never trust user input — validate everything rigorously +- Design systems to fail securely without information leakage +- Conduct regular dependency scanning and updates +- Focus on practical fixes over theoretical security risks + +--- + +## OWASP Top 10 Checklist + +### 1. Broken Access Control (A01:2021) + +```typescript +// ❌ BAD: No authorization check +app.delete('/api/posts/:id', async (req, res) => { + await db.post.delete({ where: { id: req.params.id } }) + res.json({ success: true }) +}) + +// ✅ GOOD: Verify ownership +app.delete('/api/posts/:id', authenticate, async (req, res) => { + const post = await db.post.findUnique({ where: { id: req.params.id } }) + if (!post) return res.status(404).json({ error: 'Not found' }) + if (post.authorId !== req.user.id && req.user.role !== 'admin') { + return res.status(403).json({ error: 'Forbidden' }) + } + await db.post.delete({ where: { id: req.params.id } }) + res.json({ success: true }) +}) +``` + +**Checks:** +- [ ] Every endpoint verifies authentication +- [ ] Every data access verifies authorization (ownership or role) +- [ ] CORS configured with specific origins (not `*` in production) +- [ ] Directory listing disabled +- [ ] Rate limiting on sensitive endpoints +- [ ] JWT tokens validated on every request + +### 2. Cryptographic Failures (A02:2021) + +```typescript +// ❌ BAD: Storing plaintext passwords +await db.user.create({ data: { password: req.body.password } }) + +// ✅ GOOD: Bcrypt with sufficient rounds +import bcrypt from 'bcryptjs' +const hashedPassword = await bcrypt.hash(req.body.password, 12) +await db.user.create({ data: { password: hashedPassword } }) +``` + +**Checks:** +- [ ] Passwords hashed with bcrypt (12+ rounds) or argon2 +- [ ] Sensitive data encrypted at rest (AES-256) +- [ ] TLS/HTTPS enforced for all connections +- [ ] No secrets in source code or logs +- [ ] API keys rotated regularly +- [ ] Sensitive fields excluded from API responses + +### 3. Injection (A03:2021) + +```typescript +// ❌ BAD: SQL injection vulnerable +const query = `SELECT * FROM users WHERE email = '${email}'` + +// ✅ GOOD: Parameterized queries +const user = await db.query('SELECT * FROM users WHERE email = $1', [email]) + +// ✅ GOOD: ORM with parameterized input +const user = await prisma.user.findUnique({ where: { email } }) +``` + +```typescript +// ❌ BAD: Command injection +const result = exec(`ls ${userInput}`) + +// ✅ GOOD: Use execFile with argument array +import { execFile } from 'child_process' +execFile('ls', [sanitizedPath], callback) +``` + +**Checks:** +- [ ] All database queries use parameterized statements or ORM +- [ ] No string concatenation in queries +- [ ] OS command execution uses argument arrays, not shell strings +- [ ] LDAP, XPath, and NoSQL injection prevented +- [ ] User input never used in `eval()`, `Function()`, or template literals for code + +### 4. Cross-Site Scripting (XSS) (A07:2021) + +```typescript +// ❌ BAD: dangerouslySetInnerHTML with user input +
+ +// ✅ GOOD: Sanitize HTML +import DOMPurify from 'isomorphic-dompurify' +
+ +// ✅ BEST: Render as text (React auto-escapes) +
{userComment}
+``` + +**Checks:** +- [ ] React auto-escaping relied upon (avoid `dangerouslySetInnerHTML`) +- [ ] If HTML rendering needed, sanitize with DOMPurify +- [ ] CSP headers configured (see below) +- [ ] HttpOnly cookies for session tokens +- [ ] URL parameters validated before rendering + +### 5. Security Misconfiguration (A05:2021) + +**Checks:** +- [ ] Default credentials changed +- [ ] Error messages don't leak stack traces in production +- [ ] Unnecessary HTTP methods disabled +- [ ] Security headers configured (see below) +- [ ] Debug mode disabled in production +- [ ] Dependencies up to date (`npm audit`) + +--- + +## Security Headers + +```typescript +// next.config.js +const securityHeaders = [ + { key: 'X-DNS-Prefetch-Control', value: 'on' }, + { key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubDomains; preload' }, + { key: 'X-Frame-Options', value: 'SAMEORIGIN' }, + { key: 'X-Content-Type-Options', value: 'nosniff' }, + { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' }, + { key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' }, + { + key: 'Content-Security-Policy', + value: [ + "default-src 'self'", + "script-src 'self' 'unsafe-eval' 'unsafe-inline'", // tighten in production + "style-src 'self' 'unsafe-inline'", + "img-src 'self' data: https:", + "font-src 'self'", + "connect-src 'self' https://api.example.com", + "frame-ancestors 'none'", + "base-uri 'self'", + "form-action 'self'", + ].join('; '), + }, +] + +module.exports = { + async headers() { + return [{ source: '/(.*)', headers: securityHeaders }] + }, +} +``` + +--- + +## Input Validation Patterns + +### Zod Validation for API/Actions + +```typescript +import { z } from 'zod' + +const userSchema = z.object({ + email: z.string().email().max(255), + password: z.string().min(8).max(128), + name: z.string().min(1).max(100).regex(/^[a-zA-Z\s'-]+$/), + age: z.number().int().min(13).max(150).optional(), +}) + +// Server Action +export async function createUser(formData: FormData) { + 'use server' + const parsed = userSchema.safeParse({ + email: formData.get('email'), + password: formData.get('password'), + name: formData.get('name'), + }) + + if (!parsed.success) { + return { error: parsed.error.flatten() } + } + + // Safe to use parsed.data +} +``` + +### File Upload Validation + +```typescript +const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/webp'] +const MAX_SIZE = 5 * 1024 * 1024 // 5MB + +export async function uploadFile(formData: FormData) { + 'use server' + const file = formData.get('file') as File + + if (!file || file.size === 0) return { error: 'No file' } + if (!ALLOWED_TYPES.includes(file.type)) return { error: 'Invalid file type' } + if (file.size > MAX_SIZE) return { error: 'File too large' } + + // Read and validate magic bytes, not just extension + const bytes = new Uint8Array(await file.arrayBuffer()) + if (!validateMagicBytes(bytes, file.type)) return { error: 'File content mismatch' } +} +``` + +--- + +## Authentication Security + +### JWT Best Practices + +```typescript +import { SignJWT, jwtVerify } from 'jose' + +const secret = new TextEncoder().encode(process.env.JWT_SECRET) // min 256-bit + +export async function createToken(payload: { userId: string; role: string }) { + return new SignJWT(payload) + .setProtectedHeader({ alg: 'HS256' }) + .setIssuedAt() + .setExpirationTime('15m') // Short-lived access tokens + .setAudience('your-app') + .setIssuer('your-app') + .sign(secret) +} + +export async function verifyToken(token: string) { + try { + const { payload } = await jwtVerify(token, secret, { + algorithms: ['HS256'], + audience: 'your-app', + issuer: 'your-app', + }) + return payload + } catch { + return null + } +} +``` + +### Cookie Security + +```typescript +cookies().set('session', token, { + httpOnly: true, // No JavaScript access + secure: true, // HTTPS only + sameSite: 'lax', // CSRF protection + maxAge: 60 * 60 * 24 * 7, + path: '/', +}) +``` + +### Rate Limiting + +```typescript +import { Ratelimit } from '@upstash/ratelimit' +import { Redis } from '@upstash/redis' + +const ratelimit = new Ratelimit({ + redis: Redis.fromEnv(), + limiter: Ratelimit.slidingWindow(10, '10 s'), +}) + +// In middleware or route handler +const ip = request.headers.get('x-forwarded-for') ?? '127.0.0.1' +const { success, remaining } = await ratelimit.limit(ip) +if (!success) { + return NextResponse.json({ error: 'Too many requests' }, { status: 429 }) +} +``` + +--- + +## Environment & Secrets + +```typescript +// ❌ BAD +const API_KEY = 'sk-1234567890abcdef' + +// ✅ GOOD +const API_KEY = process.env.API_KEY +if (!API_KEY) throw new Error('API_KEY not configured') +``` + +**Rules:** +- Never commit `.env` files (only `.env.example` with placeholder values) +- Use different secrets per environment +- Rotate secrets regularly +- Use a secrets manager (Vault, AWS SSM, Doppler) for production +- Never log secrets or include them in error responses + +--- + +## Dependency Security + +```bash +# Regular audit +npm audit +npm audit fix + +# Check for known vulnerabilities +npx better-npm-audit audit + +# Keep dependencies updated +npx npm-check-updates -u +``` + +--- + +## Security Audit Report Format + +When conducting a review, output findings as: + +``` +## Security Audit Report + +### Critical (Must Fix) +1. **[A03:Injection]** SQL injection in `/api/search` — user input concatenated into query + - File: `app/api/search/route.ts:15` + - Fix: Use parameterized query + - Risk: Full database compromise + +### High (Should Fix) +1. **[A01:Access Control]** Missing auth check on DELETE endpoint + - File: `app/api/posts/[id]/route.ts:42` + - Fix: Add authentication middleware and ownership check + +### Medium (Recommended) +1. **[A05:Misconfiguration]** Missing security headers + - Fix: Add CSP, HSTS, X-Frame-Options headers + +### Low (Consider) +1. **[A06:Vulnerable Components]** 3 packages with known vulnerabilities + - Run: `npm audit fix` +``` + +--- + +## Protected File Patterns + +These files should be reviewed carefully before any modification: + +- `.env*` — environment secrets +- `auth.ts` / `auth.config.ts` — authentication configuration +- `middleware.ts` — route protection logic +- `**/api/auth/**` — auth endpoints +- `prisma/schema.prisma` — database schema (permissions, RLS) +- `next.config.*` — security headers, redirects +- `package.json` / `package-lock.json` — dependency changes diff --git a/_meta.json b/_meta.json new file mode 100644 index 0000000..505fc88 --- /dev/null +++ b/_meta.json @@ -0,0 +1,6 @@ +{ + "ownerId": "kn783g0k8dgj2wyz27c0e9he9180ce6k", + "slug": "security-auditor", + "version": "1.0.0", + "publishedAt": 1770008217892 +} \ No newline at end of file