Vibe coding e segurança 2.0: a auditoria evoluiu para um prompt unificado de 16 itens, pronto para copiar e rodar em qualquer app feito com IA

No mês passado eu publiquei aqui o artigo Vibe coding e segurança: os erros mais comuns em software criado com IA, com um checklist de 8 frentes e um prompt para rodar a auditoria com o próprio agente de IA que tem acesso ao código. A repercussão confirmou o que eu já sentia: a pergunta "é seguro desenvolver com IA?" não é curiosidade, é urgência. Gente colocando SaaS no ar em uma semana, MVP saindo de gerador de app direto para cliente pagante, ferramenta interna virando sistema crítico sem ninguém perceber.
Desde então, aquele prompt rodou. Não porque algum cliente pediu, e sim porque essa sempre foi uma prática nossa: rodei nas ferramentas internas da Forte e em projetos de amigos que me procuraram. E o que mais me fez mergulhar nesse tema foi um movimento que merece o crédito: os alertas vieram do nosso próprio time, que nas análises proativas do dia a dia identificou padrões preocupantes em código gerado por IA e trouxe cada achado para buscarmos a solução juntos. Rodar auditoria em campo ensina coisas que nenhum checklist teórico ensina. Vimos o agente marcar item como "resolvido" sem ter aberto o arquivo. Vimos chave de API que tinha sido "apagada" do repositório, mas continuava viva no histórico do Git. Vimos projeto saído de gerador com o banco inteiro aberto para qualquer pessoa com a chave pública. E vimos relatório de auditoria que ninguém conseguia usar como evidência, porque não registrava o antes e o depois.
Esta versão 2.0 nasce dessa experiência. Não é um retoque, é uma reconstrução: o checklist de 8 frentes virou um prompt unificado de 16 itens, com regras de conduta para o agente, fase de reconhecimento do projeto, critérios objetivos de reprovação, correção assistida item por item e um relatório final de antes e depois que serve como evidência documental da auditoria.
Como eu sempre digo: não existe 100% de segurança e não existe risco zero. Mas existe know-how. E know-how bom é know-how que aprende com a prática. Este artigo mostra o que mudou, o que entrou de novo, por que cada mudança importa, e entrega o prompt completo para você copiar e rodar hoje.
Do checklist ao contrato: a mudança mais importante não é técnica
A primeira versão pedia para a IA auditar. A segunda versão estabelece um contrato com a IA antes de deixá-la auditar. Essa é a diferença conceitual central, e ela nasceu de um problema real: agentes de IA são otimistas e obedientes demais. Se você pede um relatório, eles entregam um relatório bonito. Bonito não é sinônimo de verdadeiro.
O prompt 2.0 abre com seis regras inegociáveis, que valem para toda a auditoria:
1. Evidência ou nada. O agente não pode marcar um item como "passou" sem citar o arquivo e a linha que provam isso. Suposição não conta como evidência. Na versão 1 essa ideia existia em uma linha; na versão 2 ela vira a regra número um do contrato, porque foi o comportamento que eu mais vi falhar em campo.
2. O agente não altera nada sozinho. Ele audita, aponta e entrega o conserto pronto. Só encosta em arquivo depois que você aprovar, um item por vez. Auditoria e correção são momentos diferentes, e misturar os dois é receita para o agente "consertar" algo que você não revisou.
3. Segredo nunca aparece no relatório. Se o agente encontrar uma chave exposta, ele cita apenas o arquivo e a linha onde ela está, e mascara o valor: sk-live-****1234. Parece detalhe, mas não é: um relatório de auditoria que transcreve a chave vazada vira, ele mesmo, um segundo vazamento.
4. Existe um terceiro veredito: NÃO VERIFICADO. Essa é uma das adições mais importantes da versão 2.0. "Não encontrei o problema" não é a mesma coisa que "o problema não existe". Quando o agente não consegue verificar algo, como um backup que vive no painel do provedor ou uma configuração que não está no código, ele marca NÃO VERIFICADO e diz exatamente o que precisa de você para fechar o item. E a regra é explícita: NÃO VERIFICADO não conta como aprovado em nenhuma hipótese.
5. Só leitura. Nada de comando destrutivo, nada que envie dados para fora, nada que precise de credencial de produção.
6. Linguagem para quem não é programador de carreira. Porque boa parte de quem desenvolve por vibe coding não é, e o relatório precisa ser útil para quem vai tomar a decisão de negócio.
A regra anti-falso-positivo, que também é anti-falso-negativo
Essa merece seção própria, porque resolve o defeito mais perigoso de auditoria feita por IA: o resultado limpo demais.
O prompt 2.0 traz uma instrução direta: se a auditoria terminar com 0 ou 1 item reprovado, o agente é obrigado a parar e refazer os itens mais sensíveis com rigor extra, listando arquivo e linha de cada aprovação. A lógica é estatística e vem da prática: aplicação feita com IA quase nunca passa limpa. Resultado impecável geralmente significa que o agente não olhou fundo o suficiente, não que o app está seguro.
E o inverso também está coberto: o agente é instruído a não inventar falha para parecer útil. Se passou de verdade e existe evidência, marca como aprovado e cita a prova. Auditoria que infla achado é tão inútil quanto auditoria que esconde.
Essa desconfiança estruturada não é implicância minha, tem respaldo em pesquisa recente. O instituto de pesquisa METR conduziu um experimento controlado randomizado com desenvolvedores experientes trabalhando em repositórios de código aberto que eles mesmos mantinham, e encontrou um dado desconfortável: com as ferramentas de IA do início de 2025, as tarefas levaram cerca de 19% mais tempo, enquanto os participantes estimavam ter sido cerca de 20% mais rápidos, mesmo depois de terminar. Percepção e realidade apontando para lados opostos. Na continuação do estudo, publicada em fevereiro de 2026, o quadro de velocidade mudou: com os agentes do fim de 2025, os dados passaram a sugerir que a IA de fato acelera os desenvolvedores. A lição central, porém, permaneceu, e o próprio METR reafirma que a autoavaliação de quem usa IA é um termômetro não confiável. É exatamente o fenômeno que esta auditoria ataca: a sensação de "está tudo certo" não é evidência de nada. Por isso o prompt exige arquivo e linha até para aprovar.
Fase 0: a auditoria agora começa conhecendo o projeto
A versão 1 pedia para você preencher três linhas de contexto: stack, escopo e tipo de aplicação. A versão 2 inverte a responsabilidade: o agente descobre sozinho, lendo os arquivos do projeto. Framework, banco, provedor de deploy, mecanismo de autenticação, existência de migrations.
E dois pontos dessa fase mudam o peso da auditoria inteira:
Detecção de origem em gerador. O prompt manda o agente verificar se o projeto nasceu em plataformas como Lovable, v0, Bolt, Replit ou Base44. Se veio de gerador, a atenção redobra nos itens onde essas ferramentas mais deixam furo: segredos no lado do cliente, Row-Level Security ausente e autorização apenas visual. Não é implicância com as ferramentas, é padrão observado: geradores otimizam para o app funcionar na demonstração, não para resistir a ataque.
Detecção de multi-tenant. O agente procura colunas como tenant_id, org_id, company_id e workspace_id no schema. Se encontrar, o item de isolamento entre clientes deixa de ser opcional e vira obrigatório e crítico. Na versão 1, essa decisão dependia de você declarar o tipo de aplicação; agora o próprio código denuncia.
O que entrou de novo: os furos que a versão 1 não cobria
A estrutura de 8 frentes da versão 1 continua viva dentro dos 16 itens: autorização no servidor, segredos, injeção, rate limiting, supply chain, configuração de produção e isolamento multi-tenant estão todos lá, agora com comandos de busca prontos e critérios objetivos de reprovação. E o item de supply chain segue cobrindo o slopsquatting, os pacotes alucinados pela IA que atacantes registram com código malicioso.
Sobre o slopsquatting, aliás, o que era anedota virou número: pesquisadores da University of Texas at San Antonio analisaram 576 mil amostras de código geradas por 16 modelos de linguagem e encontraram mais de 200 mil nomes únicos de pacotes que simplesmente não existem, com taxa de alucinação na casa de 20% nos modelos de código aberto e em torno de 5% nos modelos comerciais, segundo o estudo de Spracklen e colegas apresentado no USENIX Security Symposium de 2025. Cada um desses nomes é um endereço que um atacante pode registrar antes de você instalar.
Mas a experiência de campo trouxe furos novos para o radar, e são eles que justificam a atualização:
Row-Level Security: o furo número um dos apps de gerador
A versão 1 falava de autorização no backend em termos gerais. A versão 2 dedica um item inteiro ao RLS, porque a realidade do vibe coding mudou o cenário: a maioria esmagadora desses projetos usa Supabase ou Firebase, onde o navegador conversa direto com o banco usando uma chave pública. Nesse modelo, a única coisa entre um estranho e a sua tabela de usuários são as policies de Row-Level Security. Sem RLS, qualquer pessoa com a chave pública, que está no código do navegador e é visível para todos, lê e escreve em qualquer tabela.
O prompt manda o agente listar todas as tabelas do schema e dizer, uma por uma, se tem RLS ativo e se tem policy. E cobre até o caso traiçoeiro da tabela com RLS ativo e zero policies. O conserto exigido não é genérico: é o SQL exato, tabela por tabela, com policy adaptada à realidade de cada uma.
Chave pública legítima não é chave vazada, e confundir as duas custa caro
Uma sutileza que gerava falso positivo na prática: a anon key do Supabase, a publishable key do Stripe e a apiKey do Firebase podem ir para o navegador, esse é o design delas. O que nunca pode ir é service_role, secret key, connection string com senha, chave da OpenAI ou da Anthropic. O prompt 2.0 ensina o agente a distinguir as duas, e adiciona a conexão que importa: anon key exposta não é falha em si, mas exige verificar o RLS com rigor extra, porque anon key sem RLS é acesso total ao banco por qualquer pessoa.
E quando um segredo de verdade vazou, o conserto tem uma etapa que quase todo mundo pula: rotacionar a chave. Mover a variável para o servidor não desfaz a exposição. A chave já foi vista; ela está queimada.
O histórico do Git não perdoa
A versão 1 checava se havia .env versionado no repositório. A versão 2 vai onde o problema realmente mora: o histórico. Se o arquivo de segredos esteve no Git um dia, a chave está comprometida mesmo que você tenha apagado o arquivo depois, porque o Git guarda tudo. O prompt roda a verificação nos commits antigos (git log --all --full-history), aciona ferramentas de varredura como gitleaks quando disponíveis, e é explícito sobre a ordem do conserto: primeiro rotaciona as chaves, depois arruma o .gitignore, depois tira do rastreamento. Apagar o arquivo não desqueima a chave.
Mass assignment: o usuário que se promove a admin
Novidade absoluta em relação à versão 1, e um clássico de código gerado por IA: a rota que faz update ou create com o corpo inteiro da requisição, sem allowlist de campos. O padrão update(req.body) parece inofensivo e funciona perfeitamente na demonstração. Até alguém enviar no payload role=admin, is_paid=true ou tenant_id de outro cliente, e o sistema aceitar. É escalada de privilégio servida de bandeja, e a IA gera esse padrão porque ele é o caminho mais curto para "funcionar".
Open redirect: o phishing que usa o seu domínio
Outro item novo: o parâmetro de redirecionamento pós-login, como ?redirect=, ?next= ou ?returnUrl=, usado sem allowlist de destinos. O ataque é elegante e cruel: o criminoso monta um link legítimo do seu domínio que, depois do login verdadeiro, joga a vítima em um site de phishing. A vítima confiou porque o link era seu. O conserto é uma allowlist de destinos permitidos, e o prompt exige o código pronto no padrão do seu framework.
CSRF: o furo que voltou com os cookies
Se a autenticação usa cookie de sessão, precisa existir proteção contra requisição forjada de outro site: token anti-CSRF, SameSite no cookie ou verificação de origem. Sem nenhum dos três, qualquer site aberto na outra aba consegue executar ações em nome do usuário logado. O prompt também sabe quando o item não se aplica: quem autentica apenas por token em header normalmente está fora desse risco, e marcar N/A com critério é tão importante quanto reprovar com critério.
As rotas que você criou e esqueceu
/api/seed, /api/reset, /api/debug, painel de migrations, GraphQL playground. Rotas de manutenção que nasceram para popular o banco no início do projeto e ficaram no ar, sem autenticação, em produção. A versão 2 caça essas rotas por nome e exige, para cada uma, a resposta: exige login? Está acessível em produção? E no mesmo item, os webhooks recebidos: sem validação de assinatura, um webhook aceita evento forjado e replay. Qualquer pessoa que descubra a URL consegue fingir que é o seu provedor de pagamento.
Log que ninguém lê e log que vaza
A versão 1 pedia logging estruturado. A versão 2 faz as duas perguntas que importam de verdade. Primeira: alguém seria avisado? Registro de tentativa frustrada de login, erro 403, pico de requisição, tudo isso é inútil se nenhum alerta chega a um ser humano. Log que ninguém lê não é monitoramento, é diário íntimo. Segunda: o log é ele mesmo um vazamento? Senha, token, header de autorização e CPF gravados em texto claro transformam o sistema de observabilidade no ponto mais fácil de exfiltrar dado.
Backup que nunca foi restaurado é uma esperança
Item expandido com a frase que resume dez anos de resposta a incidente: backup que nunca foi testado em restauração não é backup, é esperança. E junto dele, a segregação de ambientes: segredo de produção reaproveitado em dev e staging significa que o ambiente menos protegido carrega a chave do mais valioso. Comprometer o staging vira comprometer a produção. O prompt compara os arquivos de ambiente entre si procurando exatamente essa repetição.
Qualidade que vira falha de segurança
O item 16 formaliza uma tese que eu defendo há tempos: alguns problemas de "qualidade" são vulnerabilidade com outro nome. Falha de lógica em billing ou permissão é explorável: desconto infinito, upgrade sem pagar, permissão que o próprio usuário se concede. N+1 queries em rota exposta é negação de serviço autoinfligida: o atacante nem precisa de botnet, a sua própria listagem derruba o banco sob volume. E ausência de teste automatizado nos fluxos de autenticação e pagamento significa que o conserto de hoje pode reabrir o furo de ontem sem ninguém perceber. O prompt manda o agente revisar a lógica de negócio crítica linha a linha, porque é exatamente aí que a IA que gerou o código mais erra de forma silenciosa.
Da auditoria à evidência: as quatro fases
A segunda grande evolução estrutural: a versão 2.0 não termina no relatório de problemas. Ela conduz o ciclo completo, em quatro fases.
Fase 1, a auditoria. Os 16 itens, cada um com o que procurar, os comandos prontos de busca, o critério objetivo de reprovação e a gravidade. Para cada falha, o agente entrega quatro blocos: onde está, com arquivo e linha; o que dá para fazer com isso, o ataque concreto explicado como se fosse para o dono do negócio; o conserto, com o código pronto para colar no seu stack, e não um exemplo genérico; e como você confirma que fechou.
Fase 2, o relatório. Tabela de vereditos, achados ordenados do mais crítico ao menos, uma seção de achados extras, reservada à vulnerabilidade grave que não estava na lista, porque furo não respeita checklist, e a lista curta do que bloqueia o deploy. Termina com um placar: X de 16 reprovados, Y críticos, Z não verificados.
Fase 3, o conserto assistido. Só começa se você autorizar. Um item por vez, começando pelos críticos, com o diff mostrado antes de cada mudança e a sua aprovação explícita. Duas regras de segurança operacional que aprendi do jeito difícil: nunca mexer em autenticação e banco na mesma leva, e chave exposta se conserta em duas etapas: primeiro a rotação no painel do provedor, depois a mudança no código.
Fase 4, o relatório de antes e depois. Ao final, o agente gera um documento de fechamento: placar de abertura contra placar de fechamento, tabela comparativa item a item, detalhe de cada correção com o teste de verificação que passou, pendências e riscos aceitos, e uma recomendação final direta sobre se o app está apto a ir ou permanecer em produção. Esse documento é o que transforma a auditoria em evidência: algo que você arquiva, mostra para um cliente, apresenta em uma due diligence ou usa como linha de base da próxima rodada.
Por que rodar em todo software feito com IA?
Aqui vai o argumento que eu considero o mais importante do artigo. E já adianto a resposta curta: em todo, sem exceção.
A tentação é reservar auditoria para "projeto sério". O site institucional, a ferramenta interna, o MVP de validação ficam de fora, porque "não tem nada de valor ali". Esse raciocínio tem três defeitos, e cada um deles eu já vi virar incidente:
Primeiro: o risco não está no tamanho do projeto, está no padrão de quem o gerou. A IA que escreveu o seu formulário de contato é a mesma que escreveu o SaaS do concorrente, treinada nos mesmos dados, otimizando para o mesmo objetivo: funcionar. Os furos são sistemáticos, não proporcionais à ambição do projeto. Um formulário simples pode carregar a chave de API paga no navegador, e a conta de milhares de reais chega igual, seja o site grande ou pequeno.
E "sistemático" aqui não é força de expressão, é resultado medido na semana passada. O GenAI Code Security Report 2026, da Veracode, publicado em julho de 2026, testou mais de cem modelos de linguagem e encontrou vulnerabilidade em cerca de 44% das tarefas de geração de código, com a taxa média de aprovação em segurança estagnada em 56%, contra 55% da edição anterior do relatório. O resumo do próprio estudo cabe em uma frase: sintaxe está resolvida, segurança não. Os modelos hoje geram código sintaticamente correto perto de 100% das vezes, e é exatamente essa fluência que cria a falsa confiança: código que compila, roda e parece limpo, carregando furo explorável. Nem o melhor modelo do ranking escapa, ele ainda falha em cerca de uma a cada três tarefas de segurança. A régua não pergunta se o projeto é um SaaS ou uma landing page, ela vem embutida na forma como os modelos aprenderam a programar.
Segundo: software feito com IA muda de função sem cerimônia. A ferramenta interna "só para o time" ganha um login para o cliente testar. O MVP de validação começa a receber dado real porque os primeiros usuários chegaram antes do refactor. O script de fim de semana vira dependência da operação. Nenhum desses momentos vem com um alerta dizendo "agora vale auditar". Quando você percebe, o código de protótipo está em produção há meses. Auditar tudo desde o início elimina a dependência desse julgamento que ninguém faz na hora certa.
Terceiro: o custo da auditoria desabou, e isso muda a conta. Auditoria de segurança já foi sinônimo de consultoria de semanas. Este prompt roda em minutos, no mesmo agente que escreveu o código, sem custo adicional de ferramenta. Quando verificar custa uma fração mínima de construir, não verificar deixa de ser economia e vira negligência. A pergunta certa não é "este projeto merece auditoria?", é "por que este projeto seria exceção?".
E tem o efeito colateral que eu mais gosto: rodar a auditoria em projeto pequeno é treino. Você aprende a ler o relatório, entende os padrões de furo da sua stack, e chega ao projeto grande sabendo exatamente o que olhar. Segurança é hábito antes de ser técnica.
O prompt 2.0 completo
Copie o bloco inteiro abaixo e cole na sessão do agente de IA que enxerga os arquivos do seu projeto, como Claude Code, Cursor ou equivalente, aberto na pasta do projeto. Não precisa preencher nada: a Fase 0 descobre o contexto sozinha. E uma regra de ouro antes de rodar: não cole nenhuma chave, senha ou token no chat. O prompt encontra os segredos sozinho e é instruído a nunca escrever o valor de um segredo no relatório.
Você é um auditor de segurança sênior. Sua tarefa é auditar ESTE projeto contra os 16 furos de segurança mais comuns em aplicações feitas com IA, e me entregar um relatório com o conserto exato de cada falha.
CONTRATO DA AUDITORIA, siga à risca
1. LEIA OS ARQUIVOS DE VERDADE. Nunca marque um item como PASSOU sem citar o arquivo e a linha que provam isso. Suposição não conta como evidência.
2. NÃO ALTERE NADA nesta rodada. Apenas audite e proponha. Você só encosta em arquivo depois que eu aprovar, no final.
3. NUNCA escreva no relatório o valor de uma chave, token, senha ou connection string. Cite apenas onde está (arquivo e linha). Se precisar mostrar para eu identificar, mascare: sk-live-****1234.
4. NÃO CHUTE. Se não conseguir verificar algo, marque NÃO VERIFICADO e diga exatamente qual arquivo, acesso ou comando você precisa de mim.
5. NÃO RODE comandos destrutivos, comandos que enviem dados para fora, nem comandos que precisem de credencial de produção. Só leitura local, git e auditoria de dependências (npm audit, pip-audit).
6. Escreva para alguém que NÃO é programador de carreira. Sem jargão sem tradução.
FASE 0 RECONHECIMENTO
Antes de auditar, descubra e me diga em até 8 linhas:
- Framework e versão (package.json, requirements.txt, go.mod, composer.json)
- Banco e provedor (Supabase, Firebase, Postgres, Mongo, Prisma, Drizzle...)
- Onde roda (vercel.json, netlify.toml, wrangler.toml, Dockerfile, render.yaml)
- Como faz autenticação (Supabase Auth, Clerk, NextAuth, Firebase Auth, feita à mão, nenhuma)
- Se existe pasta de migrations ou SQL no repositório
- Se o projeto veio de um gerador (Lovable, v0, Bolt, Replit, Base44)
- Se a aplicação é MULTI-TENANT (dados de clientes/organizações diferentes na mesma base). Procure colunas tenant_id, org_id, company_id, workspace_id no schema.
- Se existem webhooks recebidos ou funcionalidades que buscam URLs fornecidas pelo usuário
Comandos úteis:
ls -la
cat package.json
git log --oneline -5
IMPORTANTE: se veio de gerador, redobre a atenção nos itens 2, 3, 6 e 7, são os furos que esses geradores deixam com mais frequência. Se for multi-tenant, o item 14 é obrigatório e crítico.
FASE 1 OS 16 ITENS
Para cada item: procure a evidência, decida o veredito, e se falhou, escreva o conserto pronto para colar NO MEU STACK, não um exemplo genérico.
1. RATE LIMITING
O que é: impedir que alguém dispare 10 mil requisições no seu login, derrube o app ou queime seu saldo de API.
Procure:
- middleware de rate limit (upstash/ratelimit, express-rate-limit, rate-limiter-flexible)
- config do provedor (vercel.json, Cloudflare WAF, limites do Supabase)
- rotas de login, cadastro e recuperação de senha
- QUALQUER rota que chame API paga (OpenAI, Anthropic, envio de e-mail/SMS, upload)
- rotas de exportação/relatório sem limite de volume por requisição
Comando:
grep -rEi "ratelimit|rate_limit|rate-limit|throttle|slowdown" --include=*.ts --include=*.tsx --include=*.js --include=*.jsx --include=*.json --include=*.toml .
FALHOU se: não existe limite em rota de autenticação OU em rota que gasta dinheiro por chamada.
Gravidade: CRÍTICO se rota paga está exposta sem limite. ALTO se só o login está sem limite.
2. CHAVES E SEGREDOS SÓ NO SERVIDOR
O que é: chave que vai para o navegador é chave pública. Qualquer pessoa abre o inspecionar elemento e lê.
Procure:
- variáveis com prefixo público carregando segredo real: NEXT_PUBLIC_, VITE_, REACT_APP_, EXPO_PUBLIC_, PUBLIC_
- chaves escritas direto no código (hardcoded)
- chamadas a API paga feitas do lado do cliente (componente sem "use server", código dentro de useEffect, onClick)
Comandos:
grep -rEn "(NEXT_PUBLIC_|VITE_|REACT_APP_|EXPO_PUBLIC_)[A-Z_]*(KEY|SECRET|TOKEN|PASSWORD|SERVICE)" .
grep -rEn "(sk-|sk_live|service_role|AIza|xoxb-|ghp_|-----BEGIN)" --include=*.ts --include=*.tsx --include=*.js --include=*.jsx --include=*.env* .
ATENÇÃO, não confunda:
- chave PÚBLICA legítima: anon key do Supabase, publishable key do Stripe, apiKey do Firebase. Essas PODEM ir para o navegador, desde que o item 3 (RLS) e o item 6 estejam corretos.
- SEGREDO VAZADO: service_role, secret key, connection string com senha, chave da OpenAI/Anthropic. Essas NUNCA vão para o navegador.
Se encontrar anon key exposta, não marque falha aqui, mas verifique com rigor extra o item 3. Anon key sem RLS é acesso total ao banco por qualquer pessoa.
FALHOU se: qualquer segredo real chega ao navegador.
Gravidade: sempre CRÍTICO.
Conserto: mover a chamada para rota de servidor (API route / server action / edge function), tirar o prefixo público da variável, e ROTACIONAR a chave, ela já foi exposta.
3. ROW-LEVEL SECURITY (RLS) EM TODA TABELA
O que é: sem RLS, qualquer pessoa com a chave pública lê e escreve em qualquer tabela. Inclusive a tabela de usuários.
Procure:
- migrations e arquivos .sql no repo: "enable row level security", "create policy"
- pasta supabase/migrations
- no Firebase: firestore.rules e storage.rules
Comandos:
grep -rin "row level security\|create policy\|rls" --include=*.sql .
ls -la supabase/migrations 2>/dev/null
cat firestore.rules 2>/dev/null
Liste TODAS as tabelas que você encontrar no schema e diga, uma por uma, se tem RLS ativo e se tem policy. Tabela com RLS ativo e ZERO policy também é problema, ela fica inacessível ou, dependendo da config, aberta.
Se não conseguir enxergar o banco pelo código, me diga qual comando eu rodo para te mostrar (ex.: o SQL de listar tabelas sem RLS no editor do Supabase).
FALHOU se: existe pelo menos uma tabela sem RLS ativo, ou com RLS ativo e nenhuma policy.
Gravidade: CRÍTICO.
Conserto: entregue o SQL exato, tabela por tabela. Formato:
ALTER TABLE nome_da_tabela ENABLE ROW LEVEL SECURITY;
CREATE POLICY "usuario_le_o_proprio" ON nome_da_tabela FOR SELECT USING (auth.uid() = user_id);
Adapte a policy à realidade de cada tabela (dado do usuário, dado público, dado só de admin), não repita a mesma policy para todas.
4. .ENV FORA DO REPOSITÓRIO INCLUINDO O HISTÓRICO
O que é: o arquivo com suas chaves não pode estar no Git. E se ele já esteve um dia, a chave está queimada MESMO que você tenha apagado depois, o histórico do Git guarda tudo.
Comandos (rode os três):
cat .gitignore
git log --all --full-history --name-only -- "*.env*"
git ls-files | grep -i "env"
Se a ferramenta gitleaks ou trufflehog estiver disponível no ambiente, rode também uma varredura do histórico completo:
gitleaks detect --source . 2>/dev/null || echo "gitleaks não disponível, varredura manual feita acima"
FALHOU se: .env não está no .gitignore, OU aparece rastreado agora, OU aparece em qualquer commit do histórico.
Gravidade: CRÍTICO se está no histórico ou rastreado.
Conserto (seja explícito sobre a ordem, porque a maioria erra):
a) ROTACIONE todas as chaves que estavam no arquivo. Isso primeiro. Apagar o arquivo não desqueima a chave.
b) Adicione .env, .env.local e .env*.local ao .gitignore
c) Tire do rastreamento: git rm --cached .env
d) Só se for repositório PÚBLICO e você souber o que está fazendo, limpe o histórico (git filter-repo ou BFG). Avise que isso reescreve o histórico e quebra clones existentes.
5. VALIDAÇÃO DE INPUT, INJEÇÃO, SSRF E PATH TRAVERSAL
O que é: o que o usuário digita não pode virar comando no seu banco, código na sua tela, requisição para sua rede interna nem acesso a arquivo fora da pasta permitida.
Procure:
- schema de validação: zod, yup, joi, valibot, class-validator
- concatenação de string em query SQL (o clássico: "SELECT * FROM x WHERE id = " + id)
- dangerouslySetInnerHTML, innerHTML, v-html com dado do usuário
- eval, new Function, exec com dado do usuário
- upload de arquivo sem checagem de tipo real (não só extensão) e tamanho
- SSRF: funcionalidades que fazem fetch/request para URL fornecida pelo usuário (importação, preview de link, proxy) sem validar destino nem bloquear IPs internos (127.0.0.1, 169.254.x, 10.x, 192.168.x)
- path traversal: leitura/escrita de arquivo usando nome vindo do usuário sem normalizar o caminho (procure "../" chegando em fs.readFile, sendFile, open)
- deserialização insegura: pickle/yaml.load em dado do cliente, JSON.parse seguido de uso dinâmico de propriedades como código
- XSS por escape ausente: dado do usuário renderizado sem sanitização, além do dangerouslySetInnerHTML, veja template server-side que interpola sem escapar e resposta de API que o front injeta direto no DOM
- mass assignment: rota que faz update/create com o corpo inteiro da requisição (update(req.body), Model(**data)) sem allowlist de campos. Permite o usuário enviar role=admin, is_paid=true, tenant_id=outro e escalar privilégio ou trocar de dono
Comandos:
grep -rn "dangerouslySetInnerHTML\|innerHTML\|v-html\|eval(\|new Function(" --include=*.ts --include=*.tsx --include=*.js --include=*.jsx --include=*.vue .
grep -rn "zod\|yup\|joi\|valibot" package.json
grep -rEn "fetch\(|axios\.|http\.get|requests\.get" --include=*.ts --include=*.js --include=*.py . | grep -in "req\.\|body\.\|params\.\|query\."
grep -rn "readFile\|sendFile\|createReadStream\|open(" --include=*.ts --include=*.js --include=*.py . | grep -in "req\.\|body\.\|params\."
grep -rEn "update\(req\.body|create\(req\.body|\.update\(body|\(\*\*(request|req)" --include=*.ts --include=*.js --include=*.py .
FALHOU se: existe rota que recebe dado do usuário sem validar antes de usar, requisição a URL do usuário sem validação de destino, acesso a arquivo com caminho controlado pelo usuário, escape ausente em dado renderizado, ou update/create com corpo inteiro sem allowlist de campos.
Gravidade: CRÍTICO se tem SQL concatenado, eval, SSRF, path traversal explorável ou mass assignment que escala privilégio. ALTO nos demais.
Conserto: entregue o schema de validação escrito para os campos reais daquela rota, a allowlist explícita de campos que podem ser gravados, e a normalização de caminho ou destino quando for SSRF ou traversal, não um exemplo genérico.
6. NENHUMA TABELA, BUCKET OU COLEÇÃO PÚBLICA POR PADRÃO
O que é: storage aberto é o furo mais esquecido. A pessoa protege o banco e deixa a pasta de uploads com leitura pública, onde estão os documentos dos clientes.
Procure:
- policies de STORAGE (não só de tabela) no Supabase
- storage.rules no Firebase, atenção especial a "allow read, write: if true"
- buckets S3/R2 com ACL pública
- endpoints de API que retornam lista completa sem filtro de usuário
Comandos:
cat storage.rules 2>/dev/null
cat firestore.rules 2>/dev/null
grep -rin "public\|allow read\|allow write" --include=*.rules --include=*.sql .
FALHOU se: qualquer bucket ou coleção aceita leitura ou escrita anônima de dado que não deveria ser público.
Gravidade: CRÍTICO.
7. AUTENTICAÇÃO E AUTORIZAÇÃO NO SERVIDOR (INCLUINDO IDOR)
O que é: esconder o botão não é proteger. Se a checagem só existe na tela, a pessoa digita o endereço direto e entra. E estar logado não basta: o usuário A não pode acessar o recurso do usuário B trocando um ID na URL.
Faça assim:
a) Liste todas as rotas e páginas que deveriam exigir login (admin, dashboard, configurações, qualquer coisa com dado de usuário)
b) Para CADA uma, diga onde a checagem acontece: servidor ou navegador
c) Aponte todo caso em que a proteção é só visual
d) Para cada rota que recebe um ID de recurso (/api/orders/:id, /api/users/:id), verifique se o servidor confere que o recurso PERTENCE ao usuário logado, não só que existe uma sessão. Se não confere, é IDOR.
Procure:
- middleware.ts / middleware.js e o que ele realmente cobre (leia o matcher)
- checagem dentro de API routes e server actions
- padrão suspeito: "if (!user) return null" dentro de componente de tela, sem nada no servidor
- rotas de API sem nenhuma verificação de sessão
- queries por ID sem filtro de dono: findUnique({ where: { id } }) sem AND user_id
- redirect aberto: parâmetro de redirecionamento pós-login (?redirect=, ?next=, ?returnUrl=, ?callback=) usado sem allowlist de destinos. Permite montar um link do seu domínio que joga a vítima num site de phishing depois do login
Comandos:
cat middleware.ts 2>/dev/null || cat middleware.js 2>/dev/null || cat src/middleware.ts 2>/dev/null
grep -rn "getUser\|getSession\|auth()\|currentUser\|requireAuth" --include=*.ts --include=*.tsx .
grep -rEn "redirect\(|res\.redirect|Location:" --include=*.ts --include=*.js --include=*.py . | grep -in "req\.\|query\.\|params\.\|next\|returnurl\|callback"
FALHOU se: existe rota sensível cuja única proteção está no navegador, rota autenticada que entrega recurso de outro usuário por troca de ID, OU redirecionamento que aceita destino externo vindo do usuário sem allowlist.
Gravidade: CRÍTICO para proteção só no navegador e IDOR. ALTO para redirect aberto.
Conserto: entregue o código da checagem no servidor para cada rota exposta, incluindo o filtro de dono na query, e a allowlist de destinos de redirect, no padrão do meu framework.
8. ERROS SEM STACK TRACE
O que é: mensagem de erro detalhada é mapa do tesouro para quem quer te atacar, mostra caminho de arquivo, nome de tabela, versão de biblioteca.
Procure:
- handlers que devolvem a exceção crua: "catch (e) { return Response.json(e) }", "res.send(err)", "throw err" chegando ao cliente
- NODE_ENV, DEBUG, APP_DEBUG ligados em produção
- página de erro padrão do framework exposta
Comandos:
grep -rn "catch" --include=*.ts --include=*.tsx --include=*.js -A 3 . | grep -i "json(e\|send(e\|message: e\|error: e"
grep -rn "DEBUG\|NODE_ENV" .env* vercel.json netlify.toml 2>/dev/null
FALHOU se: erro de servidor devolve stack trace, nome de tabela ou caminho de arquivo para o cliente.
Gravidade: MÉDIO sozinho, ALTO se combinado com outro furo.
Conserto: erro genérico para o usuário, erro detalhado só no log.
9. ENDPOINTS DE ADMIN, DEBUG E SEED TRAVADOS, E WEBHOOKS ASSINADOS
O que é: aquela rota /api/seed que você criou para popular o banco no começo e esqueceu no ar. E webhook que aceita qualquer requisição sem verificar quem mandou.
Procure rotas com: admin, debug, test, seed, migrate, reset, cron, webhook, docs, swagger, graphql playground, /api/dev
Comandos:
find . -path ./node_modules -prune -o -type d -name "api" -print
grep -rln "seed\|migrate\|reset\|debug\|admin\|webhook\|cron" --include=*.ts --include=*.tsx app/api pages/api src/app/api 2>/dev/null
Para cada uma encontrada, diga se exige autenticação e se está acessível em produção. Para cada webhook recebido, verifique se a assinatura do remetente é validada (ex.: stripe.webhooks.constructEvent, verificação de HMAC). Webhook sem validação de assinatura aceita evento forjado e replay.
FALHOU se: existe rota administrativa, de manutenção ou webhook acessível sem autenticação/assinatura.
Gravidade: CRÍTICO.
10. LOGGING QUE DENUNCIA ATAQUE, SEM VAZAR DADO SENSÍVEL
O que é: sem registro, um ataque acontece em silêncio e você descobre pelo cliente reclamando. E o oposto também é furo: log que grava senha, token ou CPF em texto claro vira o próprio vazamento.
Verifique se existe registro de:
- tentativas de login que falharam
- erros de autorização (403)
- picos de requisição
- E principalmente: se ALGUÉM SERIA AVISADO. Log que ninguém lê não é monitoramento.
Verifique também o conteúdo dos logs:
- senha, token, cabeçalho Authorization ou dado pessoal (CPF, e-mail completo) indo para o log em texto claro
- dado sensível passando em query string de URL (GET /reset?token=...), porque query string acaba em log de servidor e histórico de navegador
Comandos:
grep -rn "console.log\|logger\." --include=*.ts --include=*.js . | grep -in "password\|token\|authorization\|senha\|cpf"
Procure: Sentry, Axiom, Logtail, Better Stack, Datadog, console.error estruturado, alertas configurados no provedor.
FALHOU se: não existe nenhum registro de tentativa de acesso indevido, ou existe registro mas nenhum alerta, OU o log grava segredo/dado pessoal em texto claro.
Gravidade: MÉDIO para ausência de alerta. ALTO se o log vaza segredo ou dado pessoal.
11. SUPPLY CHAIN: PACOTES ALUCINADOS, CVEs E VERSÕES SOLTAS
O que é: IA às vezes inventa nome de pacote que não existe, e atacantes registram esses nomes com código malicioso esperando alguém instalar (slopsquatting). Além disso, código gerado por IA costuma vir com dependência antiga carregando vulnerabilidade conhecida.
Faça assim:
a) Liste as dependências diretas e confirme que cada uma existe no registro oficial e é o pacote que parece ser (cuidado com typo: "requets" em vez de "requests", "colourama" em vez de "colorama")
b) Rode a auditoria de vulnerabilidades do gerenciador
c) Verifique se as versões estão fixadas (arquivo de lock commitado, sem "*" ou "latest" em produção)
Comandos:
npm audit 2>/dev/null || pip-audit 2>/dev/null || echo "rode manualmente: npm audit / pip-audit / composer audit"
git ls-files | grep -E "package-lock|yarn.lock|pnpm-lock|poetry.lock|Pipfile.lock"
grep -En "\"\\*\"|\"latest\"" package.json 2>/dev/null
FALHOU se: existe pacote suspeito de alucinação/typosquatting, CVE crítica ou alta sem correção, ou dependência sem versão fixada em produção.
Gravidade: CRÍTICO para pacote suspeito ou CVE crítica explorável. MÉDIO para pinning ausente.
Conserto: nome do pacote correto, comando de atualização exato, e commit do arquivo de lock.
12. CORS, CSP E HEADERS DE SEGURANÇA
O que é: CORS com "*" combinado com credenciais deixa qualquer site fazer requisição autenticada em nome do seu usuário. E a ausência de headers básicos facilita XSS e clickjacking.
Procure:
- Access-Control-Allow-Origin: * junto com Allow-Credentials: true
- config de CORS aceitando origem refletida (devolve o Origin que chegou, sem allowlist)
- presença de CSP (Content-Security-Policy), HSTS, X-Frame-Options, X-Content-Type-Options
- uso de helmet (Node) ou headers no vercel.json/netlify.toml/next.config
- CSRF: se a autenticação usa cookie de sessão, existe proteção contra requisição forjada de outro site? Procure token anti-CSRF, SameSite=Strict ou Lax no cookie, ou verificação de header de origem. Cookie de sessão sem nenhum dos três deixa qualquer site executar ação em nome do usuário logado. Obs.: quem usa apenas token em header (Authorization: Bearer) normalmente não é vulnerável, marque N/A nesse caso
Comandos:
grep -rn "Access-Control-Allow-Origin\|cors(" --include=*.ts --include=*.js --include=*.json .
grep -rn "Content-Security-Policy\|X-Frame-Options\|Strict-Transport-Security\|helmet" --include=*.ts --include=*.js --include=*.json --include=*.toml .
grep -rEn "csrf|SameSite|sameSite|samesite" --include=*.ts --include=*.js .
FALHOU se: CORS wildcard com credenciais, origem refletida sem allowlist, OU sessão em cookie sem proteção CSRF (token, SameSite ou verificação de origem).
Gravidade: CRÍTICO para CORS aberto com credenciais. ALTO para CSRF em sessão por cookie. MÉDIO para headers ausentes.
Conserto: allowlist de origens exata para o meu domínio, o bloco de headers pronto para o meu provedor de deploy, e a proteção CSRF adequada ao meu framework (SameSite no cookie ou token anti-CSRF).
13. CRIPTOGRAFIA: SENHAS, JWT E DADOS EM REPOUSO
O que é: IA replica padrões antigos que viu em treinamento, hash MD5, JWT sem expiração, dado pessoal sem criptografia. Cada um desses transforma um vazamento pequeno em desastre.
Procure:
- hash de senha: precisa ser bcrypt, scrypt ou argon2. MD5 e SHA1 reprovam na hora. SHA256 puro (sem salt/stretching) também reprova.
- JWT: valida assinatura? Rejeita alg "none"? Tem expiração (exp) razoável? Existe forma de revogar/invalidar sessão (logout efetivo no servidor)?
- dado regulado (LGPD/GDPR: CPF, dados de saúde, financeiros) criptografado em repouso ou pelo menos com acesso restrito documentado
- MFA disponível para painel administrativo (se existir área admin)
Comandos:
grep -rEn "md5|sha1\(|createHash\('sha1'\)|hashlib.md5|hashlib.sha1" --include=*.ts --include=*.js --include=*.py .
grep -rn "bcrypt\|argon2\|scrypt" package.json requirements.txt 2>/dev/null
grep -rn "jwt.verify\|jsonwebtoken\|jose" --include=*.ts --include=*.js .
grep -rn "algorithms" --include=*.ts --include=*.js . | grep -i "jwt\|jose"
FALHOU se: senha com hash fraco, JWT sem validação de assinatura/expiração ou aceitando alg none, ou dado regulado sem nenhuma proteção em repouso.
Gravidade: CRÍTICO para hash fraco e JWT sem validação. ALTO para dado regulado sem criptografia. MÉDIO para ausência de MFA no admin.
Conserto: código de migração de hash (rehash no próximo login), config correta da lib de JWT com algoritmo explícito e expiração, e a estratégia de criptografia em repouso para o meu banco.
14. ISOLAMENTO MULTI-TENANT (só se a Fase 0 detectou multi-tenant)
O que é: se o app guarda dados de clientes ou organizações diferentes na mesma base, o furo mais caro possível é o cliente A enxergar dado do cliente B. Um único endpoint sem filtro de tenant já é vazamento.
Se a Fase 0 NÃO detectou multi-tenant, marque NÃO SE APLICA e pule.
Faça assim:
a) Liste toda query que toca tabela com coluna tenant_id/org_id e verifique se TODAS filtram pelo tenant da sessão, sem exceção
b) Verifique se o tenant_id vem da SESSÃO no servidor, nunca do payload do cliente (tenant no body é tenant forjável)
c) Verifique a camada de cache: a chave de cache inclui o tenant? Cache sem tenant na chave serve dado de um cliente para outro
d) Verifique se existe teste automatizado cobrindo o cenário "usuário do tenant A tenta acessar recurso do tenant B"
Comandos:
grep -rn "tenant_id\|org_id\|company_id\|workspace_id" --include=*.ts --include=*.js --include=*.py --include=*.sql . | head -50
grep -rn "findMany\|SELECT" --include=*.ts --include=*.js . | grep -vin "tenant\|org_id" | head -30
FALHOU se: existe query em tabela multi-tenant sem filtro de tenant, tenant vindo do cliente, ou cache compartilhado sem segregação.
Gravidade: CRÍTICO.
Conserto: a query corrigida com o filtro vindo da sessão, e o padrão (middleware/scope do ORM) que impede a próxima query de nascer sem o filtro.
15. BACKUPS E SEGREGAÇÃO DE AMBIENTES
O que é: backup que nunca foi restaurado é uma esperança, não um backup. E segredo de produção reaproveitado em dev/staging significa que o ambiente menos protegido carrega a chave do mais valioso, comprometer o staging vira comprometer a produção.
Procure:
- rotina de backup do banco (config do provedor, script, cron) e qualquer evidência de teste de restauração
- os mesmos valores de segredo aparecendo em .env.example, .env.development, .env.staging e configs de produção
- banco de dev/staging apontando para a mesma instância de produção
- dados reais de clientes copiados para ambiente de teste sem anonimização
Comandos:
ls -la | grep -i "backup\|dump"
grep -rn "DATABASE_URL\|DB_HOST" .env* 2>/dev/null | sort
diff <(grep -oE "^[A-Z_]+=" .env 2>/dev/null) <(grep -oE "^[A-Z_]+=" .env.production 2>/dev/null) 2>/dev/null
Se não conseguir verificar backup pelo código (é comum, backup vive no provedor), marque NÃO VERIFICADO e me diga exatamente o que checar no painel: existe backup automático? Qual a retenção? Quando foi o último teste de restauração?
FALHOU se: nenhuma rotina de backup identificável, segredo de produção presente em ambiente de dev/staging, ou dado real de cliente em ambiente de teste sem anonimização.
Gravidade: ALTO. CRÍTICO se dev/staging compartilha credencial ou instância com produção.
Conserto: segredos distintos por ambiente, e o passo a passo de configurar e testar a restauração no meu provedor.
16. QUALIDADE QUE VIRA FALHA DE SEGURANÇA
O que é: três problemas de "qualidade" que na prática são vulnerabilidade. Falha de lógica de negócio em billing ou permissão é explorável (desconto infinito, upgrade sem pagar, permissão que se auto-concede). N+1 queries sob volume real é negação de serviço auto-infligida. E ausência de teste automatizado significa que o conserto de hoje pode reabrir o furo de ontem sem ninguém perceber.
Procure:
- regras de negócio críticas (billing, cálculo de preço, concessão de permissão, limites de plano) e verifique a lógica manualmente, linha a linha, este é o item onde a IA que gerou o código mais erra de forma silenciosa
- listagens e relatórios com query dentro de loop (o padrão N+1: for sobre resultados, cada iteração disparando outra query)
- existência de testes automatizados cobrindo os fluxos de autenticação, pagamento e permissão
- inconsistência perigosa entre módulos: a mesma operação protegida em um arquivo e desprotegida em outro (código gerado em sessões diferentes)
Comandos:
grep -rn "for\|map\|forEach" --include=*.ts --include=*.js . | grep -in "await.*find\|await.*query\|await.*select" | head -20
ls -la __tests__ tests test spec 2>/dev/null
grep -rn "describe(\|it(\|test(" --include=*.test.* --include=*.spec.* . | wc -l
FALHOU se: regra crítica de negócio com falha lógica explorável, N+1 em rota exposta, ou zero teste automatizado nos fluxos de auth/pagamento/permissão.
Gravidade: CRÍTICO para falha de lógica explorável em billing/permissão. MÉDIO para N+1 e ausência de testes.
Conserto: a correção da lógica com o caso de teste que a prova, a query agregada substituindo o N+1, e o esqueleto dos testes mínimos para os fluxos críticos.
FASE 2 O RELATÓRIO
Comece com a tabela, sem texto antes:
| # | Item | Veredito | Gravidade |
Vereditos: PASSOU / FALHOU / NÃO VERIFICADO / NÃO SE APLICA
Gravidade só para os que falharam: CRÍTICO (dá para explorar hoje, de fora, sem estar logado, ou vaza dado entre clientes) / ALTO / MÉDIO
Depois, para CADA item que falhou, exatamente nesta ordem:
**Onde está:** arquivo e linha, com o trecho de código vulnerável (mascarando qualquer segredo)
**O que dá para fazer com isso:** o ataque concreto, em uma frase, sem jargão, como se explicasse para o dono do negócio
**O conserto:** o código, comando ou SQL exato para corrigir, pronto para colar, no meu stack. Não descreva o que fazer; escreva a coisa.
**Como eu confirmo:** o teste que eu rodo depois para saber que fechou
Ordene do mais crítico para o menos.
Depois dos 16 itens, inclua duas seções finais:
**ACHADOS EXTRAS:** se você encontrou vulnerabilidade relevante que não se encaixa em nenhum dos 16 itens, reporte aqui no mesmo formato (onde está, o que dá para fazer, o conserto, como confirmo). Não ignore um furo grave só porque ele não estava na lista.
**BLOQUEIA O DEPLOY:** lista curta e direta, só com os achados que impedem colocar ou manter este app em produção hoje. Se a lista estiver vazia, diga isso explicitamente.
Termine com exatamente esta linha:
RESULTADO: X de 16 reprovados (Y críticos, Z não verificados)
REGRA ANTI-FALSO-POSITIVO E ANTI-FALSO-NEGATIVO
Se o resultado der 0 ou 1 reprovado, PARE e refaça os itens 2, 3, 6, 7 e 14 com rigor extra, listando arquivo e linha de cada PASSOU. App feito com IA quase nunca passa limpo, resultado limpo demais geralmente significa que você não olhou fundo o suficiente, não que o app está seguro.
Da mesma forma, não invente falha para parecer útil. Se passou de verdade e você tem a evidência, marque PASSOU e cite a prova.
NÃO VERIFICADO não conta como PASSOU em nenhuma hipótese. Some os não verificados na linha de RESULTADO e liste o que precisa de mim para fechar cada um.
FASE 3 CONSERTO
Depois do relatório, me pergunte se quero que você aplique os consertos.
Se eu disser sim:
- comece pelos CRÍTICOS
- um de cada vez
- antes de tocar em cada item, registre o ESTADO ANTES: item, veredito original, arquivo e linha, o trecho vulnerável (com segredo mascarado) e a gravidade
- me mostre o diff ANTES de cada mudança e espere eu aprovar
- depois de aplicar e eu confirmar que funcionou, registre o ESTADO DEPOIS: o que mudou, novo veredito e o teste de verificação que passou
- nunca mexa em autenticação e banco na mesma leva
- chave exposta se conserta em duas etapas: primeiro a rotação (eu faço no painel do provedor, você me diz onde), depois a mudança no código
FASE 4 RELATÓRIO ANTES E DEPOIS
Quando eu disser que terminamos os consertos (ou não quiser corrigir mais nada), gere um relatório de fechamento em markdown, pronto para eu salvar e arquivar como evidência da auditoria. Estrutura:
1. CABEÇALHO: data, nome/identificação do projeto, stack, tipo de aplicação, e o placar de abertura vs fechamento (ex.: "Abertura: 11 de 16 reprovados, 5 críticos. Fechamento: 3 de 16 reprovados, 0 críticos").
2. TABELA COMPARATIVA, uma linha por item auditado:
| # | Item | Antes | Depois | Gravidade original | Status |
Onde Antes e Depois usam os vereditos (PASSOU / FALHOU / NÃO VERIFICADO / NÃO SE APLICA) e Status é Corrigido, Pendente, Aceito como risco (quando eu decidi não corrigir) ou Sem alteração.
3. DETALHE DOS ITENS CORRIGIDOS, para cada um:
**Antes:** o trecho vulnerável (segredo mascarado) e o ataque que era possível, em uma frase
**Depois:** o trecho corrigido e por que o ataque não funciona mais
**Verificação:** o teste que rodamos e o resultado
4. PENDÊNCIAS E RISCOS ACEITOS: lista do que ficou em aberto, por que, e o risco residual de cada um em uma frase. Inclua aqui os NÃO VERIFICADOS que eu ainda não destravei.
5. RECOMENDAÇÃO FINAL: uma frase direta sobre se, no seu julgamento, o app está apto a ir/permanecer em produção agora, ou o que falta para isso.
Regras do relatório: nunca escreva o valor de um segredo, mesmo no estado "antes", sempre mascarado. Não marque nada como Corrigido sem o teste de verificação correspondente ter passado. Se um conserto introduziu regressão que a gente reverteu, registre isso em Pendências, não em Corrigidos.Como ler o resultado
CRÍTICO significa: alguém consegue explorar isso hoje, de fora, sem estar logado, ou um cliente enxerga dado de outro. Conserte antes de dormir.
NÃO VERIFICADO não é aprovação. É a IA dizendo que precisa de mais acesso para responder. Dê o que ela pediu e rode de novo.
Deu 0 de 16? Desconfie. O próprio prompt manda o agente refazer a verificação nesse cenário, mas se ainda assim vier limpo, peça o arquivo e a linha que provam cada aprovação.
Deu 9, 11, 13 de 16 reprovados? Normal. Não é sinal de que você é ruim, é sinal de que a IA que escreveu o app otimiza para funcionar, não para resistir a ataque. Conserte os críticos hoje e o resto na semana.
Calibragem por tipo de aplicação
Site institucional ou landing page: o peso está em rate limiting, segredos, histórico do Git, validação de input, stack trace, supply chain e headers, que são os itens 1, 2, 4, 5, 8, 11 e 12. RLS, storage e multi-tenant tendem a dar NÃO SE APLICA, e está tudo bem.
SaaS ou qualquer sistema com dados de múltiplos clientes: RLS, storage, autorização no servidor e isolamento multi-tenant, os itens 3, 6, 7 e 14, são os que decidem se o negócio sobrevive a um incidente. Não podem ser pulados em hipótese alguma.
Quando rodar de novo: a cada mudança estrutural relevante. Nova integração, nova tabela com dado sensível, nova rota pública, novo webhook. Não é auditoria de lançamento, é auditoria de ciclo de vida. E o relatório de fechamento de cada rodada vira a linha de base da próxima: você compara placares e enxerga a segurança do projeto melhorando, ou degradando, no tempo.
Conclusão: a ferramenta amadureceu porque o problema amadureceu
Um mês separa a primeira versão desta, e foi o suficiente para o prompt rodar em projetos reais e voltar diferente. Porque vibe coding é linha de produção: geradores de app entregando SaaS completo, agentes de codificação dentro de todas as equipes, código de IA sustentando operação real com dado real de cliente real. A escala disso já está documentada: o AI Index Report 2026, publicado em abril pelo Stanford HAI, o instituto de IA centrada no humano da Universidade de Stanford, registrou que a adoção organizacional de IA chegou a 88%. E a Veracode mediu o outro lado da mesma moeda: nas organizações que adotaram ferramentas de codificação com IA, ela já é autora de aproximadamente metade de todo o código commitado. Adoção nessa velocidade não recua. É o caminho sem volta em forma de estatística, e é exatamente por isso que a verificação precisa correr na mesma esteira.
O problema nunca foi a ferramenta, sempre foi o processo de quem coloca o código em produção. E se o volume de código gerado explodiu, o processo de verificação precisava evoluir na mesma velocidade.
Foi isso que esta versão 2.0 tentou fazer: transformar um checklist em um contrato, uma lista de erros em 16 verificações com evidência obrigatória, e um relatório solto em um ciclo completo que termina em documento de auditoria arquivável. Não existe 100% de segurança e não existe risco zero. Mas existe know-how, existe método, e agora existe um prompt que carrega os dois, refinado por tudo o que a prática ensinou desde a primeira versão.
Este artigo faz parte de uma série. Sempre que a prática ensinar algo novo, a auditoria evolui, e eu compartilho aqui.
E vale repetir o que fecha todas as conversas sérias sobre esse tema: a auditoria automatizada é o começo, não o fim. Se o seu projeto lida com dados sensíveis, com clientes de terceiros ou com escala, a Forte Security pode conduzir a revisão de segurança completa do seu ambiente. Fale com o nosso time.
Referências
Veracode. "2026 GenAI Code Security Report". Julho de 2026. Disponível em: https://www.veracode.com/resources/analyst-reports/2026-genai-code-security-report/
Stanford HAI. "Artificial Intelligence Index Report 2026". Stanford University, Institute for Human-Centered AI, abril de 2026. Disponível em: https://hai.stanford.edu/ai-index
METR. "Measuring the Impact of Early-2025 AI on Experienced Open-Source Developer Productivity" (julho de 2025) e a atualização do estudo "We are Changing our Developer Productivity Experiment Design" (fevereiro de 2026). Disponíveis em: https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/ e https://metr.org/blog/2026-02-24-uplift-update/
Spracklen, J.; Wijewickrama, R.; Sakib, A. H. M. N.; Maiti, A.; Viswanath, B.; Jadliwala, M. "We Have a Package for You! A Comprehensive Analysis of Package Hallucinations by Code Generating LLMs". USENIX Security Symposium, 2025. University of Texas at San Antonio. Disponível em: https://arxiv.org/abs/2406.10279