O que disparou
Um caminho de acesso novo num app de assinatura, e um punhado de telas que passaram a tratar mal quem tinha direito. Nenhuma delas era invasão.
Ela varre as nove famílias que um invasor explora, de RLS e policies a
SECURITY DEFINER, privilégio, webhook, injeção e storage. E varre o que checklist
nenhum olha: o que o seu sistema erra com quem age de boa-fé, sem atacante nenhum.
Não tem servidor para subir, nem chave de API, nem pacote para instalar. O que precisa existir na máquina é Python 3.11 ou mais novo. Roda no Claude Code e no Codex, porque os dois têm subagentes, que é o que o método exige.
git clone https://github.com/thdamas/saas-security-auditBaixa a skill inteira: o motor em Python, os prompts de cada lente e os dois catálogos.
O que esperar. Uma pasta de 34 arquivos, sem nenhum pacote para instalar.
cp -r saas-security-audit ~/.claude/skills/auditorÉ o que faz o Claude Code enxergar a skill e criar o comando /auditor.
O que esperar. No Codex, o AGENTS.md do repositório já traz o mesmo método.
python verificar.pyRoda contra um SaaS de exemplo que tem um defeito plantado para cada detector.
O que esperar. APROVADO com 19 detectores, a cobertura e o painel conferidos na sua máquina.
/auditorA skill pede o perfil do app, mostra o inventário medido e só então gasta agente.
O que esperar. Portão humano entre as fases: nada avança sem você aprovar.
Antes de rodar em app de cliente. A skill é somente leitura e nunca altera o seu código. Mas ela escreve o relatório em disco, e relatório de vulnerabilidade não pode cair no repositório: por padrão ele vai para fora, e o scanner avisa se você apontar a saída para dentro.
Um SaaS pequeno chega no momento de escalar e alguém pergunta se pode abrir o cadastro, subir a verba, trazer volume. A resposta costuma ser um palpite.
Um caminho de acesso novo num app de assinatura, e um punhado de telas que passaram a tratar mal quem tinha direito. Nenhuma delas era invasão.
Contratar pentest custa caro e responde outra pergunta. Rodar um prompt de auditoria é barato e audita por amostragem sem avisar que amostrou.
Porque escalar multiplica o dano de um defeito que já existe. O custo de descobrir depois é sempre maior que o de olhar antes.
Duas dimensões, e por que a segunda não existe no mercado
Nove famílias de falha, da RLS ao denial of wallet
As quatro lentes do erro que acontece sem atacante
Seis fases, com portão humano entre elas
Cobertura provada por aritmética, não por promessa
Agentes independentes tentando derrubar cada achado
Um arquivo, offline, com o gate de escala
O que ela não faz, dito antes de alguém descobrir
A indústria audita abuso. O prejuízo mais provável num SaaS pequeno vem do outro eixo, e ele não depende de existir alguém interessado em te atacar.
O que um atacante alcança que não deveria?
O sistema faz a coisa certa com quem age de boa-fé?
Por que o checklist não acusa. Ele pergunta se o controle existe, e nesses casos ele existe e está funcionando. O RLS está ligado, a policy está certa, a guarda está lá. O defeito é a pergunta que a guarda faz, e nenhuma lista de verificação tem uma linha para isso.
Cada classe tem severidade base, como confirmar e, o que quase nenhum material traz, o que a REFUTA. Sem a última coluna o cético improvisa e aceita tudo.
Tabela sem RLS, tautologia, escrita sem WITH CHECK, policy empilhada que vira OU
SECURITY DEFINER sem search_path, view rodando como dono, RPC sem vínculo
Chave de service role alcançável pelo bundle, segredo literal, env com prefixo público
Assinatura, idempotência, replay, enumeração de documento, rota cara sem cota
XSS armazenado, filtro cru do PostgREST, autorização só na interface
Bucket público com documento, URL pública onde devia ser assinada, path sem dono
Injeção de prompt, contexto de um usuário no outro, denial of wallet
Backup não confirmado, projeto pausável, push direto na main sem gate
Trilha ausente, exclusão incompleta, dado atravessando escopo de tenant
A severidade não mede explorabilidade, mede consequência. Cobrar de quem não devia é pior que negar a quem tem direito: negação se conserta com um pedido de desculpa, cobrança se conserta com estorno e confiança perdida.
Quais portas essa pessoa encontra, e todas sabem que ela existe?
Na prática. Uma guarda exige assinatura paga. O convidado de cortesia não tem linha naquela tabela, então ele é tratado como inexistente em cada tela que pergunta assim.
Quando a leitura protegida falha ou volta vazia, cai em negado ou em liberado?
Na prática. Uma query lê tabela de staff com o client do usuário. O RLS devolve zero linha sem erro, e zero linha vira estado válido na tela.
Se N caminhos escrevem o mesmo dado, todos gravam os mesmos campos?
Na prática. Três caminhos gravam pagamento e um esquece o id do provedor. O estorno não acha a venda, e a comissão segue sendo paga.
A regra de data ou valor sai da fonte que o mundo real honra?
Na prática. O código calcula a vigência do jeito conveniente. O parceiro externo honra outra data, e a promessa está escrita na tela do cliente.
Nenhuma fase começa sem aprovação, e as aprovadas ficam registradas em arquivo. Ao retomar, a auditoria diz em que fase parou antes de fazer qualquer coisa.
O mapa do app, aprovado por um humano
Mapa errado audita o lugar errado com confiança
Estado real do banco, não só as migrations
Migration diz o que foi pedido, o dump diz o que é
Scanner determinístico, zero LLM
Inventário, detecção e partição provada
Portão de calibragem com um lote só
Prompt torto morre em 1, não se multiplica por 14
Um subagente por lote, em paralelo
Dinheiro e matriz ficam com o orquestrador
Agentes diferentes, contexto limpo
Sem isso a auditoria concorda consigo mesma
Um HTML com o gate de escala
Mais o que não foi verificado, declarado
Agente que escolhe o que auditar audita por amostragem e não avisa. O scanner parte o universo em lotes com lista explícita e checa a partição por asserção.
Se qualquer uma falhar, a rodada para com erro. A auditoria não continua sabendo que deixou algo de fora.
Todo achado crítico e alto vai para um agente diferente, de contexto limpo, cuja única tarefa é derrubar a acusação. Ele é a defesa, não a revisão.
procurou os bloqueadores e nenhum existe, com a lista do que procurou
o problema existe, a severidade estava errada, com o fato que a mudou
existe bloqueador real, citado com arquivo e linha. Sem localização não vale
o bloqueador existe no código e não na branch que vai ao ar. Segue contando
A assimetria é deliberada. Refutar é mais difícil que confirmar. Um achado exagerado custa dez minutos de leitura, um achado descartado por engano custa o incidente.
11 bloqueadores em aberto, contando crítico e alto que ainda não foram corrigidos ou aceitos.
A calibragem também é um limite. O motor foi afinado em Supabase com TypeScript. Em outra stack as duas dimensões continuam valendo e os detectores mecânicos acham menos. Isso está escrito na primeira página do projeto, e não em uma nota de rodapé.
O que esperar. Vai parecer burocracia. É a fase que decide se o resto audita o lugar certo.
O que esperar. Aparecem achados de graça, sem gastar um agente. Alguns vão ser óbvios e antigos.
O que esperar. O volume assusta na primeira leitura. A maioria é médio e baixo, e é normal.
O que esperar. Parte dos achados cai. É o sistema funcionando, não desperdício.
O que esperar. O baseline guarda o que já foi julgado. A segunda rodada não recomeça do zero.
Não. A auditoria é somente leitura e nunca altera, move ou apaga arquivo do seu app. O único arquivo que ela escreve lá é o perfil de configuração, que é o mapa do projeto. O relatório vai para fora do repositório, porque relatório de vulnerabilidade dentro do repo fica a um comando de distância de subir para o lugar errado.
Duas dimensões. A primeira é segurança e abuso, em nove famílias: RLS e policies, funções e RPC, privilégio e segredo, rotas e webhook, injeção no front, storage, IA, plataforma e privacidade. A segunda é corretude de negócio, que pergunta se o sistema faz a coisa certa com quem age de boa-fé.
É quando o controle está presente e funcionando, o checklist fecha verde, e mesmo assim o produto erra. Por exemplo: uma guarda nega acesso a quem tem direito porque foi escrita assumindo que ter direito é o mesmo que ter assinatura paga; ou uma consulta lê tabela protegida com o client errado, o RLS devolve zero linha sem erro, e o app lê isso como sinal de que está tudo certo.
Não. A licença é Apache 2.0 e o motor é Python puro, sem nenhuma dependência externa. O que precisa existir na máquina é Python 3.11 ou mais novo. O custo que existe é o dos agentes de IA nas fases de julgamento, que roda na sua própria assinatura do Claude Code ou do Codex.
O motor foi calibrado em Supabase, com Postgres, RLS e PostgREST, mais TypeScript. Em outra stack, como Rails, Django ou Go, as duas dimensões e os catálogos continuam valendo, mas os detectores mecânicos encontram menos, porque eles leem migrations SQL e código TypeScript. Isso está dito na primeira página do projeto de propósito.
Por aritmética, não por promessa. Um scanner determinístico inventaria o app e particiona o trabalho em lotes com lista explícita, e a partição é validada por asserção: a soma dos lotes tem que ser igual ao universo e a interseção entre eles tem que ser vazia. Se falhar, a rodada para com erro.
Todo achado crítico ou alto vai para um agente cético diferente, com contexto limpo, cuja única tarefa é derrubar a acusação. Ele só pode refutar citando o bloqueador com arquivo e linha. A assimetria é deliberada: um achado exagerado custa dez minutos de leitura, um achado descartado por engano custa o incidente.
Não. É análise estática e passiva: não forja requisição, não sonda produção, não chama função direto. Passar no gate não é certificação, é um veredito informado sobre o que foi examinado, acompanhado da lista do que não foi verificado.
Instala como skill do Claude Code e vira o comando /auditor. O motor é Python puro, sem dependência, então roda sozinho em qualquer lugar.
$ git clone https://github.com/thdamas/saas-security-audit
$ cp -r saas-security-audit ~/.claude/skills/auditor
# prova que funciona, contra o app de exemplo
$ python verificar.py
APROVADO: 19 detectores, cobertura e painel
# no Claude Code
> /auditor