Appearance
Checklist de Código Seguro
Use este checklist ao criar ou modificar endpoints, especialmente ao lidar com dados do usuário, autenticação ou integrações externas.
Autenticação e autorização
- [ ] O endpoint precisa de autenticação? Aplicar
ClerkAuthGuard(padrão global) - [ ] É realmente público? Só então usar
@Public()— revisar se faz sentido - [ ] Qual permissão é necessária? Adicionar
@Permissions('app:modulo') - [ ] A ação modifica dados? Verificar se read-only roles devem ser bloqueadas
- [ ] Envolve escalonamento de privilégios? Validar level do usuário vs. alvo
Isolamento de tenant
- [ ] Toda query tem
organizationIdno where? - [ ] Nunca confiar em
organizationIddo body — sempre usar doTenantContext - [ ] Recursos de outras organizações retornam 404 (não 403) para não revelar existência
Dados sensíveis
- [ ] O endpoint recebe CPF? Nunca armazenar em texto puro — usar
hashCpf() - [ ] O endpoint recebe senha? Usar
EncryptionService.encrypt() - [ ] O endpoint recebe token de API externa? Usar
EncryptionService.encrypt() - [ ] A resposta inclui campos sensíveis? Mascarar antes de retornar
- [ ] Está logando dados de usuário? Garantir que não tem PII (CPF, email, token)
Validação de entrada
- [ ] Inputs do usuário têm validação com class-validator?
- [ ] Strings têm
@MaxLength()? - [ ] Números têm
@Min()e@Max()? - [ ] Arrays têm
@ArrayMaxSize()? - [ ] Enums têm
@IsIn()?
Webhooks externos
- [ ] Validar assinatura HMAC? (Meta usa
x-hub-signature-256, Clerk usa Svix) - [ ] Usar
crypto.timingSafeEqual()para comparação de hashes - [ ] Retornar 200 imediatamente, processar assincronamente (evitar timeout)
- [ ] Deduplicar por ID do evento (Redis com TTL)
Operações destrutivas
- [ ] Deletar um recurso — verificar se pertence à organização do usuário?
- [ ] Operações em bulk — limitar quantidade máxima?
- [ ] Há rollback se falhar no meio?
Endpoints de arquivo/upload
- [ ] Validar MIME type além da extensão
- [ ] Limitar tamanho (ex: 5MB para imagens)
- [ ] Não executar arquivos recebidos
- [ ] Armazenar em MinIO, não no sistema de arquivos do container
Rate limiting
- [ ] Endpoint pode ser abusado? Adicionar
@Throttle() - [ ] Endpoint público sensível? Considerar rate limit mais agressivo
Auditoria
- [ ] A ação é sensível? Adicionar regra no
AuditInterceptor - [ ] Incluir metadados úteis (IDs relevantes, mas sem PII)
Comunicação com serviços externos
- [ ] Timeout configurado? (HTTP timeouts no HttpModule)
- [ ] Retry logic? (BullMQ para operações assíncronas,
retry.util.tspara síncronas) - [ ] O que acontece se o serviço externo ficar indisponível?
Exemplos de código seguro
Query com isolamento de tenant
typescript
// ✅ Correto
const campaign = await this.prisma.campaign.findFirst({
where: {
id: campaignId,
organizationId: TenantContext.organizationId, // sempre incluir
},
});
// ❌ Errado
const campaign = await this.prisma.campaign.findUnique({
where: { id: campaignId }, // sem tenant isolation!
});Comparação de tokens
typescript
// ✅ Correto — timing-safe
const a = Buffer.from(receivedToken);
const b = Buffer.from(expectedToken);
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
throw new ForbiddenException();
}
// ❌ Errado — vulnerável a timing attack
if (receivedToken !== expectedToken) {
throw new ForbiddenException();
}Retorno de dados sensíveis
typescript
// ✅ Correto — mascara token na resposta
return {
...instance,
token: '********',
accessToken: '********',
};
// ❌ Errado — expõe token
return instance;