Ir para o conteúdo

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.yml que 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.