Fendix: قرارات إصدار مبنية على أدلة أمنية
أداة CLI مفتوحة المصدر مكتوبة بـ Go، ومعاها منصة hosted، بيحوّلوا نتايج SAST و DAST و SCA ونتايج SARIF المستوردة لـ findings ليها درجة ثقة (confidence)، ولقرار إصدار بياخد التغطية (coverage) في اعتباره. أنا اللي عملت Fendix، وبتابع صيانته لوحدي.
- فحص في كتالوج v3.6.3
- 85
- تصنيف للـ findings
- 18
- إصدار من أبريل 2026 لحد دلوقتي
- 53
- محرك مفتوح المصدر
- MIT
المشكلة
إيه المشكلة اللي كنا بنحلها؟
أغلب بوابات الأمان في الـ CI بتوقّف الـ build على أساس الخطورة (severity) بس. الـ findings اللي مش مؤكدة بتوقّف الإصدارات، والفرق بتتعود تعمل override للبوابة، والمشاكل الحقيقية بتضيع وسط الـ noise.
وفيه مشكلة تانية أصعب في الملاحظة: الـ scanner اللي ما اشتغلش — أداة مش موجودة، أو إعدادات غلط، أو target مش بيتوصل له — مش بيطلّع أي findings، وبيبان كأنه فحص نجح من غير ما يلاقي مشاكل. والفريق محتاج يعرف الإصدار بيحقق مستوى الأمان المطلوب ولا لأ، وليه، وهل الأدلة كاملة.
القيود
إيه القيود اللي أثّرت على التصميم؟
- يشتغل جوه الـ CI في دقايق، من غير ما يعتمد على أي خدمة hosted في النسخة مفتوحة المصدر.
- يكون deterministic وقابل للتفسير: مفيش أي موديل machine learning لا في الـ detection ولا في حساب الدرجات ولا في القرارات.
- عمره ما يخبّي findings — كل ملاحظة بتفضل في التقرير؛ الحاجة الوحيدة اللي ممكن تتغير هي هل بتعمل block ولا لأ.
- يشتغل مع الأدوات اللي الفرق بتشغّلها أصلًا، عن طريق استيراد وتصدير SARIF 2.1.0.
- توزيع موثوق: artifacts موقّعة، و SBOMs منشورة، ومفيش أي telemetry من الـ CLI.
الـ architecture
النظام متصمم إزاي؟
فيه orchestrator مكتوب بـ Go بيشغّل الـ analyzers بتاعته، وبيدير Semgrep و sidecar بـ Python بيعمل taint analysis، وبيستورد SARIF، وبعدين بيدّي كل finding درجة ويقرر هل يعمل block ولا لأ. والـ hosted workspace بيضيف قواعد للتغطية (coverage) وقرار إصدار واحد لكل scan.
مصادر الأدلة
الـ API وهو شغال (DAST)
15 فحص HTTP مدمج: فحوصات passive، وفحوصات بـ authentication، و IDOR بمستخدمين اتنين، و active probes بالاختيار (opt-in).
الكود والـ specs (SAST)
الـ secrets (18 قاعدة)، و textscan (29 قاعدة)، و Semgrep (23 قاعدة لـ Python)، و taint analysis على الـ AST بتاع Python، وفحوصات OpenAPI.
الـ dependencies (SCA)
البحث في OSV لملفات الـ manifest بتاعة pip و npm، و govulncheck للـ Go modules.
SARIF مستورد
نتايج من CodeQL أو Semgrep أو Trivy أو أي أداة SARIF 2.1.0.
محرك الأدلة
الربط (correlation)
الـ findings الـ static والـ runtime بتندمج لما تصنيفاتها تكون مرتبطة وأماكنها تتطابق: الـ route pattern، والـ endpoint، والـ path suffix، وبعدين الـ segment المشترك.
درجة الثقة
درجة deterministic من 0 لـ 100، ولكل قاعدة اتطبقت سبب واحد منشور؛ HIGH ≥ 70 و MEDIUM ≥ 40.
التغطية (coverage)
كل analyzer بيسجّل ok أو skipped أو failed، مع كود للسبب (reason code).
القرارات
بوابة الـ CLI
كل finding بيبقى BLOCK أو WARN أو INFO؛ والـ exit code بيبقى 0، أو 1 (blocking)، أو 2 (خطأ، أو فجوة في التغطية مع تفعيل الـ enforcement).
قرار الإصدار في النسخة الـ hosted
قرار واحد لكل scan: BLOCK، أو INCOMPLETE (أدلة مطلوبة ناقصة)، أو WARN، أو PASS — ومعاه الأسباب.
التسليم
GitHub Action
بيتحقق من الإصدار الموقّع قبل ما يشتغل، وبيرفع SARIF على الـ code scanning، وبيعدّي أو يوقّف على حسب الـ exit code.
الـ hosted workspace
الـ backend بـ Django و Celery و PostgreSQL؛ والـ frontend بـ Next.js بالإنجليزي والعربي.
الربط مع أدوات تانية
الـ issues على Jira، وإشعارات Slack و Teams، والـ GitHub checks.
القرارات الهندسية
اخترنا إيه، وليه؟
البوابة على أساس الثقة، مش الخطورة (v2.0)
الـ finding اللي عند الـ threshold بتاع الـ flag --fail-on أو فوقه ما بيعملش block إلا لو مستوى الثقة بتاعه (confidence band) والأدلة اللي بتدعمه يبرّروا ده؛ وغير كده بيظهر في التقرير كـ WARN. ده كان breaking change، عشان كده الـ flag --enforce-confidence=false بيرجّع السلوك القديم اللي بيعتمد على الخطورة (severity) بس، بالضبط زي ما كان، وده متحقق منه باختبار بيغطي 54 تركيبة من الخطورة والـ threshold.
الربط إشارة، مش هو البوابة
لما finding من التحليل الـ static و finding من الـ runtime يوصفوا نفس المشكلة على نفس الـ route، بيندمجوا في finding واحد مترابط (correlated) وثقته بتزيد. والربط (correlation) ده إشارة واحدة من سبع إشارات مستقلة. الدليل القوي اللي جاي من مصدر واحد — رد فعلي من الـ API بيبيّن المشكلة، أو credential حقيقي في كود الـ production — ممكن يعمل block لوحده، والربط عمره ما بيشيل findings.
كل نقطة في الدرجة ليها سبب
الثقة بتبدأ من قيمة أساسية، وكل قاعدة بتزوّد أو بتنقّص مقدار ثابت ومعاه سبب مكتوب بكلام بسيط. والاختبارات بتضمن إن مجموع النقاط المرتبطة بالأسباب يساوي الدرجة، فأي حد يقدر يشوف الـ finding عمل block ليه.
التغطية نتيجة أساسية في حد ذاتها
الـ scanner اللي ما اشتغلش بيتسجّل skipped أو failed، وعمره ما بيختفي في صمت. في الـ hosted workspace، لو فيه أدلة مطلوبة ناقصة، النتيجة بتبقى INCOMPLETE مش PASS؛ وفي الـ CLI، فيه flags بالاختيار (opt-in) بتحوّل فجوات التغطية لـ exit code بيدل على فشل.
تأكيد النتايج المستوردة محتاج دليل مستقل
الـ SARIF اللي جاي من أدوات تانية بيفضل بره الربط (correlation) بتاع Fendix نفسه. ومينفعش يأكد finding طالع من Fendix إلا لو أداة مستقلة طلّعت نفس الـ CWE في نفس المكان بعد الـ normalisation.
سلسلة توريد ممكن التحقق منها
الـ binaries، وحزم Linux، والـ SBOMs، والـ container images كلها موقّعة بالـ keyless signing بتاع cosign؛ وكل binary بينزل معاه SBOMs بصيغة CycloneDX و SPDX، و provenance statement حسب SLSA v1.0 (Level 2). والـ GitHub Action بيتحقق من سجل الـ build الموقّع قبل ما يشتغل.
المفاضلات
كسبنا إيه، وإيه اللي اتنازلنا عنه؟
فشل الـ build بيتحدد بالثقة
فيه builds كانت بتفشل وممكن دلوقتي تعدّي؛ والفرق اللي عايزة السلوك القديم لازم تعمل opt-out بشكل صريح، والقرارات دي بيتحط عليها علامة إنها policy overrides.
binary بـ Go ومعاه sidecar بـ Python
أعمق تحليل (الـ taint بتاع Python) محتاج شجرة ملفات الـ Python engine أو الـ container الرسمي؛ والـ binary لما يشتغل لوحده بيسجّله unavailable.
مستوى التحليل بيختلف حسب اللغة
في Python، الـ taint analysis بيشتغل بين الـ functions (interprocedural) وبياخد الـ sanitizers في اعتباره؛ لكن Go و JavaScript/TypeScript و Java بيعتمدوا على pattern rules على مستوى السطر الواحد.
قواعد deterministic بدل موديلات متدرّبة
قرارات ممكن تفسيرها بالكامل، بس الـ detection محدود بالقواعد الموجودة.
Linux و macOS بس
الإصدارات معمولة لـ linux و darwin على amd64 و arm64، وكمان container multi-arch؛ ومفيش build لـ Windows.
النتايج
وصلنا لإيه؟
إطلاق بوابة بتعتمد على الأدلة
القرارات بتتحدد بالثقة من v2.0 (أغسطس 2026)، ومعاها fingerprints ثابتة للـ findings من v3.0، و suppression على مستوى كل occurrence من v3.6.
إصدارات ممكن التحقق منها
53 إصدار من أبريل 2026 لحد دلوقتي؛ والإصدارات الحالية موقّعة وبينزل معاها SBOMs و provenance. واتحققت بـ cosign من توقيعات الـ binary والـ container بتوع v3.6.3.
benchmarks منشورة وحدودها موضّحة
مجموعات اختبار (corpora) عليها gate في الـ CI (زي corpus صناعي لـ SAST فيه 56 حالة)، وحدودها مكتوبة: بتقيس القواعد على حالات مختارة بعناية، مش نسبة الـ detection في الواقع.
مفتوح المصدر، ومعاه نسخة hosted
المحرك اللي برخصة MIT بيشتغل في أي CI؛ والـ hosted workspace بيضيف organisations، و roles، و MFA، و audit log، وقرارات الإصدار.
دوري
- عملت Fendix ولسه أنا الـ maintainer الوحيد ليه: محرك Go والـ analyzers بتوع Python، والـ backend والـ frontend بتوع النسخة الـ hosted، والـ release pipeline، والتوثيق.
- صممت نموذج الأدلة: قواعد الربط (correlation)، وحساب درجة الثقة، وتتبّع التغطية، وطبقتَي اتخاذ القرار.
- بنيت الـ pipeline بتاع الإصدارات الموقّعة والـ packaging (الـ binaries، وحزم .deb/.rpm، والـ container images، والـ SBOMs، والـ provenance، والـ Homebrew tap).
حدود الشغل
- تأثير الربط (correlation) على الـ false positives ما اتقاسش؛ وهو موضّح هنا كآلية عمل. نسخة أقدم من الموقع ده كانت بتذكر انخفاض في الـ false positives على إنه نتيجة مقاسة، وكانت بتوصف بوابة أشد من اللي نزلت فعلًا؛ والادعاءين دول اتشالوا.
- الـ Managed CI وسجلات القرارات اللي بتكشف أي تلاعب (tamper-evident) متاحين بشكل محدود؛ والـ demo workspace العام بيستخدم بيانات تجريبية.
- Fendix مش حاصل على أي شهادة أمان من طرف تالت، ومش بدّعي أي أرقام عملاء أو انتشار.
- الـ flag --offline لسه مش hermetic بالكامل لما فحص الـ dependencies بتاع Python بيشتغل؛ والـ README موثّق فيه الـ workaround.
دراسة الحالة اللي بعدها
Qatf
أداة مفتوحة المصدر (Apache-2.0) بتحوّل الفيديو الطويل لمقاطع 9:16 عليها captions كلمة بكلمة. موديل لغوي هو اللي بيختار اللحظات؛ لكن التفريغ وضبط حدود القص والـ rendering بيفضلوا deterministic، والـ captions بالعربي من اليمين للشمال جزء أساسي من الأداة. أنا اللي عملتها وبتابع صيانتها؛ وهي نموذج أولي شغال.
اقرأها