cd /news/ai-tools/o-prompt-de-appsec-que-eu-criei-acho… · home topics ai-tools article
[ARTICLE · art-118541] src=dev.to ↗ pub= topic=ai-tools verified=true sentiment=· neutral

O prompt de AppSec que eu criei achou 4 gaps de segurança

A developer created an AppSec prompt that found four security gaps in open-source projects, including an authorization flaw affecting one of 174 routes. The prompt enforces a contract that limits what the LLM can assert, distinguishing real findings from false positives. The developer emphasizes that static scanners like Semgrep cannot detect missing authorization controls, which require semantic understanding.

read6 min views2 publishedSep 2, 2026

Pessoal, eu quase mandei uma lista de 23 "vulnerabilidades" para projetos de software livre.

Esses dias rodei um prompt de AppSec próprio em alguns repositórios conhecidos.

XSS, CORS, CSP, um SVG suspeito. O LLM tinha encontrado um monte de coisa. Na hora, parecia um ótimo resultad, volume, evidência, todo trabalho feito.

Aí eu parei e pensei: se eu fosse o cara do outro lado da issue, eu levaria isso a sério?

A conta virou quando eu parei de pedir "audita meu código". Passei a escrever o contrato antes de abrir o repo. Direção (o que o modelo pode afirmar) e disciplina (o que ele não pode). Isso não deixa o LLM mais inteligente. Controla o comportamento. Em segurança, isso é o que separa auditoria de lista.

O que você leva daqui: o contrato (esqueleto), por que o scanner não substitui hunt, e as três portas pra não colar relatório no tracker. Os repos no meio são prova. Não são o ponto.

Esse é o ponto que eu demorei pra aceitar.

O modelo já conhece OWASP. O que muda é o que ele tem permissão de afirmar. Sem isso, "audita meu código" devolve XSS fantasma, CORS, CSP, um SVG. Volume. A pergunta de segurança some: disto aqui, o que vira issue, o que vai pro e-mail privado, o que é silêncio?

Direção amarra três coisas antes da leitura:

Disciplina é o que corta o teatro. O texto que o modelo vê antes de abrir arquivo:

Padrão: OWASP ASVS 5.0, com o ID do requisito. "Boas práticas" não conta.
Invariante: uma frase, falsificável.
Relatório: finding, observação e hipótese não misturam.

Sem arquivo:linha → não é finding.
CWE inventado → encerra a sessão.
Needs-runtime → não vira card.
Sem X de Y → não é hunt.
Estático. Sem PoC. Sem request no ambiente deles.
A sessão que encontra não é a sessão que corrige.

Esse último item parece burocracia até o modelo achar um gap e, empolgado, reescrever o where

do Prisma no mesmo fôlego, sem teste. Finding de um lado. Fix de outro.

O prompt completo é privado. Por trás dele tem regras numa knowledge base (quando hunt, quando varredura, o que é finding, as três portas) e skills do harness que amarram o modelo nisso. Sem a KB o modelo improvisa. Sem a skill ele lê o bloco e segue viagem. O esqueleto acima é o que cabe num artigo.

Um limite honesto: isso só rende em modelo de raciocínio alto. Num modelo fraco, prompt bom organiza melhor o erro.

Scanner estático (Semgrep, CodeQL, Bandit) acha padrão que existe. Chamada perigosa, query concatenada, header ausente. É rápido, determinístico, barato. Roda a cada push. Eu não troco isso por LLM.

O que ele estruturalmente não vê é um controle ausente. "Esta rota deveria checar o dono do recurso antes de retornar, e não checa" não é regex. É propriedade semântica: o que é um tenant, que recurso pertence a quem, qual rota é pública de propósito.

Por isso o Semgrep entra neste texto. Não porque ele tenha achado os gaps. Porque, no mesmo commit do caso das 174 rotas, ele foi o baseline de CI. Sete rulesets, 25 segundos:

semgrep --config p/default --config p/typescript --config p/react \
        --config p/nextjs --config p/owasp-top-ten \
        --config p/javascript --config p/secrets

23 findings. 17 eram unsafe-formatstring em template. Zero de autorização.

