
Fixtures reproduzíveis de GitHub Actions vulneráveis e corrigidas para injeção de fluxo de trabalho agêntico (CVE-2026-44246), com cobertura de detecção medida e orientação de mitigação.
Fixtures reproduzíveis de vulnerável/corrigido para a classe de bug publicada como CVE-2026-44246 (nnU-Net, injeção de fluxo de trabalho agêntico, CVSS 7.2, CWE-1427), além da saída medida de três detectores contra elas.
Ele existe porque não havia nada contra o que testar um detector. Escrever uma regra para esta classe significa escrever uma fixture à mão e torcer para que seja fiel; as revisões publicadas só estavam disponíveis sabendo qual commit olhar, o que acabou sendo a parte interessante.
Um fluxo de trabalho do GitHub Actions entrega texto não confiável a um agente de IA que possui as credenciais do repositório.
Quatro condições, todas necessárias:
issues, issue_comment, pull_request_review —
eventos cujo texto qualquer pessoa com uma conta no GitHub pode escrever.issues: write, contents: write, e assim por
diante.claude-code-action e
codex-action recusam um ator de execução sem permissão de escrita a menos que uma
entrada (allowed_non_write_users, allow-users) o habilite explicitamente. Sem
essa entrada, um autor não confiável nunca chega ao agente, e o fluxo de trabalho é
a configuração segura em vez desta.${{ github.event.issue.body }} dentro de prompt:) ou buscado em tempo de
execução (gh issue view) com o token que o job entrega.Não é injeção de template. Nada é avaliado como shell; o payload é prosa, e o
interpretador é o modelo. É por isso que o conselho habitual — coloque suas variáveis
entre aspas, não use eval — não se aplica, e por isso a correção abaixo é sobre o que
o agente pode alcançar em vez de sobre escapar a entrada.
O nnU-Net endureceu este fluxo de trabalho em dois commits, e um detector que trata "antes" e "depois" como binário erra o do meio.
| revisão | commit | data | --allowedTools do agente | veredito |
|---|---|---|---|---|
| vulnerável | 94300b49e716 | 2026-04-13 | gh issue comment, gh issue edit | escrita alcançável |
| "a correção" | 4e4770b0b0e6 | 2026-04-24 | apenas gh issue comment; rotulagem movida para um script wrapper | escrita ainda alcançável |
| posterior | 11bd8746fc06 | 2026-04-27 | nenhum; um passo posterior publica a partir de um arquivo que o agente escreve | não alcançável |
O commit que todos chamariam de correção — sua mensagem é "hardened issue
and PR agents" — removeu gh issue edit e encaminhou rótulos através de
.github/scripts/safe-label.sh, mas deixou Bash(gh issue comment:*) com o
agente. O agente ainda podia ser direcionado a comentar, e o padrão da allowlist
gh issue comment:* não é restrito à issue que disparou o evento, então o alvo era
escolha do modelo. Apenas o terceiro commit fechou isso: o agente agora
escreve /tmp/issue-comment.md e um passo posterior, não-agente, o publica com
ISSUE: ${{ github.event.issue.number }} obtido do evento.
Duas coisas decorrem disso, e são a razão pela qual este repositório não é apenas dois arquivos:
"Corrigido" é uma afirmação sobre uma revisão, não uma versão. v2.4.1 é citada como
a release corrigida, mas seu .github/workflows/ contém apenas codespell.yml — os
fluxos de trabalho do agente não estão nessa tag. Você não pode verificar a correção a
partir da tag; você tem que fixar o commit.
O bloco de permissões nunca muda. issues: write está presente e correto
em todas as três revisões, incluindo a última, porque um passo posterior precisa dele. Um
detector que se baseia apenas em permissions não consegue separar a revisão 1 da
revisão 3. O que as separa é o que o agente pode chamar.
Três detectores, executados sobre ambas as fixtures e todas as três revisões reais. Saída bruta completa e versões das ferramentas em results.md.
| revisão | agentbound 0.1.3 | sisakulint v0.3.7 | zizmor 1.30.1 |
|---|---|---|---|
1-vulnerable | HIGH write-scope, HIGH untrusted-content | ai-action-prompt-injection | — |
2-fix-commit | HIGH write-scope, CRITICAL author-association | — (apenas genérico) | — |
3-later | LOW write-scope, CRITICAL author-association | ai-action-excessive-tools, ai-action-execution-order | — |
Nenhum detector está simplesmente "errado" aqui — eles respondem a perguntas diferentes:
ai-action-prompt-injection dispara na revisão vulnerável,
onde o corpo da issue é interpolado em prompt:, e para assim que
a interpolação desaparece. Correto. Suas descobertas na revisão posterior valem
a pena ler antes de agir sobre elas — ai-action-excessive-tools sinaliza
Write, que este fluxo de trabalho usa de propósito para escrever o arquivo que um passo
posterior publica; e ai-action-execution-order quer o agente por último, que é
exatamente o que este design evita, já que os passos privilegiados vêm depois que
o turno do modelo termina.permissions do job. Sua
descoberta ci-agent-missing-author-association é reportada como critical na
revisão posterior e seu próprio README descreve isso como excessivamente severo em vez de
falso: auto-triage deve ser alcançável por qualquer pessoa.Toda ferramenta aqui tem uma descoberta que reporta em uma revisão cujo autor já havia raciocinado sobre exatamente aquela condição. Esse é o estado normal de uma heurística, e a razão pela qual uma tabela de cobertura é mais útil do que uma coluna de passa/falha.
git clone https://github.com/sushant-me/agentic-workflow-injection
cd agentic-workflow-injection
bash scripts/fetch-revisions.sh # the three real revisions, pinned by SHA
bash scripts/benchmark.sh # runs every detector that is installed
benchmark.sh reporta um detector ausente como ausente em vez de
pulá-lo silenciosamente, porque uma tabela de cobertura que omite discretamente uma ferramenta não prova
nada sobre ela.
As fixtures são a forma reduzida, escritas para este repositório e licenciadas sob MIT
como o resto dele. Elas são fiéis ao mecanismo em vez de cópias:
fixtures/vulnerable.yml carrega as quatro condições e nada mais, e
fixtures/fixed.yml é o mesmo arquivo com as seis mudanças que importam, cada uma
anotada. Se você está adicionando uma regra, esses dois arquivos são as menores entradas que
ela precisa acertar.
Duas peculiaridades de interface, tratadas no script mas que vale a pena conhecer:
not found e sai com 3. Sem vigilância, isso parece idêntico a uma varredura
limpa.path do agentbound é um basename, então uma varredura de diretório sobre arquivos com
o mesmo nome é ambígua.Detecção é a metade fácil. docs/mitigations.md cobre a outra metade: como limitar a autoridade de um agente, com as camadas classificadas por uma pergunta — este controle depende de o modelo escolher obedecer?
Essa classificação é o ponto central. Uma instrução em um prompt é entrada, não uma cláusula de guarda; um padrão de ferramenta que nomeia seu alvo, ou um token que o agente nunca possui, é um limite. O guia desce de "o modelo não pode fazer a coisa errada" até "foi pedido ao modelo que não a fizesse", com uma checklist e a evolução do nnU-Net como o exemplo prático.
fixtures/fixed.yml marca cada uma de suas seis mudanças com a camada que ela implementa
e com se ela é estrutural — duas delas não são, e estão rotuladas
assim para que um leitor não superestime o arquivo.
Buscado em tempo de execução por SHA do commit, nunca vendorizado:
| arquivo | repositório | commit | caminho |
|---|---|---|---|
real/1-vulnerable.yml | MIC-DKFZ/nnUNet | 94300b49e716 | .github/workflows/issue-triage.yml |
real/2-fix-commit.yml | MIC-DKFZ/nnUNet | 4e4770b0b0e6 | .github/workflows/issue-agent.yml |
real/3-later.yml | MIC-DKFZ/nnUNet | 11bd8746fc06 | .github/workflows/issue-agent.yml |
Nenhuma cópia da configuração do nnU-Net é redistribuída aqui; execute novamente o fetch e compare os bytes você mesmo contra os blobs acima.
Este é um artefato de engenharia de detecção. Ele não contém exploit, e nada
aqui foi testado contra um fluxo de trabalho ativo — as revisões vulneráveis são arquivos
estáticos, já públicos no histórico do nnU-Net e referenciados por um advisory
publicado. As fixtures estão marcadas do not deploy porque existem para serem
detectadas, não copiadas.
Se você mantém um detector para esta classe e quer uma linha na tabela, o
script de benchmark é a interface: adicione uma seção que executa sua ferramenta sobre
targets e imprime suas descobertas, e as fixtures e revisões fixadas fazem o
resto.
MIT.