📍 الدليل الرئيسي: Pipeline SAST DAST SCA في 2026: معمارية، أدوات، وتكامل CI/CD
هذا الدرس يُنظِّم في GitLab CI 18 لبنات SAST و SCA و DAST الموصوفة في الدليل الرئيسي.
لماذا تنسيق أمن التطبيقات في GitLab CI
وحّدت GitLab تحت منصة واحدة المستودع Git و runner CI/CD وسجلّ الحاويات وإدارة الثغرات، مما يجعلها بيئة مثالية لتجسيد خط أنابيب DevSecOps من البداية للنهاية. النسخة GitLab 18.8، المتاحة على GitLab.com منذ يناير 2026، و 18.9 لـ Self-Managed منذ فبراير 2026، تُكمل التحسينات التي أدخلتها 18.0 — لا سيما AST_ENABLE_MR_PIPELINES الذي يُفعّل افتراضياً خطوط أنابيب merge request لمسح الأمن.
القوالب الرسمية تغطّي SAST (مُحرَّك بـ Semgrep منذ 2024، بديلاً للمحلّلات اللغوية القديمة)، Secret Detection (مبني على Gitleaks)، Dependency Scanning، Container Scanning (مبني على Trivy في 2026)، Coverage-guided Fuzz Testing، Infrastructure-as-Code Scanning و DAST (مبني على OWASP ZAP، analyzer v4). لوحة Vulnerability Report تُجمِّع النتائج من كل الأدوات، تُكرّر آلياً وتعرض workflow موافقة. يبني هذا الدرس خطوة بخطوة خط أنابيب يجمع SAST + Dependency Scanning + Container Scanning + DAST مع عتبة حجب على الثغرات الجديدة.
المتطلبات المسبقة
- نسخة GitLab 18.8+ (gitlab.com أو Self-Managed) مع runners CI تعمل.
- مشروع يحتوي كوداً تطبيقياً (Python, Node, Java, Go، إلخ) و Dockerfile لـ container scanning.
- ترخيص GitLab Premium أو Ultimate لتفعيل Vulnerability Report وعتبات Merge Request Approval — SAST/Secret Detection متاحة في Free، لكن الفرز المركزي يتطلب Premium على الأقل.
- المستوى المتوقع: الراحة مع ملف
.gitlab-ci.yml، فهم مفهوم stage و job و artefact. - الوقت المقدّر: 60 دقيقة لخط الأنابيب الأدنى، 120 للنسخة مع DAST موثَّق.
الخطوة 1 — تفعيل قوالب الأمن بـ include واحد
أسرع نهج هو تضمين قالب Auto DevOps Security الذي تصونه GitLab. هذا التضمين يُضيف عشرات job أمن في الخلفية، تُنفَّذ آلياً عند توفر الشروط (وجود Dockerfile لـ Container Scanning، manifest اعتماديات لـ Dependency Scanning، إلخ). أنشئ أو حرّر ملف .gitlab-ci.yml في جذر المستودع.
stages:
- test
- build
- scan
- deploy
include:
- template: Security/SAST.gitlab-ci.yml
- template: Security/Secret-Detection.gitlab-ci.yml
- template: Security/Dependency-Scanning.gitlab-ci.yml
- template: Security/Container-Scanning.gitlab-ci.yml
variables:
AST_ENABLE_MR_PIPELINES: "true"
SAST_EXCLUDED_PATHS: "spec, test, tests, tmp, vendor"
المتغير AST_ENABLE_MR_PIPELINES: "true" يضمن تشغيل المسح في خطوط أنابيب merge request، وهو المُحفِّز الموصى به الآن. SAST_EXCLUDED_PATHS يتجنب مسح مجلدات الاختبار التي تحتوي غالباً credentials اختبارية وتُلوّث التقرير. ادفع الـ commit، راقب خط الأنابيب الذي يُشغَّل، ولاحظ في Security & Compliance → Vulnerability Report ظهور النتائج الأولى.
الخطوة 2 — بناء صورة Docker في خط الأنابيب
Container Scanning يحلّل صورة OCI؛ فيجب بناء الصورة قبل خطوة المسح. الممارسة الموصى بها استخدام BuildKit أو Buildah في job مخصص، ثم دفع الصورة إلى GitLab Container Registry المدمج. أضف هذين الـ jobs في stage build.
build_image:
stage: build
image: docker:27-cli
services:
- docker:27-dind
variables:
DOCKER_TLS_CERTDIR: "/certs"
IMAGE_TAG: "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA"
script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
- docker build -t "$IMAGE_TAG" .
- docker push "$IMAGE_TAG"
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
خدمة docker:27-dind توفّر démon Docker-in-Docker المطلوب لـ docker build. متغيرات $CI_REGISTRY_* تُحقَن آلياً من GitLab وتعطي وصولاً إلى registry المشروع بلا سرّ يدوي. علامة الصورة تتضمّن SHA المختصر للـ commit، مما يسمح للـ job التالي بمسح نفس الصورة التي قد تُنشَر. القاعدة rules: تقصر الـ job على MR و push على الفرع الافتراضي، متجنبةً بناءات لا فائدة منها على فروع الميزة.
الخطوة 3 — ربط Container Scanning على الصورة المبنية
قالب Container Scanning في GitLab يستخدم Trivy في 2026. افتراضياً يمسح $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG، لكن يمكن توجيهه صراحةً على الـ tag الثابت المُنتَج في الخطوة السابقة عبر CS_IMAGE.
container_scanning:
stage: scan
variables:
CS_IMAGE: "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA"
CS_SEVERITY_THRESHOLD: "HIGH"
needs:
- build_image
إعلان needs: build_image يسمح بتوازي بقية المسوحات دون انتظار انتهاء build_image لـ SAST أو Dependency Scanning؛ فقط Container Scanning ينتظر الصورة. العتبة CS_SEVERITY_THRESHOLD: "HIGH" تشير إلى أن الـ job يفشل فقط عند العثور على CVE خطورتها ≥ HIGH، لكنه يبقى إعلامياً للـ MEDIUM/LOW. هذه الإستراتيجية تتجنب كسر خط الأنابيب على ضجيج مع حجب الطوارئ الحقيقية.
الخطوة 4 — إضافة DAST على بيئة Review App
DAST يتطلّب تطبيقاً مُنشَراً ومتاحاً. ممارسة GitLab هي ربط DAST ببيئة Review App تُنشَأ ديناميكياً لكل MR: كل MR تنشر preview، يهاجمها DAST، ثم تُهدم البيئة عند إغلاق MR. أضف قالب DAST و job الـ Review App.
include:
- template: Security/DAST.gitlab-ci.yml
review:
stage: deploy
image: alpine:3.20
script:
- apk add --no-cache curl bash openssh-client
- ./scripts/deploy-review.sh "$CI_COMMIT_REF_SLUG"
environment:
name: review/$CI_COMMIT_REF_SLUG
url: https://review-$CI_COMMIT_REF_SLUG.example.com
on_stop: stop_review
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
dast:
stage: scan
variables:
DAST_WEBSITE: "https://review-$CI_COMMIT_REF_SLUG.example.com"
DAST_FULL_SCAN_ENABLED: "false"
needs:
- review
المتغير DAST_FULL_SCAN_ENABLED: "false" يحوّل إلى الوضع السلبي (baseline scan) الذي لا يُرسل payload هجومياً. لتحليل حقيقي، حوّل إلى true على خطوط أنابيب ليلية عبر قاعدة rules: - if: $CI_PIPELINE_SOURCE == "schedule". الوضع النشط مدمّر: مهاجمته في MR تخاطر بكتابة بيانات في القاعدة، لذا فصله في خط أنابيب scheduled خارج MR ممارسة جيدة.
الخطوة 5 — إعداد مصادقة DAST
أغلب الثغرات المهمة توجد خلف المصادقة. ZAP المُشغَّل عبر DAST template يستطيع المصادقة عبر نموذج أو عبر OAuth/OIDC حسب الإعداد. طريقة النموذج هي الأكثر شيوعاً.
dast:
variables:
DAST_WEBSITE: "https://review-$CI_COMMIT_REF_SLUG.example.com"
DAST_AUTH_URL: "https://review-$CI_COMMIT_REF_SLUG.example.com/login"
DAST_USERNAME: "qa@example.com"
DAST_PASSWORD: "$DAST_PASSWORD_SECRET"
DAST_AUTH_USERNAME_FIELD: "id:email"
DAST_AUTH_PASSWORD_FIELD: "id:password"
DAST_AUTH_SUBMIT_FIELD: "css:button[type='submit']"
DAST_AUTH_VERIFICATION_URL: "https://review-$CI_COMMIT_REF_SLUG.example.com/account"
المُحدِّدات (id:email، css:button[type='submit']) تتبع تركيب ZAP-Selector. كلمة المرور مخزّنة كمتغير CI/CD masked + protected في Settings → CI/CD → Variables. يحاول DAST الاتصال، يتحقق من بلوغه DAST_AUTH_VERIFICATION_URL في الوضع المُصادَق، ثم يهاجم الأسطح المحمية. إذا فشلت المصادقة، يشير التقرير بوضوح وتصبح كل النتائج مشبوهة (تغطية ناقصة)؛ تحقق من المُحدِّدات ومن وجود CAPTCHA يحجب.
الخطوة 6 — تعريف العتبات في Merge Request Approval
الهدف النهائي ليس توليد تقارير بل حجب ما يجب حجبه. GitLab Premium يعرض Merge Request Approval Policies في Security & Compliance → Policies. أنشئ سياسة YAML تشترط موافقة إضافية بمجرد ظهور ثغرة حرجة جديدة في MR.
type: scan_result_policy
name: Block critical SAST findings
description: Require security team approval if a CRITICAL SAST finding appears in a MR
enabled: true
rules:
- type: scan_finding
branches: [main]
scanners: [sast, dependency_scanning, container_scanning]
vulnerabilities_allowed: 0
severity_levels: [critical]
vulnerability_states: [newly_detected]
actions:
- type: require_approval
approvals_required: 1
role_approvers: [security_engineer]
القاعدة تنطلق فقط على النتائج newly_detected، مما يتجنب حجب MR بسبب الدين التاريخي القائم على main. المُحفِّز المعتمد على الخطورة (critical) يُبقي الاحتكاك معتدلاً. الدور security_engineer يجب أن يكون موجوداً في المشروع (مُنشأ عبر Settings → Members بـ custom role). عندما تنطلق السياسة، يصبح زر Merge رمادياً ويُسرد تعليق تلقائي النتائج المعنية.
الخطوة 7 — استيراد التقارير في DefectDojo
Vulnerability Report الخاص بـ GitLab يلائم اليومي الداخلي، لكن فريق أمن متعدد الفرق يفضّل غالباً مُجمِّعاً مركزياً. آثار كل مسح متاحة بصيغة GitLab JSON الأصيلة ويمكن تحويلها إلى SARIF ثم استيرادها في DefectDojo. أضف job نهائياً يدفع التقارير.
push_to_defectdojo:
stage: scan
image: python:3.12-slim
needs:
- sast
- dependency_scanning
- container_scanning
script:
- pip install --quiet defectdojo_api_v2
- python ./scripts/push-to-defectdojo.py
variables:
DOJO_URL: "https://defectdojo.example.com"
DOJO_API_KEY: "$DOJO_API_KEY"
DOJO_PRODUCT_ID: "42"
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
سكربت push-to-defectdojo.py يستدعي endpoint /api/v2/import-scan/ مع كل artefact JSON من GitLab باختيار نوع الماسح المناسب (GitLab SAST Report، GitLab Dependency Scanning Report، إلخ). DefectDojo يعرف بشكل أصيل أكثر من 160 صيغة، منها الأصيلة لـ GitLab. القاعدة $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH تتجنب تلويث DefectDojo بنتائج عابرة لفروع الميزات، التي تبقى مرئية فقط في Vulnerability Report لـ GitLab.
الخطوة 8 — تفعيل schedules للمسوحات العميقة
بعض المسوحات (DAST full، Coverage-guided Fuzzing، IaC scanning على كل المستودع) تستغرق 30 إلى 60 دقيقة وليس مكانها في MR. خطوط الأنابيب المُجدوَلة تُنفَّذ بفواصل منتظمة على الفرع الافتراضي، خارج تدفق المطورين. اضبط في CI/CD → Schedules cron يومياً عند 02:00 ومرّر الوضع full في متغير.
dast_full:
extends: dast
variables:
DAST_FULL_SCAN_ENABLED: "true"
DAST_TARGET_AVAILABILITY_TIMEOUT: "600"
rules:
- if: $CI_PIPELINE_SOURCE == "schedule" && $SCHEDULED_JOB == "dast_full"
المتغير SCHEDULED_JOB: dast_full يُعرَّف على مستوى الـ schedule نفسه، مما يسمح بتمييز schedule الـ DAST عن schedule الـ fuzz. هذه التجزئة تُبقي مصفوفة الـ jobs مقروءة وتتجنب أن تدور كل الـ schedules بالتوازي وتُشبع الـ runners. النتيجة تُغذّي نفس Vulnerability Report الخاص بمسوحات MR، مع tag scheduled لتمييز المصادر.
الأخطاء الشائعة
| العَرَض | السبب | الحل |
|---|---|---|
| SAST لا يكتشف أي لغة | المستودع أساساً ملفات إعداد | افرض القائمة بـ SAST_EXCLUDED_ANALYZERS: "" و SAST_DEFAULT_ANALYZERS: "semgrep" |
| Container Scanning يفشل بـ image not found | CS_IMAGE لا يطابق tag المُدفَع فعلاً |
وحّد صراحةً بـ $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA في build و scan |
| مصادقة DAST تفشل | مُحدِّد CSS/ID تغيّر بعد refactor frontend | اختبر المُحدِّدات في DevTools console، استخدم data-testid ثابت في frontend |
| خط أنابيب طويل جداً > 30 دقيقة | كل المسوحات تسلسلياً في MR واحد | استخدم needs: للتوازي، انقل DAST full إلى schedule ليلي |
| Vulnerability Report يعرض نفس CVE مرتين | لا تكرار بين Container Scanning و Dependency Scanning | فعّل SECURE_LOG_LEVEL: "info" واضبط dedup في Settings → Security |
| سياسة MR Approval لا تنطلق أبداً | السياسة مُطبَّقة على main بينما MR تستهدف develop | وسّع branches: [main, develop] في السياسة |
الأسئلة الشائعة
أي قوالب Security مجانية على GitLab Free؟ SAST و Secret Detection متاحتان في Free منذ 2022. Dependency Scanning و Container Scanning و License Compliance و DAST و Vulnerability Report تتطلّب Premium أو Ultimate حسب الميزة. المصفوفة الدقيقة موثّقة في Security & Compliance → Configuration لكل مشروع.
كيف ندمج Trivy بديلاً عن Container Scanning الأصيل؟ عطّل قالب Container Scanning وأضف job trivy يستخدم aquasec/trivy:latest بمخرَج SARIF + GitLab JSON. آثار GitLab JSON مُعترَف بها من Vulnerability Report. انتبه لتثبيت النسخة بعد حادثة مارس 2026: استخدم aquasec/trivy:0.70.0 كحد أدنى، مثالياً عبر digest sha256.
ما مصير AutoDevOps في 2026؟ AutoDevOps يستمر كمُسرِّع (include واحد وكل شيء يعمل)، لكن النهج الموصى به الآن هو تضمين قوالب Security فردياً للاحتفاظ بالسيطرة على المتغيرات. AutoDevOps يبقى نقطة انطلاق جيدة لإثبات القيمة، يُستبدل بإعداد صريح عند اكتساب النضج.
هل يستهلك DAST دقائق CI كثيرة؟ في الوضع السلبي، 5 إلى 10 دقائق لكل MR. في وضع full على تطبيق مُصادَق غير تافه، 30 إلى 90 دقيقة. على GitLab.com مع runners مشتركة، توقّع الأثر على حصة Premium لدقائق CI؛ لـ Self-Managed، احسب حجم runners أو خصّصها للـ scheduled.
كيف نُصدِّر النتائج كـ SBOM CycloneDX؟ job Dependency Scanning يُنتج ملفات gl-sbom-*.cdx.json بصيغة CycloneDX (نسخ 1.4 إلى 1.6 مدعومة). الميزة dependency_scanning_using_sbom_reports رسمية منذ GitLab 17.3 ومُفعّلة افتراضياً منذ 17.5. الـ artefact مُرفق بـ pipeline ويمكن استهلاكه من أي منصة متوافقة.
دروس ذات صلة
- الاستضافة الذاتية لـ SonarQube Community Build 26.4 على VPS Linux — لربط SonarQube مكملاً لـ SAST الأصيل في GitLab عبر تعليق PR.
- حجب PR على الثغرات الجديدة دون عرقلة الفريق — منهجية مطبَّقة على Merge Request Approval Policies.
🔝 العودة إلى الدليل الرئيسي: Pipeline SAST DAST SCA في 2026: معمارية، أدوات، وتكامل CI/CD