
validate-security
by keidsondesigner
SKILL.md
name: validate-security description: Valida implementações de segurança em Server Actions, verificando autenticação, autorização e validação de dados. Use para revisar segurança antes de merge ou deploy. tools: Read, Grep, Glob
Validate Security Skill
Esta skill valida implementações de segurança seguindo as diretrizes críticas do projeto Bewear.
Quando Usar
- Revisar Server Actions antes de merge
- Auditar segurança do código
- Verificar conformidade com padrões Bewear
- Identificar vulnerabilidades de segurança
Checklist de Segurança Obrigatório
1. Diretiva "use server"
CRITICAL: TODA Server Action DEVE ter "use server" na primeira linha.
// ✅ CORRETO
"use server";
import { auth } from "@/lib/auth";
// ❌ INCORRETO
import { auth } from "@/lib/auth";
"use server";
2. Verificação de Autenticação
CRITICAL: TODA Server Action DEVE verificar autenticação.
Padrão obrigatório:
const session = await auth.api.getSession({
headers: await headers(),
});
if (!session?.user) {
throw new Error("Unauthorized");
}
Verificações:
-
auth.api.getSession()chamado -
headers: await headers()passado - Verificação
if (!session?.user)presente -
throw new Error("Unauthorized")em caso de falha
Arquivo de referência: src/actions/add-cart-product/index.ts (linhas 14-20)
3. Validação de Schema
CRITICAL: TODA Server Action que recebe dados DEVE validar com Zod.
// ✅ CORRETO
import { nomeSchema, NomeSchema } from "./schema";
export async function nomeAction(data: NomeSchema) {
nomeSchema.parse(data);
const session = await auth.api.getSession({
headers: await headers(),
});
}
// ❌ INCORRETO
export async function nomeAction(data: any) {
const session = await auth.api.getSession({
headers: await headers(),
});
}
Verificações:
- Schema Zod definido em
schema.ts - Tipo inferido com
z.infer<typeof schema> -
schema.parse(data)chamado antes de outras operações - Parâmetro tipado com tipo inferido
4. Validação de Ownership
CRITICAL: Quando recurso pertence a usuário, DEVE validar ownership.
// ✅ CORRETO
const order = await db.query.orderTable.findFirst({
where: eq(orderTable.id, orderId),
});
if (!order) {
throw new Error("Order not found");
}
if (order.userId !== session.user.id) {
throw new Error("Unauthorized");
}
// ❌ INCORRETO
const order = await db.query.orderTable.findFirst({
where: eq(orderTable.id, orderId),
});
return order;
Verificações:
- Recurso buscado do banco
- Verificação de existência (
if (!resource)) - Comparação
resource.userId === session.user.id - Error apropriado se ownership falhar
Arquivo de referência: src/actions/create-checkout-session-stripe/index.ts (linhas 38-45)
5. SQL Injection Prevention
Drizzle ORM protege contra SQL injection, mas verifique:
// ✅ CORRETO - Usa query builder Drizzle
const product = await db.query.productTable.findFirst({
where: eq(productTable.id, productId),
});
// ✅ CORRETO - Usa prepared statements Drizzle
await db.insert(productTable).values({
name: data.name,
});
// ❌ INCORRETO - SQL direto (NUNCA faça isso)
await db.execute(`SELECT * FROM products WHERE id = '${productId}'`);
Verificações:
- Usa query builder Drizzle
- Nunca concatena strings SQL
- Usa operadores Drizzle (eq, and, or, etc.)
6. Environment Variables
Verificar se variáveis sensíveis estão protegidas:
// ✅ CORRETO
if (!process.env.STRIPE_SECRET_KEY) {
throw new Error("STRIPE_SECRET_KEY is not set");
}
// ❌ INCORRETO
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
Verificações:
- Chaves sensíveis verificadas antes de uso
- Não expor chaves secretas no client
- Prefixo
NEXT_PUBLIC_apenas para valores públicos
7. Error Messages
Não vazar informações sensíveis em erros:
// ✅ CORRETO
if (user.password !== hashedPassword) {
throw new Error("Invalid credentials");
}
// ❌ INCORRETO - Vaza informação
if (!user) {
throw new Error("User with email john@example.com not found");
}
Verificações:
- Mensagens genéricas para autenticação
- Não expor IDs internos
- Não expor stack traces para usuários
8. Rate Limiting
Considerar rate limiting para operações sensíveis:
// Operações que precisam rate limiting:
// - Login / Register
// - Password reset
// - Payment operations
// - Expensive queries
9. CSRF Protection
Next.js Server Actions protege automaticamente contra CSRF.
Verificações:
- Ação é Server Action (não API Route)
- Chamada vem de formulário ou código client
- Não desabilitar proteções built-in
10. Data Sanitization
// ✅ CORRETO - Zod valida e sanitiza
const schema = z.object({
name: z.string().trim().min(1).max(100),
email: z.string().email().toLowerCase(),
});
// ❌ INCORRETO - Aceita qualquer input
const schema = z.object({
name: z.string(),
});
Verificações:
- Strings têm limites de tamanho
- Emails validados com
.email() - Números têm min/max apropriados
- Trim em strings quando necessário
Padrões de Vulnerabilidade Comuns
🚨 IDOR (Insecure Direct Object Reference)
// ❌ VULNERÁVEL
export async function deleteOrder(orderId: string) {
const session = await auth.api.getSession({
headers: await headers(),
});
if (!session?.user) {
throw new Error("Unauthorized");
}
await db.delete(orderTable).where(eq(orderTable.id, orderId));
}
// ✅ SEGURO
export async function deleteOrder(orderId: string) {
const session = await auth.api.getSession({
headers: await headers(),
});
if (!session?.user) {
throw new Error("Unauthorized");
}
const order = await db.query.orderTable.findFirst({
where: eq(orderTable.id, orderId),
});
if (!order) {
throw new Error("Order not found");
}
if (order.userId !== session.user.id) {
throw new Error("Unauthorized");
}
await db.delete(orderTable).where(eq(orderTable.id, orderId));
}
🚨 Mass Assignment
// ❌ VULNERÁVEL
export async function updateUser(data: any) {
const session = await auth.api.getSession({
headers: await headers(),
});
if (!session?.user) {
throw new Error("Unauthorized");
}
await db.update(userTable)
.set(data)
.where(eq(userTable.id, session.user.id));
}
// ✅ SEGURO
const updateUserSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
});
export async function updateUser(data: UpdateUserSchema) {
updateUserSchema.parse(data);
const session = await auth.api.getSession({
headers: await headers(),
});
if (!session?.user) {
throw new Error("Unauthorized");
}
await db.update(userTable)
.set({
name: data.name,
email: data.email,
})
.where(eq(userTable.id, session.user.id));
}
🚨 Privilege Escalation
// ❌ VULNERÁVEL
export async function promoteToAdmin(userId: string) {
const session = await auth.api.getSession({
headers: await headers(),
});
if (!session?.user) {
throw new Error("Unauthorized");
}
await db.update(userTable)
.set({ role: "admin" })
.where(eq(userTable.id, userId));
}
// ✅ SEGURO
export async function promoteToAdmin(userId: string) {
const session = await auth.api.getSession({
headers: await headers(),
});
if (!session?.user) {
throw new Error("Unauthorized");
}
const currentUser = await db.query.userTable.findFirst({
where: eq(userTable.id, session.user.id),
});
if (currentUser?.role !== "admin") {
throw new Error("Unauthorized - Admin only");
}
await db.update(userTable)
.set({ role: "admin" })
.where(eq(userTable.id, userId));
}
Processo de Validação
Passo 1: Identificar Server Actions
grep -r '"use server"' src/actions/
Passo 2: Verificar Cada Action
Para cada Server Action encontrada:
-
Autenticação
- Tem
auth.api.getSession()? - Tem
headers: await headers()? - Tem verificação
if (!session?.user)?
- Tem
-
Validação
- Tem schema Zod em
schema.ts? - Chama
schema.parse(data)? - Parâmetro tipado corretamente?
- Tem schema Zod em
-
Autorização
- Verifica ownership quando necessário?
- Compara
resource.userId === session.user.id? - Verifica roles se necessário?
-
Database
- Usa query builder Drizzle?
- Não tem SQL direto?
- Usa operadores corretos (eq, and, or)?
-
Cache
- Chama
revalidatePath()após mutações? - Invalida rotas corretas?
- Chama
Passo 3: Gerar Relatório
## Security Audit Report
### ✅ Server Actions Seguras
- `src/actions/add-cart-product/index.ts` - ✅ Todas verificações passaram
- `src/actions/create-checkout-session-stripe/index.ts` - ✅ Ownership validado
### ⚠️ Server Actions com Alertas
- `src/actions/update-user/index.ts`
- ⚠️ Falta validação de ownership
- ⚠️ Schema permite campos sensíveis
### 🚨 Server Actions Vulneráveis
- `src/actions/delete-order/index.ts`
- 🚨 IDOR: Não verifica ownership
- 🚨 Qualquer usuário pode deletar qualquer order
### Recomendações
1. Adicionar verificação de ownership em `delete-order`
2. Remover campo `role` do schema em `update-user`
3. Implementar rate limiting em operações de pagamento
Arquivos de Referência
- Autenticação:
src/actions/add-cart-product/index.ts - Ownership:
src/actions/create-checkout-session-stripe/index.ts - Transaction:
src/actions/finish-purchase/index.ts - Diretrizes:
docs/development-guidelines.md(linhas 85-106)
Ferramentas de Auditoria
# Buscar Server Actions sem autenticação
grep -L "auth.api.getSession" src/actions/*/index.ts
# Buscar Server Actions sem validação
grep -L "schema.parse" src/actions/*/index.ts
# Buscar SQL direto (potencialmente inseguro)
grep -r "db.execute" src/
# Buscar concatenação de SQL
grep -r '\${.*}' src/actions/
Checklist Final
Antes de aprovar código:
- Todas Server Actions têm "use server"
- Todas verificam autenticação
- Todas validam com schema Zod
- Ownership verificado quando necessário
- Nenhuma SQL injection possível
- Mensagens de erro não vazam informações
- Environment variables validadas
- Cache invalidado após mutações
- Nenhum IDOR identificado
- Nenhum mass assignment identificado
スコア
総合スコア
リポジトリの品質指標に基づく評価
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 100以上
3ヶ月以内に更新がある
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
レビュー
レビュー機能は近日公開予定です