المستوى المطلوب: محترف (Senior DevOps / Platform Engineer)
لو الـ CI/CD pipeline بتاعك بيستخدم actions/checkout@v4 أو uses: tj-actions/changed-files@v45، أنت ضمنيًا بتثق إن صاحب الـ tag مش هيغيّر اللي تحته. الـ tag في GitHub mutable، والافتراض ده اتكسر في حادثة فعلية يوم 14 مارس 2025.
SHA Pinning في GitHub Actions: الحماية الحقيقية لـ Supply Chain
action شائع اسمه tj-actions/changed-files اتخترق، والـ tags كلها (من v1 لـ v45) ابتدت تطبع GITHUB_TOKEN، AWS keys، وnpm tokens في اللوج. أكتر من 23,000 workflow public اتأثروا في أقل من 12 ساعة حسب StepSecurity advisory. الـ workflows اللي كانت بتستخدم commit SHA ثابت بدل tag ما اتأثرتش — لأن الـ SHA بيقفل النسخة عند نقطة معروفة قبل الاختراق.
المشكلة باختصار
السطر uses: owner/action@v3 مش immutable. مالك الـ repo (أو حد اخترق حسابه) يقدر يحرّك الـ tag لأي commit في أي وقت. السطر اللي عندك في الـ workflow ما يتغيّرش بصريًا، لكن الكود اللي بيتنفّذ في الـ runner بيتغيّر فعليًا. النتيجة: arbitrary code execution بصلاحيات الـ GITHUB_TOKEN ووصول كامل لكل secret بتقرأه الخطوة.
تخيّلها بمثال بسيط الأول
تخيّل إنك بتشتري قهوة من نفس الكافيه كل صبح، واللافتة على باب المحل بتقول "كافيه فلان". فجأة المالك بدّل اللافتة على محل تاني، فيه شخص بيدّيك مشروب فيه مادة غريبة. أنت لسه ماشي على نفس اسم اللافتة، لكن داخلك حاجة تانية بالكامل.
الـ tag في GitHub زي اللافتة — اسم بشري قابل للتحريك. الـ commit SHA زي بصمة المحل نفسه: 40 حرف بيعرّفوا نسخة الكود بالظبط، وما يتغيّروش طول ما الـ git history سليم. لو ربطت اللافتة بالبصمة، بقى عندك دليل لو حد لعب.
التعريف العلمي
الـ commit SHA-1 في git هو hash بطول 40 hex character (160 bit)، بيتحسب على content الـ commit بالكامل: tree object الجذر + parent SHAs + author + committer + timestamp + message. أي تعديل ولو حرف واحد بيغيّر الـ hash كله بسبب خاصية avalanche في دوال الـ hashing.
GitHub بيقبل تشغيل action عند أي ref: branch, tag, أو commit_sha. الأولين mutable. الأخير immutable طول ما الـ commit موجود في الـ repo. لما تكتب uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab، GitHub بيـ checkout الكود اللي عند الـ commit ده بالظبط، حتى لو الـ tag v4 اتنقل بكره لـ commit مختلف.
الـ workflow الصحيح: pin + comment + automate
- اقرأ الـ commit SHA الحالي للـ tag اللي بتستخدمه
- استبدل الـ tag بـ SHA كامل، وحط comment فيه الـ version للقراءة البشرية
- شغّل Renovate أو Dependabot يقترح SHAs الجديدة بصيغة PR، ما تعملش الترقية يدوي
- راجع كل PR ترقية action ولو ثانية واحدة قبل ما تـ merge