Neste artigo
Autenticação o agente costuma acertar, porque é padrão e tem biblioteca boa. Autorização ele erra, e erra de um jeito que passa em todos os testes: o endpoint confirma que você está logado e esquece de confirmar que aquele recurso é seu. Num SaaS multi-tenant isso significa um cliente lendo o dado do outro.
Que diferença resolve metade dos bugs?
Autenticação responde “quem é você”. Autorização responde “você pode fazer isso com este recurso”. São duas perguntas, e o agente responde a primeira com competência e trata a segunda como detalhe.
O código problemático parece perfeitamente saudável:
// Parece certo. Tem guard, tem usuario logado.
@Get('projects/:id')
async findOne(@Param('id') id: string) {
return this.projects.findById(id); // <- e aqui que vaza
}
O guard garantiu que existe um usuário. Ninguém garantiu que o projeto id pertence ao tenant desse usuário. Trocar o número na URL devolve o projeto de outro cliente, com status 200 e sem nenhum erro no log.
Isso é IDOR, e é a falha mais comum que eu encontro em código gerado por agente.
Por que os testes não pegam esse erro?
Porque teste gerado por agente segue o caminho feliz: cria um usuário, cria um projeto desse usuário, busca o projeto, confirma que voltou. Tudo verde.
O teste que pega é o negativo, e ele quase nunca é escrito espontaneamente: cria dois tenants, cria um recurso no tenant A, autentica como tenant B, busca o recurso do A, e exige 404.
Repare no detalhe: 404, não 403. Responder 403 confirma que o recurso existe, o que já é vazamento de informação.
Onde colocar a checagem para ela não ser esquecida?
Se o filtro por tenant depende de alguém lembrar em cada endpoint, uma hora alguém esquece. As três camadas, da mais fraca pra mais forte:
| Camada | Como funciona | Ponto fraco |
|---|---|---|
| No controller | Cada rota checa | Depende de disciplina, esquece fácil |
| No service ou repositório | Um ponto central injeta o tenant em toda query | Query crua escapa |
| Row Level Security no Postgres | O banco recusa a linha | Exige configurar sessão por request |
Eu uso a segunda como padrão e a terceira quando o dado é sensível. A combinação transforma um esquecimento em erro de banco, em vez de vazamento silencioso, e essa diferença é o que interessa quando o código é escrito rápido.
O que ler linha por linha, sempre?
Não reviso tudo que o agente escreve. Reviso estes, sem exceção:
- Todo endpoint que recebe um id na URL ou no corpo
- Toda query crua, porque ela escapa do repositório central
- Toda rota administrativa, porque "é só admin" é a desculpa mais comum pra pular a checagem
- Todo lugar que monta filtro dinâmico a partir de parâmetro do cliente
- Convite, troca de senha e transferência de posse, que mexem em quem pode o quê
Como um convite passou pela minha revisão?
Num projeto eu escrevi no PRD apenas que o admin convida pessoas pro workspace. O agente implementou convite por e-mail com link, que é razoável. Só que o link não expirava, aceitava múltiplos usos, e não checava se quem aceitou era o e-mail convidado.
Ou seja: qualquer pessoa com o link entrava no workspace, para sempre. Não apareceu em teste nenhum, porque o teste convidava e aceitava, e funcionava. Apareceu quando eu fui ler o arquivo por outro motivo.
A lição não foi "o agente é ruim", foi que eu não escrevi a restrição. Hoje a linha está no template do PRD: convite expira, é de uso único, e só é aceito pelo destinatário. Formato completo em como escrever um PRD para Claude Code e Codex.
Como pedir isso para o agente?
Implemente o CRUD de projects.
Autorizacao (obrigatorio):
- toda leitura e escrita filtra por tenant_id da sessao
- recurso de outro tenant responde 404, nunca 403
- rota de admin NAO ignora o filtro de tenant
- nenhuma query crua sem o filtro
Testes obrigatorios, alem do caminho feliz:
- tenant B tentando ler recurso do tenant A -> 404
- tenant B tentando editar recurso do tenant A -> 404
- usuario sem papel tentando rota de admin -> 403
Quando o teste negativo está no pedido, ele vem junto, e o próprio agente costuma corrigir o código pra fazer o teste passar.
Perguntas frequentes
RLS resolve sozinho?
Resolve o vazamento por query esquecida, que é o pior. Não resolve regra de papel, do tipo "membro só edita a tarefa dele", que continua sendo lógica de aplicação.
Dá pra confiar quando o agente diz que revisou a segurança?
Não como prova. Ele revisa contra o que você descreveu, e o buraco costuma estar no que ninguém descreveu. O teste negativo executado vale mais que qualquer confirmação em texto.
E se dois agentes concordarem que está seguro?
Continua não sendo prova. Eles compartilham viés de treino e concordam errado com frequência. Escrevi sobre isso em Claude Code e Codex juntos. O checklist antes de subir está em checklist de segurança.


