
تراكيب GitHub Actions قابلة لإعادة الإنتاج للثغرات والإصلاحات الخاصة بحقن سير العمل الوكيلي (CVE-2026-44246)، مع تغطية الكاشف المقاسة وإرشادات التخفيف.
تجهيزات قابلة لإعادة الإنتاج للنسخ المصابة والمُصلَّحة لفئة الثغرة المنشورة باسم CVE-2026-44246 (nnU-Net، حقن سير العمل الوكيلي، CVSS 7.2، CWE-1427)، بالإضافة إلى المخرجات المقاسة لثلاثة كواشف مقابلها.
وُجد هذا لأنه لم يكن هناك شيء لاختبار الكاشف مقابله. كتابة قاعدة لهذه الفئة تعني كتابة تجهيز يدويًا والأمل في أنه دقيق؛ لم تكن المراجعات المنشورة متاحة إلا بمعرفة أي commit يجب النظر إليه، والذي تبيّن أنه الجزء المثير للاهتمام.
يمنح سير عمل GitHub Actions نصًا غير موثوق لوكيل ذكاء اصطناعي يمتلك بيانات اعتماد المستودع.
أربعة شروط، جميعها ضرورية:
issues، issue_comment، pull_request_review —
أحداث يمكن لأي شخص يمتلك حساب GitHub أن يكتب نصها.issues: write، contents: write، وما إلى ذلك.claude-code-action و
فاعل تشغيل بدون صلاحية كتابة إلا إذا قام مُدخل
(، ) بتأهيله صراحةً. بدون ذلك المُدخل
لا يصل مؤلف غير موثوق أبدًا إلى الوكيل، ويكون سير العمل هو التكوين الآمن بدلًا
من هذا التكوين.codex-actionallowed_non_write_usersallow-users${{ github.event.issue.body }} داخل prompt:) أو مُجلبًا وقت التشغيل
(gh issue view) بالرمز الذي تسلّمه المهمة.إنه ليس حقن قوالب. لا يُقيَّم أي شيء كـ shell؛ الحمولة نصية، والمُفسِّر هو النموذج.
لهذا السبب لا تنطبق النصيحة المعتادة — ضع متغيراتك بين علامات اقتباس، لا تستخدم
eval — ولهذا فإن الإصلاح أدناه يتعلق بما يمكن للوكيل الوصول إليه بدلًا من
التهريب من المدخلات.
حصّنت nnU-Net سير العمل هذا في اثنين من الـ commits، والكاشف الذي يتعامل مع "قبل" و"بعد" كحالة ثنائية يُخطئ في الوسط.
| المراجعة | commit | التاريخ | --allowedTools الخاصة بالوكيل | الحكم |
|---|---|---|---|---|
| مصابة | 94300b49e716 | 2026-04-13 | gh issue comment، gh issue edit | كتابة قابلة للوصول |
| "الإصلاح" | 4e4770b0b0e6 | 2026-04-24 | gh issue comment فقط؛ نُقل وضع التسميات إلى سكربت غلاف | لا تزال الكتابة قابلة للوصول |
| لاحقًا | 11bd8746fc06 | 2026-04-27 | لا هذا ولا ذاك؛ خطوة لاحقة تنشر من ملف يكتبه الوكيل | غير قابلة للوصول |
الـ commit الذي سيسميه الجميع الإصلاح — رسالته هي "hardened issue and PR agents" —
أزال gh issue edit ووجّه التسميات عبر .github/scripts/safe-label.sh، لكنه
ترك Bash(gh issue comment:*) مع الوكيل. كان لا يزال بالإمكان توجيه الوكيل
للتعليق، ونمط قائمة السماح gh issue comment:* غير محصور بالمشكلة المُشغِّلة، لذا
كان الهدف بيد النموذج ليختاره. فقط الـ commit الثالث أغلقه: الآن يكتب الوكيل
/tmp/issue-comment.md وخطوة لاحقة غير وكيلية تنشره بـ
ISSUE: ${{ github.event.issue.number }} مأخوذًا من الحدث.
يترتب على ذلك أمران، وهما سبب كون هذا المستودع ليس مجرد ملفين:
"مُصلَّح" ادعاء عن مراجعة، وليس عن إصدار. يُستشهد بـ v2.4.1 كالإصدار
المُصلَّح، لكن .github/workflows/ الخاص به يحتوي فقط على codespell.yml —
سير عمل الوكيل غير موجود في ذلك الوسم إطلاقًا. لا يمكنك التحقق من الإصلاح من
الوسم؛ عليك تثبيت الـ commit.
كتلة الصلاحيات لا تتغير أبدًا. issues: write موجودة وصحيحة في المراجعات
الثلاث، بما في ذلك الأخيرة، لأن خطوة لاحقة تحتاجها. الكاشف الذي يعتمد على
permissions وحده لا يمكنه فصل المراجعة 1 عن المراجعة 3. ما يفصل بينهما هو ما
يجوز للوكيل استدعاؤه.
ثلاثة كواشف، شُغّلت على كلا التجهيزين وعلى المراجعات الحقيقية الثلاث. المخرجات الخام الكاملة وإصدارات الأدوات في results.md.
| المراجعة | 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 | — (عام فقط) | — |
3-later | LOW write-scope، CRITICAL author-association | ai-action-excessive-tools، ai-action-execution-order | — |
لا يوجد كاشف "مخطئ" ببساطة هنا — إنهم يجيبون على أسئلة مختلفة:
ai-action-prompt-injection يُطلق على المراجعة
المصابة، حيث يُدرج نص المشكلة في prompt:، ويتوقف بمجرد اختفاء الإدراج. صحيح.
نتائجه على المراجعة اللاحقة تستحق القراءة قبل التصرف بناءً عليها —
ai-action-excessive-tools يشير إلى Write، الذي يستخدمه سير العمل هذا عن
قصد لكتابة الملف الذي تنشره خطوة لاحقة؛ وai-action-execution-order يريد
الوكيل أخيرًا، وهو بالضبط ما يتجنبه هذا التصميم، لأن الخطوات ذات الصلاحيات
تأتي بعد انتهاء دور النموذج.permissions الخاصة بالمهمة. نتيجته
ci-agent-missing-author-association مُبلَّغ عنها بـ critical على المراجعة
اللاحقة، ويصف README الخاص به ذلك بأنه مفرط في الشدة بدلًا من كونه خاطئًا:
auto-triage مقصود أن يكون قابلًا للوصول من قبل أي شخص.كل أداة هنا لديها نتيجة تبلّغ عنها على مراجعة كان مؤلفها قد فكّر بالفعل في ذلك الشرط بالضبط. هذه هي الحالة الطبيعية لأي استدلال، وهي السبب في أن جدول التغطية أكثر فائدة من عمود نجاح/فشل.
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 عن الكاشف المفقود كمفقود بدلًا من تخطيه بصمت، لأن جدول
تغطية يحذف أداة بهدوء لا يثبت شيئًا عنها.
التجهيزات هي الشكل المُختصر، مكتوبة لهذا المستودع ومرخّصة بـ MIT مثل بقية
المستودع. إنها وفية للآلية بدلًا من كونها نسخًا: fixtures/vulnerable.yml يحمل
الشروط الأربعة ولا شيء غيرها، وfixtures/fixed.yml هو الملف نفسه مع التغييرات
الستة المهمة، كل منها مُعلَّق. إذا كنت تضيف قاعدة، فهذان الملفان هما أصغر
المُدخلات التي يجب أن تتعامل معها بشكل صحيح.
خصوصيتان في الواجهة، مُعالَجتان في السكربت لكن يجدر معرفتهما:
not found ويخرج بـ 3. بدون مراقبة، يبدو ذلك مطابقًا لفحص نظيف.path في JSON الخاص بـ agentbound هو اسم أساسي، لذا فإن فحص دليل
لملفات تحمل الاسم نفسه يكون غامضًا.الكشف هو النصف السهل. docs/mitigations.md يغطي النصف الآخر: كيفية تحديد سلطة الوكيل، مع ترتيب الطبقات حسب سؤال واحد — هل يعتمد هذا الضابط على اختيار النموذج للامتثال؟
هذا الترتيب هو بيت القصيد. التعليمات في الـ prompt هي مُدخل، وليست شرط حماية؛ نمط أداة يسمّي هدفه، أو رمز لا يحمله الوكيل أبدًا، هو حد. يعمل الدليل نزولًا من "لا يمكن للنموذج فعل الشيء الخاطئ" إلى "طُلب من النموذج ألا يفعل"، مع قائمة تحقق وتطور nnU-Net كمثال عملي.
يوسم fixtures/fixed.yml كلًا من تغييراته الستة بالطبقة التي ينفذها وبما إذا
كان حاملًا للحمل — اثنان منها ليسا كذلك، وموسومان بهذه الطريقة حتى لا يبالغ
القارئ في تقدير الملف.
يُجلب وقت التشغيل بواسطة commit SHA، ولا يُضمَّن أبدًا:
| الملف | المستودع | commit | المسار |
|---|---|---|---|
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 هنا؛ أعد تشغيل الجلب وقارن البايتات بنفسك مقابل الـ blobs أعلاه.
هذا عمل هندسي للكشف. لا يحتوي على أي استغلال، ولم يُختبر أي شيء هنا مقابل سير
عمل حي — المراجعات المصابة هي ملفات ثابتة، منشورة بالفعل في تاريخ nnU-Net
ومُشار إليها في إشعار منشور. التجهيزات موسومة بـ do not deploy لأنها موجودة
ليُكشف عنها، لا لتُنسخ.
إذا كنت تصون كاشفًا لهذه الفئة وتريد صفًا في الجدول، فسكربت المعيار هو الواجهة:
أضف قسمًا يشغّل أداتك على targets ويطبع نتائجه، والتجهيزات والمراجعات المثبتة
تفعل الباقي.
MIT.