Tool/everything-claude-code/docs/zh-TW/skills/security-review/SKILL.md
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.
npx skillsauth add lyxjack/toolbox security-reviewInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
此技能確保所有程式碼遵循安全性最佳實務並識別潛在漏洞。
const apiKey = "sk-proj-xxxxx" // 寫死的密鑰
const dbPassword = "password123" // 在原始碼中
const apiKey = process.env.OPENAI_API_KEY
const dbUrl = process.env.DATABASE_URL
// 驗證密鑰存在
if (!apiKey) {
throw new Error('OPENAI_API_KEY not configured')
}
.env.local 在 .gitignore 中import { z } from 'zod'
// 定義驗證 schema
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) {
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
}
}
function validateFileUpload(file: File) {
// 大小檢查(最大 5MB)
const maxSize = 5 * 1024 * 1024
if (file.size > maxSize) {
throw new Error('File too large (max 5MB)')
}
// 類型檢查
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif']
if (!allowedTypes.includes(file.type)) {
throw new Error('Invalid file type')
}
// 副檔名檢查
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
}
// 危險 - SQL 注入漏洞
const query = `SELECT * FROM users WHERE email = '${userEmail}'`
await db.query(query)
// 安全 - 參數化查詢
const { data } = await supabase
.from('users')
.select('*')
.eq('email', userEmail)
// 或使用原始 SQL
await db.query(
'SELECT * FROM users WHERE email = $1',
[userEmail]
)
// ❌ 錯誤:localStorage(易受 XSS 攻擊)
localStorage.setItem('token', token)
// ✅ 正確:httpOnly cookies
res.setHeader('Set-Cookie',
`token=${token}; HttpOnly; Secure; SameSite=Strict; Max-Age=3600`)
export async function deleteUser(userId: string, requesterId: string) {
// 總是先驗證授權
const requester = await db.users.findUnique({
where: { id: requesterId }
})
if (requester.role !== 'admin') {
return NextResponse.json(
{ error: 'Unauthorized' },
{ status: 403 }
)
}
// 繼續刪除
await db.users.delete({ where: { id: userId } })
}
-- 在所有表格上啟用 RLS
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
-- 使用者只能查看自己的資料
CREATE POLICY "Users view own data"
ON users FOR SELECT
USING (auth.uid() = id);
-- 使用者只能更新自己的資料
CREATE POLICY "Users update own data"
ON users FOR UPDATE
USING (auth.uid() = id);
import DOMPurify from 'isomorphic-dompurify'
// 總是淨化使用者提供的 HTML
function renderUserContent(html: string) {
const clean = DOMPurify.sanitize(html, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'p'],
ALLOWED_ATTR: []
})
return <div dangerouslySetInnerHTML={{ __html: clean }} />
}
// 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()
}
]
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 }
)
}
// 處理請求
}
res.setHeader('Set-Cookie',
`session=${sessionId}; HttpOnly; Secure; SameSite=Strict`)
import rateLimit from 'express-rate-limit'
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 分鐘
max: 100, // 每視窗 100 個請求
message: 'Too many requests'
})
// 套用到路由
app.use('/api/', limiter)
// 搜尋的積極速率限制
const searchLimiter = rateLimit({
windowMs: 60 * 1000, // 1 分鐘
max: 10, // 每分鐘 10 個請求
message: 'Too many search requests'
})
app.use('/api/search', searchLimiter)
// ❌ 錯誤:記錄敏感資料
console.log('User login:', { email, password })
console.log('Payment:', { cardNumber, cvv })
// ✅ 正確:遮蔽敏感資料
console.log('User login:', { email, userId })
console.log('Payment:', { last4: card.last4, userId })
// ❌ 錯誤:暴露內部細節
catch (error) {
return NextResponse.json(
{ error: error.message, stack: error.stack },
{ status: 500 }
)
}
// ✅ 正確:通用錯誤訊息
catch (error) {
console.error('Internal error:', error)
return NextResponse.json(
{ error: 'An error occurred. Please try again.' },
{ status: 500 }
)
}
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
}
}
async function verifyTransaction(transaction: Transaction) {
// 驗證收款人
if (transaction.to !== expectedRecipient) {
throw new Error('Invalid recipient')
}
// 驗證金額
if (transaction.amount > maxAmount) {
throw new Error('Amount exceeds limit')
}
// 驗證使用者有足夠餘額
const balance = await getBalance(transaction.from)
if (balance < transaction.amount) {
throw new Error('Insufficient balance')
}
return true
}
# 檢查漏洞
npm audit
# 自動修復可修復的問題
npm audit fix
# 更新依賴
npm update
# 檢查過時套件
npm outdated
# 總是 commit lock 檔案
git add package-lock.json
# 在 CI/CD 中使用以獲得可重現的建置
npm ci # 而非 npm install
// 測試認證
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 tooManyRequests = responses.filter(r => r.status === 429)
expect(tooManyRequests.length).toBeGreaterThan(0)
})
任何生產部署前:
記住:安全性不是可選的。一個漏洞可能危及整個平台。有疑慮時,選擇謹慎的做法。
tools
Orchestrate Unity Editor via MCP (Model Context Protocol) tools and resources. Use when working with Unity projects through MCP for Unity - creating/modifying GameObjects, editing scripts, managing scenes, running tests, or any Unity Editor automation. Provides best practices, tool schemas, and workflow patterns for effective Unity-MCP integration.
development
Use when implementing any feature or bugfix, before writing implementation code
development
Use when encountering any bug, test failure, or unexpected behavior, before proposing fixes
testing
Use when creating new skills, editing existing skills, or verifying skills work before deployment