From claude-code-settings
認証実装、ユーザー入力処理、シークレット管理、APIエンドポイント作成、決済機能等のセキュリティ関連作業時に使用。包括的なセキュリティチェックリストとパターンを提供。
npx claudepluginhub joshuarweaver/cascade-code-general-misc-1 --plugin tubone24-claude-code-settingsThis skill uses the workspace's default tool permissions.
このスキルは、すべてのコードがセキュリティベストプラクティスに従い、潜在的な脆弱性を特定することを保証します。
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
このスキルは、すべてのコードがセキュリティベストプラクティスに従い、潜在的な脆弱性を特定することを保証します。
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クッキー
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
# 常にロックファイルをコミット
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)
})
本番デプロイ前に必ず確認: