
Reproduzierbare verwundbare und korrigierte GitHub-Actions-Fixtures für agentische Workflow-Injection (CVE-2026-44246), mit gemessener Detektorabdeckung und Mitigationsanleitung.
Reproduzierbare verwundbare/gefixte Fixtures für die Fehlerklasse, die als CVE-2026-44246 veröffentlicht wurde (nnU-Net, agentische Workflow-Injektion, CVSS 7.2, CWE-1427), plus die gemessene Ausgabe von drei Detektoren gegen diese.
Es existiert, weil es nichts gab, wogegen man einen Detektor testen konnte. Eine Regel für diese Klasse zu schreiben bedeutet, ein Fixture von Hand zu schreiben und zu hoffen, dass es originalgetreu ist; die veröffentlichten Revisionen waren nur verfügbar, wenn man wusste, welchen Commit man sich ansehen musste, was sich als der interessante Teil herausstellte.
Ein GitHub-Actions-Workflow übergibt nicht vertrauenswürdigen Text an einen KI-Agenten, der über die Anmeldedaten des Repositorys verfügt.
Vier Bedingungen, alle notwendig:
issues, issue_comment, pull_request_review —
Ereignisse, deren Text jeder mit einem GitHub-Konto verfassen kann.issues: write, und so weiter.contents: writeclaude-code-action und
codex-action lehnen einen Run-Actor ohne Schreibberechtigung ab, es sei denn, ein Input
(allowed_non_write_users, allow-users) meldet ihn explizit an. Ohne
diesen Input erreicht ein nicht vertrauenswürdiger Autor den Agenten nie, und der Workflow ist
die sichere Konfiguration statt dieser.${{ github.event.issue.body }} innerhalb von prompt:) oder zur Laufzeit abgerufen
(gh issue view) mit dem Token, das der Job übergibt.Es ist keine Template-Injektion. Nichts wird als Shell ausgewertet; die Nutzlast ist
Prosa, und der Interpreter ist das Modell. Deshalb gilt der übliche Rat — quoten Sie
Ihre Variablen, verwenden Sie kein eval — nicht, und deshalb geht es beim Fix unten darum,
was der Agent erreichen kann, statt um das Escapen von Eingaben.
nnU-Net hat diesen Workflow in zwei Commits gehärtet, und ein Detektor, der „vorher" und „nachher" als binär behandelt, bekommt den mittleren falsch.
| Revision | Commit | Datum | --allowedTools des Agenten | Urteil |
|---|---|---|---|---|
| verwundbar | 94300b49e716 | 2026-04-13 | gh issue comment, gh issue edit | erreichbarer Schreibzugriff |
| „der Fix" | 4e4770b0b0e6 | 2026-04-24 | nur gh issue comment; Labeling in ein Wrapper-Skript verschoben | weiterhin erreichbarer Schreibzugriff |
| später | 11bd8746fc06 | 2026-04-27 | keines von beiden; ein späterer Schritt postet aus einer Datei, die der Agent schreibt | nicht erreichbar |
Der Commit, den jeder als den Fix bezeichnen würde — seine Nachricht lautet „hardened issue
and PR agents" — entfernte gh issue edit und leitete Labels durch
.github/scripts/safe-label.sh, ließ aber Bash(gh issue comment:*) beim
Agenten. Der Agent konnte weiterhin dazu gesteuert werden, zu kommentieren, und das Allowlist-
Muster gh issue comment:* ist nicht auf das auslösende Issue beschränkt, sodass das
Ziel vom Modell gewählt werden konnte. Nur der dritte Commit schloss es: Der Agent schreibt jetzt
/tmp/issue-comment.md und ein späterer, Nicht-Agent-Schritt postet es mit
ISSUE: ${{ github.event.issue.number }} aus dem Ereignis.
Daraus folgen zwei Dinge, und sie sind der Grund, warum dieses Repository nicht nur zwei Dateien umfasst:
„Gefixt" ist eine Aussage über eine Revision, nicht über eine Version. v2.4.1 wird als
gefixte Release zitiert, aber sein .github/workflows/ enthält nur codespell.yml — die
Agent-Workflows sind in diesem Tag überhaupt nicht enthalten. Sie können den Fix nicht vom
Tag aus verifizieren; Sie müssen den Commit pinnen.
Der Permissions-Block ändert sich nie. issues: write ist in allen drei Revisionen vorhanden und korrekt,
einschließlich der letzten, weil ein späterer Schritt es benötigt. Ein
Detektor, der allein auf permissions abzielt, kann Revision 1 nicht von
Revision 3 unterscheiden. Was sie unterscheidet, ist, was der Agent aufrufen darf.
Drei Detektoren, ausgeführt über beide Fixtures und alle drei echten Revisionen. Vollständige Roh- Ausgabe und Tool-Versionen in results.md.
| Revision | 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 | — (nur generisch) | — |
3-later | LOW write-scope, CRITICAL author-association | ai-action-excessive-tools, ai-action-execution-order | — |
Kein Detektor ist hier einfach „falsch" — sie beantworten unterschiedliche Fragen:
ai-action-prompt-injection schlägt bei der verwundbaren
Revision an, wo der Issue-Body in prompt: interpoliert wird, und hört auf, sobald
die Interpolation verschwunden ist. Korrekt. Seine Befunde zur späteren Revision sind
es wert, gelesen zu werden, bevor man darauf reagiert — ai-action-excessive-tools markiert
Write, das dieser Workflow absichtlich verwendet, um die Datei zu schreiben, die ein späterer
Schritt postet; und ai-action-execution-order will den Agenten zuletzt, was genau das ist, was dieses Design vermeidet, da die privilegierten Schritte nach dem Ende des Modell-Durchlaufs kommen.permissions des Jobs. Sein
ci-agent-missing-author-association-Befund wird bei der
späteren Revision als critical gemeldet, und sein eigenes README beschreibt das als übermäßig streng statt
falsch: auto-triage soll für jeden erreichbar sein.Jedes Tool hier hat einen Befund, den es bei einer Revision meldet, deren Autor bereits über genau diese Bedingung nachgedacht hatte. Das ist der normale Zustand einer Heuristik, und der Grund, warum eine Coverage-Tabelle nützlicher ist als eine Bestanden/Durchgefallen-Spalte.
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 meldet einen fehlenden Detektor als fehlend, statt ihn stillschweigend zu
überspringen, weil eine Coverage-Tabelle, die ein Tool stillschweigend auslässt, nichts über es beweist.
Die Fixtures sind die reduzierte Form, für dieses Repository geschrieben und MIT-lizenziert
wie der Rest davon. Sie sind mechanismustreu statt Kopien:
fixtures/vulnerable.yml trägt die vier Bedingungen und nichts weiter, und
fixtures/fixed.yml ist dieselbe Datei mit den sechs Änderungen, die zählen, jede
annotiert. Wenn Sie eine Regel hinzufügen, sind diese beiden Dateien die kleinsten Eingaben, die
sie richtig hinbekommen muss.
Zwei Interface-Eigenheiten, im Skript behandelt, aber wissenswert:
not found aus und beendet mit 3. Unbeobachtet sieht das identisch zu einem sauberen
Scan aus.path ist ein Basename, sodass ein Verzeichnis-Scan über Dateien mit
demselben Namen mehrdeutig ist.Erkennung ist die leichte Hälfte. docs/mitigations.md behandelt die andere Hälfte: wie man die Autorität eines Agenten begrenzt, mit den Schichten geordnet nach einer Frage — hängt diese Kontrolle davon ab, dass das Modell sich entscheidet, zu gehorchen?
Diese Rangfolge ist der ganze Punkt. Eine Anweisung in einem Prompt ist Eingabe, keine Guard- Klausel; ein Tool-Muster, das sein Ziel benennt, oder ein Token, das der Agent nie hält, ist eine Grenze. Der Leitfaden arbeitet sich von „das Modell kann nicht das Falsche tun" herunter zu „das Modell wurde gebeten, es nicht zu tun", mit einer Checkliste und der nnU-Net-Evolution als durchgearbeitetem Beispiel.
fixtures/fixed.yml versieht jede seiner sechs Änderungen mit einem Tag für die Schicht, die sie implementiert,
und damit, ob sie tragend ist — zwei von ihnen sind es nicht, und sind so gekennzeichnet,
damit ein Leser die Datei nicht überschätzt.
Zur Laufzeit per Commit-SHA abgerufen, nie mitgeliefert:
| Datei | Repository | Commit | Pfad |
|---|---|---|---|
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 |
Es wird hier keine Kopie von nnU-Nets Konfiguration weiterverbreitet; führen Sie den Abruf erneut aus und vergleichen Sie die Bytes selbst mit den oben genannten Blobs.
Dies ist ein Artefakt der Erkennungstechnik. Es enthält keinen Exploit, und nichts
hier wurde gegen einen Live-Workflow getestet — die verwundbaren Revisionen sind statische
Dateien, bereits öffentlich in nnU-Nets Historie und referenziert durch eine veröffentlichte
Advisory. Die Fixtures sind mit do not deploy gekennzeichnet, weil sie existieren, um
erkannt zu werden, nicht um kopiert zu werden.
Wenn Sie einen Detektor für diese Klasse pflegen und eine Zeile in der Tabelle möchten, ist das
Benchmark-Skript die Schnittstelle: Fügen Sie einen Abschnitt hinzu, der Ihr Tool über
targets ausführt und seine Befunde ausgibt, und die Fixtures und gepinnten Revisionen erledigen den
Rest.
MIT.