Pular para o conteúdo

Guia

Checklist prático para revisar código gerado por IA antes do merge

Dez pontos a verificar no código que um assistente escreveu, de dependências inventadas e auth só no navegador a erros engolidos e testes que não testam nada.

· 9 min de leitura · Pela equipe do vetkit

Um robô apresenta orgulhoso uma janela de código enquanto um desenvolvedor marca itens em um grande checklist preso a uma prancheta.Um robô apresenta orgulhoso uma janela de código enquanto um desenvolvedor marca itens em um grande checklist preso a uma prancheta.

O código de um assistente geralmente compila, é fácil de ler e faz mais ou menos o que você pediu. É exatamente por isso que é fácil aprová-lo sem olhar direito. Os erros raramente são de sintaxe. São decisões que o modelo tomou em silêncio: uma dependência que ele supôs existir, uma verificação de permissão colocada no lugar errado, um erro que ele decidiu ignorar.

Este é o checklist que usamos ao revisar mudanças assistidas por IA. Não se trata de saber se o código é elegante. Trata-se do punhado de problemas que viram incidentes.

Por que código de IA exige outra revisão

Quando um colega escreve código, normalmente você pode confiar nas partes que ele não mencionou. Ele conhecia a base de código, testou a mudança localmente e teria avisado se algo estivesse inacabado. Um modelo não tem nada desse contexto, a menos que você o tenha fornecido, e tende a preencher lacunas com algo plausível em vez de fazer uma pergunta. Então a revisão deixa de ser “isto está bem escrito?” e passa a ser “o que ele presumiu?”.

1. Dependências

Verifique cada pacote que a mudança adiciona. Ele existe no registry? É o pacote que a documentação oficial recomenda ou um de nome parecido? Foi publicado na semana passada? Às vezes os modelos inventam nomes de pacotes, e alguns desses nomes acabam registrados por atacantes. Nosso guia sobre slopsquatting mostra como verificar um pacote em um minuto.

Olhe também as versões. Uma versão fixada em algo que nunca foi lançado vai quebrar a próxima instalação do zero, e uma dependência sem versão fixada significa que o código que você revisou não é o código que roda.

2. Segredos e variáveis públicas

Procure no diff por chaves, tokens e senhas, inclusive em arquivos de teste e exemplos. Depois olhe as variáveis de ambiente com prefixos públicos como VITE_, NEXT_PUBLIC_ ou EXPO_PUBLIC_. Tudo o que tem esses prefixos é copiado para o bundle do navegador no momento do build. Um assistente pode renomear um segredo para um desses prefixos só para fazer um recurso funcionar no navegador. Este guia detalha a versão desse erro com uma chave de API de LLM.

3. Onde o acesso é verificado

Encontre a linha que decide se o usuário atual pode fazer isto. Se ela estiver em um componente React, em um route guard ou em um botão escondido, é um recurso de experiência do usuário, não de segurança. Qualquer pessoa pode chamar a sua API diretamente. A verificação precisa acontecer no servidor, em toda requisição, contra o recurso acessado: não só “este usuário está logado?”, mas “este usuário é dono desta fatura?”.

Fique atento a novas rotas de API sem nenhum middleware de autenticação e a backends como Supabase ou Firebase, em que o banco de dados é acessível pelo navegador. Ali, a proteção são as regras do próprio banco. Uma tabela do Supabase sem row level security fica aberta para qualquer pessoa que tenha a sua chave anon pública.

4. Entrada não confiável

Siga a entrada do usuário desde onde ela entra até onde é usada. SQL montado com strings, comandos de shell montados a partir de campos da requisição, caminhos de arquivo vindos de query parameters, HTML renderizado a partir de conteúdo do usuário e URLs buscadas em nome de um usuário são os padrões clássicos de injection, path traversal, XSS e SSRF. Os assistentes ainda os produzem, principalmente quando o código ao redor já faz algo parecido.

text
// Looks harmless, is an injectiondb.query(`SELECT * FROM users WHERE email = '${req.query.email}'`); // Parameterizeddb.query('SELECT * FROM users WHERE email = $1', [req.query.email]);

5. Código inacabado

Gerações longas às vezes param antes da hora. Procure placeholders na mudança: TODO, “implementar depois”, “resto do código aqui”, funções que retornam dados de exemplo hard-coded, credenciais de mentira e arquivos com nomes como utils copy.ts. Procure também supressões genéricas como // @ts-ignore, eslint-disable no topo de um arquivo ou any usado para calar o type checker. Elas costumam esconder exatamente o problema que o modelo não conseguiu resolver.

6. Tratamento de erros

Um bloco catch que registra o erro e segue em frente, ou que captura tudo e retorna um resultado vazio, transforma uma falha barulhenta em perda silenciosa de dados. Pergunte o que deveria acontecer quando essa chamada falhar e se o código faz isso. Desconfie de retries sem limite e de erros convertidos em respostas de sucesso.

7. Testes

Vale a pena ler os testes gerados linha por linha. Problemas comuns: testes que nunca fazem nenhuma asserção, testes que mockam justamente a função que estão testando, asserções que só verificam se uma função foi chamada e .only ou .skip esquecidos. Uma suíte de testes que passa não é evidência até você saber que ela consegue falhar. Quebre o código de propósito uma vez e veja se algum teste percebe.

8. Timeouts e chamadas bloqueantes

Chamadas HTTP de saída sem timeout são um incidente de produção esperando por uma dependência lenta. Muitos clientes populares esperam indefinidamente por padrão, incluindo o requests do Python e o http.DefaultClient do Go. No Node.js, operações síncronas de arquivo ou hashing de senha dentro de um handler de requisição bloqueiam todas as outras requisições daquele processo. Isso raramente aparece em testes locais, porque o seu notebook e as suas dependências estão rápidos e ociosos.

9. Acesso a dados

Procure queries dentro de loops (o problema N+1), endpoints de listagem que retornam todas as linhas sem paginação e clientes de banco de dados criados a cada requisição em vez de compartilhados. Verifique as migrations com cuidado também: um sync de schema que roda na inicialização, ou uma migration que apaga e recria uma tabela, pode apagar dados de produção no próximo deploy.

10. Recursos com LLM

Se a mudança chama um modelo, trate a saída do modelo como entrada não confiável. Ela nunca deve ser executada, concatenada em SQL nem renderizada como HTML bruto. Verifique se a chave de API fica no servidor, se a rota que chama o modelo exige um usuário logado e tem rate limit, e se o tamanho da saída é limitado. Se um agente tem ferramentas, verifique o que essas ferramentas podem fazer e se textos de documentos ou páginas web conseguem direcioná-las.

Transforme isso em rotina

Dez verificações por pull request é muita coisa para fazer à mão, e o objetivo de um checklist é parar de depender da memória. A maioria desses itens pode ser automatizada, pelo menos em parte. Isso deixa a sua atenção para o que as ferramentas fazem mal, como saber se a mudança faz o que o produto realmente precisa.

O vetkit agrupa essas verificações em suítes: segurança (segredos, dependências vulneráveis, vulnerabilidades no código, configuração de infraestrutura), qualidade de código, sustentabilidade, riscos de código de IA (pacotes inventados, código inacabado, armadilhas de plataformas de apps, segurança de LLMs e agentes) e prontidão para produção (timeouts, chamadas bloqueantes, uso do banco de dados, fundamentos operacionais). Ele lê o seu código sem instalar, compilar ou executar nada e reporta o que encontra com o arquivo, a linha e uma sugestão de correção. Como qualquer análise estática, ele pode deixar coisas passarem e pode errar, então apoia uma revisão em vez de substituí-la.

Solicite acesso antecipado

Conte um pouco sobre sua equipe e os repositórios que você quer auditar. Recebemos equipes em pequenos grupos e responderemos por e-mail.

Usamos esses dados apenas para responder à sua solicitação. Política de privacidade