CI/CD Integration
أدلة
التكامل مع CI/CD
تقبل كل نقطة طرفية للماسح مفتاح API، لذا يمكن تشغيل أيٍّ منها كخطوة ضمن خط الأنابيب. تتناول هذه الصفحة معنى مخرجات كل أداة، وكيفية تحويلها إلى بوابة نجاح/فشل، وأمثلة عملية لأربع منصات CI.
فحص الكود باستخدام GitHub Action
أسرع طريقة لفحص الكود في CI. يشغّل @scanverra/cli على المشغّل (ولا يغادره الكود أبدًا)، ويوقف المهمة عند درجة الخطورة التي تختارها، ويرفع SARIF إلى فحص الكود في GitHub.
on: [push, pull_request]
jobs:
scanverra:
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@v4
- uses: scanverra/scan-action@v1
with:
api-key: ${{ secrets.SCANVERRA_API_KEY }}
fail-on: highأساس احتساب النتائج لكل أداة
ما تُعيده كل نقطة طرفية، وما الذي ينبغي تحديد حد أدنى له.
| الأداة | المخرجات | الأساس | بوابة مدمجة |
|---|---|---|---|
| تدقيق الموقع الإلكتروني | 4 نتائج من 0 إلى 100: performance وseo وaccessibility وbestPractices | درجات الفئات من 0 إلى 100. العُرف المتبع: ≥90 جيد، 50-89 بحاجة إلى تحسين، <50 ضعيف. | لا توجد بوابة مدمجة — اختر الحد الأدنى الخاص بك لكل نتيجة. |
| فحص الأمان | riskScore من 0 إلى 100 (كلما ارتفعت القيمة، زادت السلامة) | 100 مطروحًا منها خصومات مرجّحة حسب الشدة ومحدودة لكل فئة. درجات التقييم: A+ 90 فأكثر … F أقل من 30. | لا توجد بوابة مدمجة — اختر الحد الأدنى الخاص بك. |
| تدقيق المتصفح | مصفوفات: jsErrors وbrokenLinks وformIssues وimgIssues (لكل منها severity: "error" | "warning") | لا توجد نتيجة إجمالية. | بوابة تعتمد على عدد المشكلات، مثل وجود أي severity === "error". |
| ماسح المستودعات | score من 0 إلى 100 + qualityGate: "passed" | "failed" | 100 مطروحًا منها خصومات مرجّحة حسب الشدة، محدودة لكل فئة. | بوابة مدمجة — تفشل تلقائيًا عند وجود أي مشكلة حرجة أو نتيجة أقل من 60. |
| محسّن السيو | score من 0 إلى 100 (سيو داخل الصفحة + إشارات GEO/E-E-A-T) | 100 مطروحًا منها خصومات مرجّحة حسب الشدة، محدودة لكل فئة، عبر ملاحظات الصفحة وGEO وE-E-A-T. | غير مدمجة — حدّد حدك الخاص. |
إيقاف خط الأنابيب
آلية عمل CI واحدة بغض النظر عن المنصة: أي خطوة تخرج برمز غير صفري تُفشل المهمة. لمنع طلب سحب (PR) أو طلب دمج فعليًا من الدمج، أضف هذه المهمة كـ فحص مطلوب/إلزامي ضمن إعدادات حماية الفرع أو موافقات طلبات الدمج في مضيف المستودع لديك — فشل خطوة واحدة بمفرده يظهر باللون الأحمر فقط، ولا يمنع الدمج من تلقاء نفسه.
أمثلة على خطوط الأنابيب
الفحوصات الخمسة نفسها — التدقيق، وفحص الأمان، وتدقيق المتصفح، وفحص المستودع، ومحسّن السيو — مكتوبة لأربع منصات. اختر نظام CI الخاص بك أدناه.
name: Scanverra checks
on: [pull_request]
jobs:
scanverra:
runs-on: ubuntu-latest
env:
API_KEY: ${{ secrets.SCANVERRA_API_KEY }}
TARGET_URL: https://staging.example.com # your deployed preview URL
steps:
- name: Website Audit (perf/SEO/a11y)
run: |
RES=$(curl -sf -X POST https://www.scanverra.com/api/audit \
-H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
-d "{\"url\":\"$TARGET_URL\"}")
echo "$RES" | jq .
PERF=$(echo "$RES" | jq '.performanceScore')
A11Y=$(echo "$RES" | jq '.accessibilityScore')
if (( $(echo "$PERF < 50" | bc -l) )) || (( $(echo "$A11Y < 90" | bc -l) )); then
echo "::error::Audit failed thresholds (perf=$PERF, a11y=$A11Y)"
exit 1
fi
- name: Security Scan
run: |
SCAN_ID=$(curl -sf -X POST https://www.scanverra.com/api/security-scan \
-H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
-d "{\"url\":\"$TARGET_URL\"}" | jq -r .scanId)
for i in $(seq 1 30); do
RESULT=$(curl -sf "https://www.scanverra.com/api/security-scan?id=$SCAN_ID" -H "X-API-Key: $API_KEY")
STATUS=$(echo "$RESULT" | jq -r .status)
[ "$STATUS" != "running" ] && break
sleep 10
done
RISK=$(echo "$RESULT" | jq '.riskScore')
echo "riskScore=$RISK"
[ "$STATUS" = "failed" ] && { echo "::error::Security scan failed to complete"; exit 1; }
[ "$RISK" -lt 70 ] && { echo "::error::Security risk score too low ($RISK)"; exit 1; }
true
- name: Browser Audit (JS errors, broken links)
run: |
TEST_ID=$(curl -sf -X POST https://www.scanverra.com/api/browser-test \
-H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
-d "{\"url\":\"$TARGET_URL\"}" | jq -r .testId)
for i in $(seq 1 30); do
RESULT=$(curl -sf "https://www.scanverra.com/api/browser-test?id=$TEST_ID" -H "X-API-Key: $API_KEY")
STATUS=$(echo "$RESULT" | jq -r .status)
[ "$STATUS" != "running" ] && break
sleep 10
done
ERRORS=$(echo "$RESULT" | jq '[.jsErrors[]? | select(.severity=="error")] | length')
[ "$STATUS" = "failed" ] && { echo "::error::Browser audit failed to complete"; exit 1; }
[ "$ERRORS" -gt 0 ] && { echo "::error::$ERRORS JS errors found"; exit 1; }
true
- name: Repo Scan (SAST, secrets, deps, quality gate)
run: |
SCAN_ID=$(curl -sf -X POST https://www.scanverra.com/api/repo/scan \
-H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
-d '{"owner":"your-org","repo":"your-repo","branch":"${{ github.head_ref }}"}' | jq -r .scanId)
for i in $(seq 1 30); do
RESULT=$(curl -sf "https://www.scanverra.com/api/repo/result?id=$SCAN_ID" -H "X-API-Key: $API_KEY")
STATUS=$(echo "$RESULT" | jq -r .status)
[ "$STATUS" != "running" ] && break
sleep 10
done
echo "$RESULT" | jq '{score, qualityGate}'
GATE=$(echo "$RESULT" | jq -r .qualityGate)
[ "$GATE" = "failed" ] && { echo "::error::Repo quality gate failed"; exit 1; }
true
- name: SEO Optimizer (on-page, GEO, E-E-A-T)
run: |
SCAN_ID=$(curl -sf -X POST https://www.scanverra.com/api/seo-optimizer \
-H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
-d "{\"url\":\"$TARGET_URL\"}" | jq -r .scanId)
for i in $(seq 1 30); do
RESULT=$(curl -sf "https://www.scanverra.com/api/seo-optimizer?id=$SCAN_ID" -H "X-API-Key: $API_KEY")
STATUS=$(echo "$RESULT" | jq -r .status)
[ "$STATUS" != "running" ] && break
sleep 10
done
SCORE=$(echo "$RESULT" | jq '.score')
echo "seoScore=$SCORE"
[ "$STATUS" = "failed" ] && { echo "::error::SEO scan failed to complete"; exit 1; }
[ "$SCORE" -lt 70 ] && { echo "::error::SEO score too low ($SCORE)"; exit 1; }
trueأين تُخزَّن مفتاح API كسرّ (secret)، حسب المنصة:
- GitHub Actions: سرّ على مستوى المستودع — Settings ← Secrets and variables ← Actions.
- GitLab CI: متغيّر CI/CD مُخفى (masked) — Settings ← CI/CD ← Variables.
- Azure Pipelines: متغيّر خط أنابيب سرّي (أيقونة القفل) أو مجموعة متغيرات (Variable Group) ضمن Pipelines ← Library.
- Bitbucket Pipelines: إعدادات المستودع > Pipelines > متغيرات المستودع، مع تحديدها كمتغيرات محمية.
- Jenkins: بيانات اعتماد نصية سرية (Secret text credential) — Manage Jenkins ← Credentials — يُشار إليها عبر
credentials(). تحتاج صورة الوكيل (agent image) أيضًا إلى تثبيتcurlوjqوbc؛ تتضمنها مسبقًا أدوات التشغيلubuntu-latestالمستضافة على GitHub وAzure، بينما لا تتضمنها صورةalpineفي GitLab (ومن هنا تأتي خطوةapk add).
تحتاج خطوة ماسح المستودعات إلى ربط حساب GitHub/Bitbucket/Azure الخاص بك مسبقًا في إعدادات Scanverra — فهي تفحص باستخدام الرمز (token) المخزَّن هناك، وليس رمزًا يُرسَل مع كل طلب.
مخرجات SARIF لفحص الشيفرة
SARIF (تنسيق تبادل نتائج التحليل الثابت) هو تنسيق JSON القياسي الذي تتبادل به أدوات فحص الشيفرة نتائجها. تكتب واجهة Scanverra CLI ملفات SARIF 2.1.0 باستخدام --sarif، فتظهر النتائج في أي مكان يقرأ SARIF.
- فحص الشيفرة في GitHub: فعّل sarif: true في scanverra/scan-action لتظهر النتائج في تبويب Security وكتعليقات على طلبات الدمج.
- يمكن لـ SonarQube والمنصات الأخرى التي تستورد SARIF قراءة الملف نفسه.
- في Bitbucket وGitLab: احتفظ بملف SARIF كأحد مخرجات خط التنفيذ، أو استخدم --json لخطوة تقارير خاصة بك.
- uses: actions/checkout@v4
- uses: scanverra/scan-action@v1
with:
api-key: ${{ secrets.SCANVERRA_API_KEY }}
sarif: true # uploads the report to GitHub code scanning
# The job needs: permissions: { security-events: write }ماسح المستودعات فقط هو من يُعيد حقل qualityGate الجاهز. أما بالنسبة للأدوات الأربع الأخرى، فحدّد حدودك الخاصة مسبقًا وحافظ على ثباتها عبر جميع البيئات (على سبيل المثال، لا تقارن نتيجة أمان عنوان URL خاص ببيئة تجريبية بحد ضُبط لبيئة الإنتاج).