vibe-security-skill
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseVibe Security Skill
Vibe Security 技能
Skill by ara.so — Security Skills collection.
This skill provides expertise in using the Vibe Security tool to audit vibe-coded applications for common security vulnerabilities that AI coding assistants frequently introduce. It helps identify hardcoded secrets, missing RLS policies, insecure auth patterns, payment vulnerabilities, and other security anti-patterns.
由ara.so开发的技能——安全技能合集。
本技能提供使用Vibe Security工具审核vibe编码应用的专业能力,可检测AI代码助手常引入的各类安全漏洞,包括硬编码机密信息、缺失的RLS策略、不安全的认证模式、支付漏洞及其他安全反模式。
What Vibe Security Does
Vibe Security 功能介绍
Vibe Security is an agent skill that scans codebases for security vulnerability patterns common in AI-generated code. It uses technology-specific reference files to audit only relevant parts of your stack (Supabase, Stripe, React Native, etc.), catching issues like:
- Hardcoded API keys and secrets
- Disabled or missing Row-Level Security (RLS) policies
- Insecure authentication patterns
- Client-submitted payment amounts
- Missing rate limiting
- Tokens stored in localStorage
- Exposed secrets in mobile bundles
- AI API keys without usage caps
Vibe Security是一款Agent技能,用于扫描代码库中AI生成代码常见的安全漏洞模式。它通过特定技术的参考文件,仅审核你技术栈中相关的部分(如Supabase、Stripe、React Native等),可检测以下问题:
- 硬编码的API密钥和机密信息
- 禁用或缺失的行级安全(Row-Level Security, RLS)策略
- 不安全的认证模式
- 客户端提交支付金额
- 缺失速率限制
- 令牌存储在localStorage中
- 移动包中暴露的机密信息
- 无使用上限的AI API密钥
Installation
安装方法
For Claude Code
适用于Claude Code
bash
npx skills add https://github.com/raroque/vibe-security-skill --skill vibe-securitybash
npx skills add https://github.com/raroque/vibe-security-skill --skill vibe-securityFor OpenAI Codex
适用于OpenAI Codex
bash
npx skills add https://github.com/raroque/vibe-security-skill --skill vibe-securitySelect "Codex" when prompted.
bash
npx skills add https://github.com/raroque/vibe-security-skill --skill vibe-security出现提示时选择“Codex”。
Manual Installation
手动安装
bash
undefinedbash
undefinedProject-level
项目级安装
git clone https://github.com/raroque/vibe-security-skill.git
cp -r vibe-security-skill/vibe-security/ .claude/skills/vibe-security/
git clone https://github.com/raroque/vibe-security-skill.git
cp -r vibe-security-skill/vibe-security/ .claude/skills/vibe-security/
Global installation
全局安装
cp -r vibe-security-skill/vibe-security/ ~/.claude/skills/vibe-security/
undefinedcp -r vibe-security-skill/vibe-security/ ~/.claude/skills/vibe-security/
undefinedUsage
使用方法
Triggering Security Audits
触发安全审计
Claude Code:
/vibe-securityOr use natural language:
- "check my code for security issues"
- "is this safe?"
- "audit this Supabase setup"
Codex:
$vibe-securityClaude Code:
/vibe-security或使用自然语言指令:
- "检查我的代码是否存在安全问题"
- "这样安全吗?"
- "审核这个Supabase配置"
Codex:
$vibe-securityAutomatic Activation
自动激活
The skill automatically activates when working with:
- Authentication flows
- Payment processing
- Database queries
- API key configuration
- User data handling
- Environment variables
当处理以下场景时,技能会自动激活:
- 认证流程
- 支付处理
- 数据库查询
- API密钥配置
- 用户数据处理
- 环境变量
Key Security Checks
核心安全检查项
1. Secrets & Environment Variables
1. 机密信息与环境变量
Bad Pattern:
typescript
// ❌ Hardcoded secret
const supabase = createClient(
'https://xxx.supabase.co',
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
)
// ❌ Exposed in client bundle
const OPENAI_API_KEY = 'sk-proj-...'Good Pattern:
typescript
// ✅ Environment variable
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
// ✅ Server-side only
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY // Not NEXT_PUBLIC_
})不良模式:
typescript
// ❌ 硬编码机密信息
const supabase = createClient(
'https://xxx.supabase.co',
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
)
// ❌ 在客户端包中暴露
const OPENAI_API_KEY = 'sk-proj-...'良好模式:
typescript
// ✅ 使用环境变量
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
// ✅ 仅在服务端使用
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY // 不使用NEXT_PUBLIC_前缀
})2. Supabase Row-Level Security
2. Supabase行级安全
Bad Pattern:
sql
-- ❌ RLS disabled
CREATE TABLE user_data (
id uuid,
user_id uuid,
sensitive_data text
);
-- No ALTER TABLE ... ENABLE ROW LEVEL SECURITY
-- ❌ Allows everything
CREATE POLICY "allow_all" ON user_data
FOR ALL USING (true);Good Pattern:
sql
-- ✅ RLS enabled with proper policies
CREATE TABLE user_data (
id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
user_id uuid REFERENCES auth.users NOT NULL,
sensitive_data text
);
ALTER TABLE user_data ENABLE ROW LEVEL SECURITY;
CREATE POLICY "users_select_own" ON user_data
FOR SELECT USING (auth.uid() = user_id);
CREATE POLICY "users_insert_own" ON user_data
FOR INSERT WITH CHECK (auth.uid() = user_id);
CREATE POLICY "users_update_own" ON user_data
FOR UPDATE USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);不良模式:
sql
-- ❌ RLS未启用
CREATE TABLE user_data (
id uuid,
user_id uuid,
sensitive_data text
);
-- 未执行ALTER TABLE ... ENABLE ROW LEVEL SECURITY
-- ❌ 允许所有操作
CREATE POLICY "allow_all" ON user_data
FOR ALL USING (true);良好模式:
sql
-- ✅ 启用RLS并配置合理策略
CREATE TABLE user_data (
id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
user_id uuid REFERENCES auth.users NOT NULL,
sensitive_data text
);
ALTER TABLE user_data ENABLE ROW LEVEL SECURITY;
CREATE POLICY "users_select_own" ON user_data
FOR SELECT USING (auth.uid() = user_id);
CREATE POLICY "users_insert_own" ON user_data
FOR INSERT WITH CHECK (auth.uid() = user_id);
CREATE POLICY "users_update_own" ON user_data
FOR UPDATE USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);3. Authentication & Authorization
3. 认证与授权
Bad Pattern:
typescript
// ❌ Decoding without verification
import jwt from 'jsonwebtoken'
const decoded = jwt.decode(token) // No signature check!
const userId = decoded.sub
// ❌ Middleware-only auth
// middleware.ts
export function middleware(req: NextRequest) {
const token = req.cookies.get('token')
if (!token) return NextResponse.redirect('/login')
}
// app/api/sensitive/route.ts - NOT protected!
export async function GET() {
return NextResponse.json(await db.getAllUserData())
}Good Pattern:
typescript
// ✅ Verify JWT signature
import jwt from 'jsonwebtoken'
const decoded = jwt.verify(token, process.env.JWT_SECRET!) as JwtPayload
const userId = decoded.sub
// ✅ Auth in every API route
// lib/auth.ts
export async function requireAuth(req: Request) {
const token = req.headers.get('authorization')?.replace('Bearer ', '')
if (!token) throw new Error('Unauthorized')
const decoded = jwt.verify(token, process.env.JWT_SECRET!)
return decoded
}
// app/api/sensitive/route.ts
export async function GET(req: Request) {
const user = await requireAuth(req)
return NextResponse.json(await db.getUserData(user.sub))
}不良模式:
typescript
// ❌ 解码时不验证签名
import jwt from 'jsonwebtoken'
const decoded = jwt.decode(token) // 未检查签名!
const userId = decoded.sub
// ❌ 仅在中间件中做认证
// middleware.ts
export function middleware(req: NextRequest) {
const token = req.cookies.get('token')
if (!token) return NextResponse.redirect('/login')
}
// app/api/sensitive/route.ts - 未受保护!
export async function GET() {
return NextResponse.json(await db.getAllUserData())
}良好模式:
typescript
// ✅ 验证JWT签名
import jwt from 'jsonwebtoken'
const decoded = jwt.verify(token, process.env.JWT_SECRET!) as JwtPayload
const userId = decoded.sub
// ✅ 在每个API路由中做认证
// lib/auth.ts
export async function requireAuth(req: Request) {
const token = req.headers.get('authorization')?.replace('Bearer ', '')
if (!token) throw new Error('未授权')
const decoded = jwt.verify(token, process.env.JWT_SECRET!)
return decoded
}
// app/api/sensitive/route.ts
export async function GET(req: Request) {
const user = await requireAuth(req)
return NextResponse.json(await db.getUserData(user.sub))
}4. Payment Security
4. 支付安全
Bad Pattern:
typescript
// ❌ Client submits price
export async function POST(req: Request) {
const { amount, productId } = await req.json()
const session = await stripe.checkout.sessions.create({
line_items: [{
price_data: {
currency: 'usd',
product: productId,
unit_amount: amount // ❌ Trusting client!
},
quantity: 1
}],
mode: 'payment'
})
}Good Pattern:
typescript
// ✅ Server determines price
const PRICES = {
'basic': 999,
'pro': 2999,
'enterprise': 9999
} as const
export async function POST(req: Request) {
const { plan } = await req.json()
if (!PRICES[plan]) throw new Error('Invalid plan')
const session = await stripe.checkout.sessions.create({
line_items: [{
price_data: {
currency: 'usd',
product: plan,
unit_amount: PRICES[plan] // ✅ Server-controlled
},
quantity: 1
}],
mode: 'payment'
})
}
// ✅ Verify webhook signatures
export async function POST(req: Request) {
const body = await req.text()
const sig = req.headers.get('stripe-signature')!
const event = stripe.webhooks.constructEvent(
body,
sig,
process.env.STRIPE_WEBHOOK_SECRET!
)
// Process event...
}不良模式:
typescript
// ❌ 由客户端提交金额
export async function POST(req: Request) {
const { amount, productId } = await req.json()
const session = await stripe.checkout.sessions.create({
line_items: [{
price_data: {
currency: 'usd',
product: productId,
unit_amount: amount // ❌ 信任客户端输入!
},
quantity: 1
}],
mode: 'payment'
})
}良好模式:
typescript
// ✅ 由服务端确定价格
const PRICES = {
'basic': 999,
'pro': 2999,
'enterprise': 9999
} as const
export async function POST(req: Request) {
const { plan } = await req.json()
if (!PRICES[plan]) throw new Error('无效套餐')
const session = await stripe.checkout.sessions.create({
line_items: [{
price_data: {
currency: 'usd',
product: plan,
unit_amount: PRICES[plan] // ✅ 服务端控制价格
},
quantity: 1
}],
mode: 'payment'
})
}
// ✅ 验证Webhook签名
export async function POST(req: Request) {
const body = await req.text()
const sig = req.headers.get('stripe-signature')!
const event = stripe.webhooks.constructEvent(
body,
sig,
process.env.STRIPE_WEBHOOK_SECRET!
)
// 处理事件...
}5. Rate Limiting
5. 速率限制
Bad Pattern:
typescript
// ❌ No rate limiting on expensive endpoints
export async function POST(req: Request) {
const { prompt } = await req.json()
const completion = await openai.chat.completions.create({
model: 'gpt-4',
messages: [{ role: 'user', content: prompt }]
})
return NextResponse.json(completion)
}Good Pattern:
typescript
// ✅ Server-side rate limiting
import { Ratelimit } from '@upstash/ratelimit'
import { Redis } from '@upstash/redis'
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, '1 h'),
analytics: true
})
export async function POST(req: Request) {
const user = await requireAuth(req)
const { success } = await ratelimit.limit(user.sub)
if (!success) {
return NextResponse.json(
{ error: 'Rate limit exceeded' },
{ status: 429 }
)
}
const { prompt } = await req.json()
const completion = await openai.chat.completions.create({
model: 'gpt-4',
messages: [{ role: 'user', content: prompt }],
max_tokens: 1000 // ✅ Cap token usage
})
return NextResponse.json(completion)
}不良模式:
typescript
// ❌ 对资源密集型接口未做速率限制
export async function POST(req: Request) {
const { prompt } = await req.json()
const completion = await openai.chat.completions.create({
model: 'gpt-4',
messages: [{ role: 'user', content: prompt }]
})
return NextResponse.json(completion)
}良好模式:
typescript
// ✅ 服务端速率限制
import { Ratelimit } from '@upstash/ratelimit'
import { Redis } from '@upstash/redis'
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, '1 h'),
analytics: true
})
export async function POST(req: Request) {
const user = await requireAuth(req)
const { success } = await ratelimit.limit(user.sub)
if (!success) {
return NextResponse.json(
{ error: '超出速率限制' },
{ status: 429 }
)
}
const { prompt } = await req.json()
const completion = await openai.chat.completions.create({
model: 'gpt-4',
messages: [{ role: 'user', content: prompt }],
max_tokens: 1000 // ✅ 限制令牌使用量
})
return NextResponse.json(completion)
}6. Mobile Security (React Native / Expo)
6. 移动端安全(React Native / Expo)
Bad Pattern:
typescript
// ❌ API key in JS bundle
const OPENAI_API_KEY = 'sk-proj-...'
// ❌ Token in AsyncStorage
import AsyncStorage from '@react-native-async-storage/async-storage'
await AsyncStorage.setItem('auth_token', token)Good Pattern:
typescript
// ✅ Use backend proxy for AI calls
const response = await fetch('https://api.myapp.com/ai/chat', {
method: 'POST',
headers: {
'Authorization': `Bearer ${userToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ prompt })
})
// ✅ Use secure storage for tokens
import * as SecureStore from 'expo-secure-store'
await SecureStore.setItemAsync('auth_token', token)
const token = await SecureStore.getItemAsync('auth_token')不良模式:
typescript
// ❌ API密钥存于JS包中
const OPENAI_API_KEY = 'sk-proj-...'
// ❌ 令牌存于AsyncStorage
import AsyncStorage from '@react-native-async-storage/async-storage'
await AsyncStorage.setItem('auth_token', token)良好模式:
typescript
// ✅ 通过后端代理调用AI服务
const response = await fetch('https://api.myapp.com/ai/chat', {
method: 'POST',
headers: {
'Authorization': `Bearer ${userToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ prompt })
})
// ✅ 使用安全存储保存令牌
import * as SecureStore from 'expo-secure-store'
await SecureStore.setItemAsync('auth_token', token)
const token = await SecureStore.getItemAsync('auth_token')7. SQL Injection & Data Access
7. SQL注入与数据访问
Bad Pattern:
typescript
// ❌ SQL injection vulnerability
export async function GET(req: Request) {
const { searchParams } = new URL(req.url)
const userId = searchParams.get('userId')
const result = await db.$queryRawUnsafe(
`SELECT * FROM users WHERE id = ${userId}`
)
}
// ❌ Prisma operator injection
const users = await prisma.user.findMany({
where: req.query // ❌ Direct user input
})Good Pattern:
typescript
// ✅ Parameterized queries
export async function GET(req: Request) {
const { searchParams } = new URL(req.url)
const userId = searchParams.get('userId')
const result = await db.$queryRaw`
SELECT * FROM users WHERE id = ${userId}
`
}
// ✅ Validate and sanitize input
const userIdSchema = z.string().uuid()
const userId = userIdSchema.parse(searchParams.get('userId'))
const user = await prisma.user.findUnique({
where: { id: userId }
})不良模式:
typescript
// ❌ 存在SQL注入漏洞
export async function GET(req: Request) {
const { searchParams } = new URL(req.url)
const userId = searchParams.get('userId')
const result = await db.$queryRawUnsafe(
`SELECT * FROM users WHERE id = ${userId}`
)
}
// ❌ Prisma操作符注入
const users = await prisma.user.findMany({
where: req.query // ❌ 直接使用用户输入
})良好模式:
typescript
// ✅ 参数化查询
export async function GET(req: Request) {
const { searchParams } = new URL(req.url)
const userId = searchParams.get('userId')
const result = await db.$queryRaw`
SELECT * FROM users WHERE id = ${userId}
`
}
// ✅ 验证并清理输入
const userIdSchema = z.string().uuid()
const userId = userIdSchema.parse(searchParams.get('userId'))
const user = await prisma.user.findUnique({
where: { id: userId }
})Configuration
配置说明
Vibe Security uses reference files organized by technology. The skill automatically detects your stack and applies relevant checks.
Vibe Security使用按技术分类的参考文件,技能会自动检测你的技术栈并应用相关检查。
Supported Technologies
支持的技术栈
- Databases: Supabase, Firebase, Convex, Prisma
- Payments: Stripe
- Mobile: React Native, Expo
- AI: OpenAI, Anthropic, other LLM providers
- Frameworks: Next.js, Vite, Express
- 数据库: Supabase, Firebase, Convex, Prisma
- 支付: Stripe
- 移动端: React Native, Expo
- AI: OpenAI, Anthropic, 其他LLM提供商
- 框架: Next.js, Vite, Express
Customizing Rules
自定义规则
You can extend the skill by adding custom security rules in your project's directory.
.claude/skills/vibe-security/rules/你可以在项目的目录中添加自定义安全规则,扩展技能功能。
.claude/skills/vibe-security/rules/Common Issues & Troubleshooting
常见问题与故障排查
"Skill not activating automatically"
"技能未自动激活"
Make sure you're working with code that involves:
- Authentication/authorization
- Database queries
- Payment processing
- Environment variables
- API integrations
Or explicitly trigger with (Claude) or (Codex).
/vibe-security$vibe-security确保你正在处理涉及以下内容的代码:
- 认证/授权
- 数据库查询
- 支付处理
- 环境变量
- API集成
或使用(Claude)或(Codex)手动触发。
/vibe-security$vibe-security"False positives on legitimate patterns"
"对合法模式误报"
The skill prioritizes security over convenience. If you have a legitimate use case for a flagged pattern:
- Document why it's safe with comments
- Ensure compensating controls exist
- Consider if there's a more secure alternative
技能优先保障安全而非便利性。如果被标记的模式有合法使用场景:
- 用注释说明其安全性
- 确保存在补偿控制措施
- 考虑是否有更安全的替代方案
"Not catching vulnerabilities"
"未检测到漏洞"
The skill focuses on common AI-generated security mistakes. For comprehensive security:
- Run additional tools (Snyk, npm audit, etc.)
- Conduct manual security reviews
- Follow OWASP guidelines
- Implement defense in depth
技能专注于AI生成代码中常见的安全错误。如需全面安全保障:
- 使用其他工具(如Snyk、npm audit等)
- 进行人工安全审查
- 遵循OWASP指南
- 实施纵深防御
Best Practices
最佳实践
Environment Variables
环境变量
bash
undefinedbash
undefined.env.local (never commit)
.env.local(绝不要提交到版本库)
DATABASE_URL="postgresql://..."
STRIPE_SECRET_KEY="sk_test_..."
OPENAI_API_KEY="sk-proj-..."
DATABASE_URL="postgresql://..."
STRIPE_SECRET_KEY="sk_test_..."
OPENAI_API_KEY="sk-proj-..."
Public vars (safe in client bundle)
公共变量(可安全存于客户端包)
NEXT_PUBLIC_SUPABASE_URL="https://xxx.supabase.co"
NEXT_PUBLIC_SUPABASE_ANON_KEY="eyJ..."
undefinedNEXT_PUBLIC_SUPABASE_URL="https://xxx.supabase.co"
NEXT_PUBLIC_SUPABASE_ANON_KEY="eyJ..."
undefinedSecurity 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: 'origin-when-cross-origin'
}
]
module.exports = {
async headers() {
return [
{
source: '/:path*',
headers: securityHeaders
}
]
}
}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: 'origin-when-cross-origin'
}
]
module.exports = {
async headers() {
return [
{
source: '/:path*',
headers: securityHeaders
}
]
}
}Input Validation
输入验证
typescript
import { z } from 'zod'
const createUserSchema = z.object({
email: z.string().email(),
name: z.string().min(1).max(100),
age: z.number().int().min(13).max(120)
})
export async function POST(req: Request) {
const body = await req.json()
const validated = createUserSchema.parse(body)
// Safe to use validated data
await db.user.create({ data: validated })
}typescript
import { z } from 'zod'
const createUserSchema = z.object({
email: z.string().email(),
name: z.string().min(1).max(100),
age: z.number().int().min(13).max(120)
})
export async function POST(req: Request) {
const body = await req.json()
const validated = createUserSchema.parse(body)
// 可安全使用验证后的数据
await db.user.create({ data: validated })
}Additional Resources
额外资源
Created by Chris Raroque and the team at Aloa.