28 · Caçando bugs nos seus guardrails¶
Este capítulo traz, por enquanto, as partes do Caçador de Bugs em Guardrails (Guardrail bug hunter) que leem um achado nos termos da OWASP e que comparam os rails que você roda com avisos publicados. O restante do capítulo -- a varredura, o catálogo de regras, as reproduções, as correções, as linhas de base e o portão -- é escrito a partir da referência do próprio caçador e chega com o guia completo.
Como ler um achado nos termos da OWASP¶
Todo achado traz as entradas do OWASP Top 10 for LLM Applications 2026 e do OWASP Top 10 for Agentic
Applications 2026 de que é uma instância, cada uma com a sua edição (LLM01:2026, ASI04:2026), para que
uma equipe de segurança o registre onde ele pertence. Uma regra mapeada para LLM03:2026 Excessive Agency
traz também a causa raiz entre colchetes -- funcionalidade, permissões ou autonomia -- como
owasp_root_cause. Uma regra sem entrada OWASP diz por quê, no mesmo lugar. O SARIF traz o mesmo
mapeamento como taxonomies e relationships, e cada resultado recebe a etiqueta OWASP <id>, para que o
code scanning possa ser filtrado por ela.
A linha de correção de cada achado traz primeiro a instrução do próprio caçador e depois a mitigação que a OWASP lista para a entrada, nas palavras deste guia.
A declaração de cobertura, no nível da política¶
O resumo de cada varredura traz uma declaração de cobertura: para quais entradas da OWASP o caçador tem regras, quais não tiveram achado nesta varredura e para quais ele não tem regra. As regras do caçador decidem a parte de cada risco que a política e o seu catálogo controlam -- no nível da política -- e nada além disso.
Com as nove regras descritas no capítulo 31 e abaixo, o caçador tem regras para nove das dez entradas LLM e
para dez das dez entradas agênticas. A única entrada que nenhuma regra cobre é LLM05:2026 Data and Model
Poisoning: uma propriedade dos dados de treinamento e dos pesos do modelo, que nenhuma política de
guardrail decide. A declaração a nomeia com esse motivo em toda varredura.
Uma varredura limpa sob uma entrada da OWASP
Uma varredura limpa sob uma entrada da OWASP significa nenhum achado no nível da política, não que o risco esteja ausente. Uma entrada sem achado nesta varredura é listada como tal, o que não diz nada além disso.
O resumo lista também toda regra que não teve nada para ler -- uma regra cujas declarações o catálogo não
faz não levanta nada, e o resumo diz isso -- e toda afirmação que a varredura usou como declarada, como uma
ferramenta que a equipe declara sandboxed, em assumptions.
Rails com um aviso publicado¶
Um defeito encontrado num rail de biblioteca viaja com toda cópia do arquivo e com toda configuração que
nomeia o rail. O caçador traz um registro de avisos publicados, e QG022 levanta um achado quando um fluxo
varrido corresponde a um deles:
- pelo texto: um fluxo cujo texto normalizado é o fluxo de um aviso -- reindentado, com comentários acrescentados ou removidos, com as continuações de linha unidas -- corresponde, e um fluxo com uma condição alterada não corresponde;
- por referência: um
config.ymlque nomeia um rail de biblioteca descrito por um aviso corresponde na linha que o nomeia.
O registro vem com dois avisos, QGA-2026-0001 (o rail de saída fiddler bot faithfulness do NeMo) e
QGA-2026-0002 (um fluxo de carrinho publicado com os exemplos do ACE). Toda varredura registra
advisories_version e advisories_digest nos seus metadados e nas propriedades da execução do SARIF, para
que um achado possa sempre ser lido diante do registro sob o qual foi levantado.
A versão que você instala: --dependencies¶
Uma referência nomeia um rail, não uma versão. Sem um arquivo de dependências, o achado diz
version: unknown e nomeia a faixa afetada do aviso. Dê ao caçador o arquivo que fixa o que você instala --
requirements.txt, pyproject.toml, uv.lock ou poetry.lock -- e a referência é conferida com a versão
instalada: dentro da faixa, ela é levantada com essa versão; fora dela, nada é levantado. Fornecer o
arquivo estreita o achado; não cria um novo, e por isso a sua impressão digital é a mesma.
from stalwart_agents.client import Client
report = Client().hunt(inputs={"rails_config": "config.yml"}, dependencies="uv.lock")
O modo hunt recebe o mesmo arquivo como a sua entrada dependencies, e o stalwart verify como a
sua opção --dependencies.
Os limites¶
- O caçador trabalha sobre as regras em torno das ações, não sobre os prompts.
- Uma varredura limpa cobre as regras que leu e nenhuma outra. Uma regra cuja declaração falta não levanta nada, e o resumo a lista como não tendo tido nada para ler.
- O registro de avisos traz os avisos publicados na versão que a varredura nomeia; um rail com um defeito
que ninguém publicou não é encontrado por
QG022.