Autorização quebrada (BOLA, OWASP API #1) não tem regra sintática estável: o padrão certo e o errado usam o mesmo findUnique

.

O hunt começa invertido. Você escreve a invariante antes de abrir o arquivo. Enumera caminhos. Classifica: enforced-explicitly

, inherited

, gap

, out-of-scope

. No fim tem denominador. "Achei um IDOR" sem "1 de 174" não é hunt. É vibe.

Naquele commit: 173 de 174 rotas seguravam. Uma não. Esse é 1 dos 4 gaps de autorização da rodada. Os outros 3 estão num alvo cujo detalhe de rota não entra neste post. Formbricks e Dub são hardening, não entram nos 4.

Scanner Hunt dirigido
Enxerga padrão presente controle ausente
Bom em injeção, XSS, secret, header, crypto fraca o 1 caminho em 100 que pula o guard
Custo centavos, a cada PR dólares, periódico
Falso positivo ruído sintático alucinação de finding, se você soltar o denominador
Ponto cego lógica de negócio cobertura exaustiva do trivial

Duas cadências, duas perguntas. Verde no CI nunca respondeu "a invariante ainda vale".

A falha mais comum de IA em segurança é fabricar achado para não devolver silêncio. Disciplina se mede nisso.

Busca ingênua por dangerouslySetInnerHTML

em um app Next: oito hits, XSS crítico fantasma. Seguindo o dado até a função, é DOMPurify. Em SSR o React escapa. Zero finding. O que virou issue foi CORS contraditório (*

  • credentials), que o navegador rejeita. formbricks#9081. Tinha um SVG Needs-runtime

. Não virou card. Prompt solto teria aberto dois. O dirigido abriu um.

Webhook de entrada: 7 de 7 autenticam o remetente. Semgrep no escopo: 0. Autenticidade não é padrão sintático. Dois HMAC com !==

no mesmo repo que já usa timingSafeEqual

. Observação, não gap da invariante. dubinc/dub#4415.

Axios, gin, Guzzle, Medusa, Documenso: invariante manteve. 3/3, 4/4, 11/11. Não abri issue. Abrir card pra dizer "olhei e está ok" é o mesmo ruído que o modelo solto fabrica.

A prova compacta, oito alvos, estático, sem PoC:

Alvo O que a disciplina fez

Se o seu critério de sucesso for volume, metade dessa tabela parece desperdício. Pra mim as linhas em silêncio são as que sustentam as outras. Impacto verificável não é o mesmo que "issue aceita": a #9081 ganhou security

e agent-ready

; a #4415 entrou no Linear deles como ENG-1732. As duas continuam abertas.

Disciplina de divulgação é a mesma disciplina de finding. Least privilege aqui é o blast radius do texto. HITL é você escolhendo a porta, não o modelo. Audit trail é a issue ou o e-mail, nunca o transcript.

Issue pública: hardening. Bypass, injeção, secret, autorização quebrada: privado primeiro. Sem SECURITY.md

, é e-mail do mantenedor, não thread.

Copia. Cola no fim da sessão, antes de abrir o browser.

O e-mail da trilha, com os links do que foi aberto e o porquê do silêncio:

Pega o repo que você mantém. Escreve uma invariante falsificável antes de abrir arquivo. "Todo webhook de entrada verifica assinatura antes de mutar estado" serve.

Roda o scanner primeiro. Depois pede ao modelo o passe só naquela invariante, com denominador e arquivo:linha

, sem PoC, sem fix na mesma sessão.

Se voltar "23 findings" sem X

de Y

e sem citação, você não tem auditoria. Tem lista. Faltou direção. Faltou disciplina.

No último output de "audit" que um modelo te deu: você teria aberto issue pública, mandado e-mail privado, ou deixado nada? Se a resposta honesta for "ia colar o relatório inteiro no tracker", a porta ainda está solta.

Sobre o produto: o prompt é privado. Neste post vai o esqueleto. Por trás dele tem regras numa knowledge base e skills no harness. O pack (metodologia, cases, templates) está sendo refinado. Se você quer ser avisado quando sair mais, ou tem caso de uso específico, me chama no LinkedIn ou GitHub.

── more in #ai-tools 4 stories · sorted by recency
── more on @owasp 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/o-prompt-de-appsec-q…] indexed:0 read:6min 2026-09-02 ·