vibe-security-skill

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Vibe 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-security
bash
npx skills add https://github.com/raroque/vibe-security-skill --skill vibe-security

For OpenAI Codex

适用于OpenAI Codex

bash
npx skills add https://github.com/raroque/vibe-security-skill --skill vibe-security
Select "Codex" when prompted.
bash
npx skills add https://github.com/raroque/vibe-security-skill --skill vibe-security
出现提示时选择“Codex”。

Manual Installation

手动安装

bash
undefined
bash
undefined

Project-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/
undefined
cp -r vibe-security-skill/vibe-security/ ~/.claude/skills/vibe-security/
undefined

Usage

使用方法

Triggering Security Audits

触发安全审计

Claude Code:
/vibe-security
Or use natural language:
  • "check my code for security issues"
  • "is this safe?"
  • "audit this Supabase setup"
Codex:
$vibe-security
Claude Code:
/vibe-security
或使用自然语言指令:
  • "检查我的代码是否存在安全问题"
  • "这样安全吗?"
  • "审核这个Supabase配置"
Codex:
$vibe-security

Automatic 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
.claude/skills/vibe-security/rules/
directory.
你可以在项目的
.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
/vibe-security
(Claude) or
$vibe-security
(Codex).
确保你正在处理涉及以下内容的代码:
  • 认证/授权
  • 数据库查询
  • 支付处理
  • 环境变量
  • API集成
或使用
/vibe-security
(Claude)或
$vibe-security
(Codex)手动触发。

"False positives on legitimate patterns"

"对合法模式误报"

The skill prioritizes security over convenience. If you have a legitimate use case for a flagged pattern:
  1. Document why it's safe with comments
  2. Ensure compensating controls exist
  3. Consider if there's a more secure alternative
技能优先保障安全而非便利性。如果被标记的模式有合法使用场景:
  1. 用注释说明其安全性
  2. 确保存在补偿控制措施
  3. 考虑是否有更安全的替代方案

"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
undefined
bash
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..."
undefined
NEXT_PUBLIC_SUPABASE_URL="https://xxx.supabase.co" NEXT_PUBLIC_SUPABASE_ANON_KEY="eyJ..."
undefined

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: '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

额外资源