Tool/everything-claude-code/docs/ja-JP/skills/security-review/SKILL.md
認証の追加、ユーザー入力の処理、シークレットの操作、APIエンドポイントの作成、支払い/機密機能の実装時にこのスキルを使用します。包括的なセキュリティチェックリストとパターンを提供します。
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'
// 検証スキーマを定義
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 Cookie
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, // 1分あたり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
# 常にロックファイルをコミット
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)
})
すべての本番デプロイメントの前に:
覚えておいてください:セキュリティはオプションではありません。1つの脆弱性がプラットフォーム全体を危険にさらす可能性があります。疑わしい場合は、慎重に判断してください。
development
React Native and Expo best practices for building performant mobile apps. Use when building React Native components, optimizing list performance, implementing animations, or working with native modules. Triggers on tasks involving React Native, Expo, mobile performance, or native platform APIs.
development
Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, artifacts, posters, or applications (examples include websites, landing pages, dashboards, React components, HTML/CSS layouts, or when styling/beautifying any web UI). Generates creative, polished code and UI design that avoids generic AI aesthetics.
data-ai
Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.
development
X/Twitter API integration for posting tweets, threads, reading timelines, search, and analytics. Covers OAuth auth patterns, rate limits, and platform-native content posting. Use when the user wants to interact with X programmatically.