
एजेंटिक वर्कफ़्लो इंजेक्शन (CVE-2026-44246) के लिए पुनरुत्पादनीय संवेदनशील और फ़िक्स्ड GitHub Actions फ़िक्स्चर, मापे गए डिटेक्टर कवरेज और शमन मार्गदर्शन के साथ।
CVE-2026-44246 (nnU-Net, एजेंटिक वर्कफ़्लो इंजेक्शन, CVSS 7.2, CWE-1427) के रूप में प्रकाशित बग वर्ग के लिए पुनरुत्पादनीय vulnerable/fixed फ़िक्स्चर, साथ ही उनके विरुद्ध तीन डिटेक्टरों का मापा गया आउटपुट।
यह इसलिए मौजूद है क्योंकि किसी डिटेक्टर को परखने के लिए कुछ नहीं था। इस वर्ग के लिए नियम लिखने का मतलब है हाथ से एक फ़िक्स्चर लिखना और उम्मीद करना कि वह faithful है; प्रकाशित revisions केवल यह जानकर उपलब्ध थे कि किस commit को देखना है, जो कि दिलचस्प हिस्सा निकला।
एक GitHub Actions वर्कफ़्लो अविश्वसनीय टेक्स्ट को एक AI एजेंट को सौंपता है जिसके पास रिपॉज़िटरी की credentials होती हैं।
चार शर्तें, सभी आवश्यक:
issues, issue_comment, pull_request_review —
ऐसे events जिनका टेक्स्ट GitHub अकाउंट रखने वाला कोई भी लिख सकता है।issues: write, contents: write, इत्यादि।claude-code-actioncodex-actionallowed_non_write_usersallow-usersprompt: के अंदर ${{ github.event.issue.body }}) या run time पर fetch किया गया
(gh issue view) उस token के साथ जो जॉब सौंपता है।यह template injection नहीं है। कुछ भी shell के रूप में evaluate नहीं होता; payload गद्य है,
और interpreter मॉडल है। इसीलिए सामान्य सलाह — अपने variables को quote करें, eval न करें —
लागू नहीं होती, और इसीलिए नीचे दिया गया fix input को escape करने के बारे में नहीं बल्कि इस
बारे में है कि एजेंट क्या reach कर सकता है।
nnU-Net ने इस वर्कफ़्लो को दो commits में harden किया, और जो डिटेक्टर "before" और "after" को binary मानता है वह बीच वाले को गलत आँकता है।
| revision | commit | date | agent's --allowedTools | verdict |
|---|---|---|---|---|
| vulnerable | 94300b49e716 | 2026-04-13 | gh issue comment, gh issue edit | reachable write |
| "the fix" | 4e4770b0b0e6 | 2026-04-24 | gh issue comment only; labelling moved to a wrapper script | still reachable write |
| later | 11bd8746fc06 | 2026-04-27 | neither; a later step posts from a file the agent writes | not reachable |
जिस commit को हर कोई fix कहेगा — उसका message है "hardened issue
and PR agents" — उसने gh issue edit हटाया और labels को
.github/scripts/safe-label.sh के माध्यम से भेजा, लेकिन एजेंट के पास
Bash(gh issue comment:*) छोड़ दिया। एजेंट को अब भी comment करने के लिए steer किया जा सकता
था, और allowlist pattern gh issue comment:* triggering issue तक सीमित नहीं है, इसलिए
target मॉडल का चुनाव था। केवल तीसरे commit ने इसे बंद किया: एजेंट अब
/tmp/issue-comment.md लिखता है और बाद का, गैर-एजेंट step उसे
ISSUE: ${{ github.event.issue.number }} के साथ post करता है जो event से लिया गया है।
इससे दो बातें निकलती हैं, और यही कारण है कि यह रिपॉज़िटरी केवल दो फ़ाइलें नहीं है:
"Fixed" एक revision के बारे में दावा है, version के बारे में नहीं। v2.4.1 को
fixed release के रूप में उद्धृत किया जाता है, लेकिन उसके .github/workflows/ में केवल
codespell.yml है — agent workflows उस tag में बिल्कुल नहीं हैं। आप tag से fix verify
नहीं कर सकते; आपको commit pin करना होगा।
permissions block कभी नहीं बदलता। issues: write तीनों revisions में मौजूद और सही है,
अंतिम सहित, क्योंकि बाद के एक step को इसकी आवश्यकता है। जो डिटेक्टर केवल permissions पर
key करता है वह revision 1 को revision 3 से अलग नहीं कर सकता। उन्हें अलग करने वाली बात यह है
कि agent क्या call कर सकता है।
तीन डिटेक्टर, दोनों fixtures और तीनों वास्तविक revisions पर चलाए गए। पूरा raw output और tool versions 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 | — (generic only) | — |
3-later | LOW write-scope, CRITICAL author-association | ai-action-excessive-tools, ai-action-execution-order | — |
यहाँ कोई भी डिटेक्टर सीधे "गलत" नहीं है — वे अलग-अलग प्रश्नों का उत्तर देते हैं:
ai-action-prompt-injection vulnerable revision पर fire
होता है, जहाँ issue body prompt: में interpolate होता है, और interpolation हटते ही
रुक जाता है। सही। later revision पर इसके findings पर कार्रवाई करने से पहले उन्हें पढ़ना
उचित है — ai-action-excessive-tools Write को flag करता है, जिसे यह वर्कफ़्लो जानबूझकर
उस फ़ाइल को लिखने के लिए उपयोग करता है जिसे बाद का step post करता है; और
ai-action-execution-order एजेंट को अंत में चाहता है, जिससे यह डिज़ाइन बचता है, क्योंकि
privileged steps मॉडल की बारी समाप्त होने के बाद आते हैं।permissions के बजाय एजेंट की tool allowlist पढ़कर। इसका
ci-agent-missing-author-association finding later revision पर critical पर रिपोर्ट
होता है और इसका अपना README इसे false के बजाय over-severe बताता है: auto-triage का
मतलब है कि कोई भी इसे reach कर सके।यहाँ हर tool का कोई न कोई finding ऐसी revision पर है जिसके लेखक ने उस सटीक शर्त पर पहले ही विचार कर लिया था। यह एक heuristic की सामान्य स्थिति है, और यही कारण है कि coverage table pass/fail column से अधिक उपयोगी है।
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 किसी अनुपस्थित डिटेक्टर को चुपचाप skip करने के बजाय अनुपस्थित बताता है,
क्योंकि जो coverage table किसी tool को चुपचाप छोड़ दे वह उसके बारे में कुछ सिद्ध नहीं करता।
fixtures घटाया गया रूप हैं, इस रिपॉज़िटरी के लिए लिखे गए और बाकी की तरह MIT-licensed।
वे प्रतियाँ नहीं बल्कि mechanism-faithful हैं:
fixtures/vulnerable.yml में चारों शर्तें हैं और कुछ नहीं, और
fixtures/fixed.yml वही फ़ाइल है उन छह बदलावों के साथ जो मायने रखते हैं, प्रत्येक annotated।
यदि आप कोई rule जोड़ रहे हैं, तो वे दो फ़ाइलें सबसे छोटे inputs हैं जिन्हें उसे सही पाना है।
दो interface quirks, script में handled लेकिन जानने लायक:
not found प्रिंट करता है और 3 पर exit करता है। बिना ध्यान दिए, यह एक
clean scan जैसा ही दिखता है।path एक basename है, इसलिए समान नाम वाली फ़ाइलों पर directory
scan अस्पष्ट हो जाता है।Detection आसान आधा है। docs/mitigations.md दूसरे आधे को cover करता है: एजेंट के authority को कैसे सीमित करें, परतों को एक प्रश्न से ranked करते हुए — क्या यह नियंत्रण इस बात पर निर्भर करता है कि मॉडल अनुपालन करना चुने?
यही ranking पूरा मुद्दा है। prompt में एक instruction input है, guard clause नहीं; कोई tool pattern जो अपने target का नाम लेता है, या कोई token जो एजेंट के पास कभी नहीं होता, एक bound है। यह गाइड "मॉडल गलत काम नहीं कर सकता" से "मॉडल से गलत काम न करने को कहा गया था" तक नीचे जाती है, एक checklist और nnU-Net evolution को worked example के रूप में लेकर।
fixtures/fixed.yml अपने छह बदलावों में से प्रत्येक को उस layer के साथ tag करता है जिसे वह
implement करता है और इसके साथ कि क्या वह load-bearing है — उनमें से दो नहीं हैं, और उसी
तरह labelled हैं ताकि पाठक फ़ाइल को अधिक न आँके।
Run time पर commit SHA द्वारा fetched, कभी vendored नहीं:
| file | repository | commit | path |
|---|---|---|---|
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 |
nnU-Net के configuration की कोई प्रति यहाँ redistribute नहीं की गई है; fetch दोबारा चलाएँ और ऊपर दिए गए blobs के विरुद्ध bytes की स्वयं तुलना करें।
यह एक detection-engineering artifact है। इसमें कोई exploit नहीं है, और यहाँ कुछ भी किसी live
workflow के विरुद्ध परखा नहीं गया — vulnerable revisions static फ़ाइलें हैं, जो nnU-Net के
history में पहले से सार्वजनिक हैं और एक प्रकाशित advisory द्वारा संदर्भित हैं। fixtures को
do not deploy चिह्नित किया गया है क्योंकि वे detect होने के लिए हैं, copy होने के लिए नहीं।
यदि आप इस वर्ग के लिए कोई डिटेक्टर maintain करते हैं और table में एक row चाहते हैं, तो
benchmark script ही interface है: एक section जोड़ें जो आपके tool को targets पर चलाए और
उसके findings प्रिंट करे, और बाकी काम fixtures और pinned revisions कर देंगे।
MIT।