
تحقق بشكل متقاطع من عروض سطح الهجوم الخاص بك واعثر على نقاط النهاية التي لا يمكنها تأكيد بعضها البعض.
تحقق من تطابق وجهات النظر الخاصة بسطح الهجوم لديك واعثر على نقاط النهاية التي لا يمكنها تأكيد بعضها البعض.
يجب أن تكون نقطة النهاية قادرة على تبرير وجودها. إنها موجودة في الكود، لذا يجب أن يصفها عقد. إنها موجودة في العقد، لذا يجب أن ينفذها شيء ما. إنها تستقبل حركة مرور حقيقية، لذا من الأفضل أن تكون موجودة في مكان ما. عندما تعرف إحدى وجهات النظر عن نقطة نهاية بينما لا تعرفها الأخريات، فإن تلك الفجوة هي النتيجة.
يقوم alibi بتشغيل OWASP noir، ويقرأ مخرجات JSON الخاصة به، ويقارن وجهات النظر ببعضها البعض.
يقرأ Noir بالفعل خمس وجهات نظر مستقلة لنفس السطح:
| وجهة النظر | تُقرأ من |
|---|---|
| code | أكثر من 200 محلل عبر 33 لغة |
| doc | OpenAPI، RAML، WSDL، GraphQL SDL، AsyncAPI، gRPC، Smithy، TypeSpec، OData، OpenRPC |
| traffic | HAR، mitmproxy، Burp، Caido، ZAP، Postman، Insomnia، Bruno، .http |
| gateway | nginx، Apache، Envoy، Kong، Traefik، APISIX، Caddy، Istio، Kubernetes Ingress و Gateway API |
| infra | Terraform، CloudFormation، CDK، Serverless، Vercel، Netlify، Wrangler، Azure Functions، Kamal |
ما لا يفعله هو مقارنتها. هذا هو العمل الكامل هنا، وهو لا يحتاج إلى أي تغيير في noir — يقوم alibi بتشغيله مرة واحدة لكل وجهة نظر ويدمج النتائج.
جزء كل وجهة نظر مهم. يقوم Noir بإزالة التكرار حسب (method, url) عبر كل محلل، لذا فإن مسار Flask ومسار OpenAPI مكتوبان بنفس الطريقة يندمجان في نقطة نهاية واحدة تحمل تقنية واحدة. هذا صحيح بالنسبة لأداة اكتشاف — إنها نقطة نهاية واحدة — لكنه يمحو التأكيد المتبادل الذي بُنيت هذه الأداة لقياسه، ويمحوه في أسوأ اتجاه ممكن: كلما اتفقت وجهتا نظر بشكل أفضل، اختفى المزيد منهما. يُفحص Casdoor على أنه 372 نقطة نهاية برمجية و9 موثقة؛ افحص دليل swagger/ الخاص به وحده وستجد أن المواصفة تحتوي على 235.
يقيّد --only-techs مجموعة الكواشف، لذا فإن فحصًا واحدًا لكل وجهة نظر يحافظ على كل واحدة منها كاملة. أي تقنية تتحدث عن أي وجهة نظر موجود في views.yml؛ وأي التقنيات موجودة هو ما يبلغ عنه noir list techs.
لا يقوم alibi بتحليل أي تنسيقات API بنفسه. مدخله الوحيد هو JSON الخاص بـ noir.
يتطلب noir الإصدار 1.0.0 أو أحدث على PATH -- هذا هو الإصدار الذي أصبح فيه noir list techs أمرًا فرعيًا، وهذا الكتالوج هو ما يعيّن كل تقنية إلى وجهة نظر. تتبع التطوير إصدار noir الحالي. يتم رفض أي ملف تنفيذي أقدم بالاسم بدلًا من تركه يفشل عند أول قراءة للكتالوج.
$ uv tool install noir-alibi # or: pipx install noir-alibi
$ alibi scan ./my-service
$ alibi scan # the working directory
$ alibi scan ./service ./contracts ./prod.har # or wherever the views live
كل مسار هو مصدر، يُفحص مرة واحدة لكل وجهة نظر. وجّهه إلى أي شيء لديك — شجرة مصدر، دليل مواصفات، ملف التقاط واحد — وستقوم وجهات النظر المفقودة بإيقاف قواعدها بدلًا من إغراق التقرير.
alibi · 1 source · 377 endpoints
code 372 doc 235
230 corroborated -- vouched for by more than one view
19 endpoints nearly matched another view -- these may be matching failures, not real gaps
SHADOW Shadow API -- Implemented, but no contract describes it
134 findings · 4 critical, 57 high, 62 medium, 11 low
critical POST /api/upload-groups router.go:87
upload paths carry more consequence than reads
critical POST /api/upload-permissions router.go:208
...
... and 122 more (SHADOW in full: -f json)
TWO SURFACES?
The doc view is 97% under /api, and 37 of these findings are outside it.
If that is a separate surface the contract never covered, narrow the scan:
alibi scan <paths> --ignore '^/(?!api(/|$))'
If it is the same surface left undocumented, they are the findings that matter most.
تتوقف المجموعات عند اثنتي عشرة — الترتيب من الأسوأ أولًا، لذا فإن الذيل هو الجزء الأقل إفادة، و-f json يحتوي على كل شيء.
تأتي أعلام noir بعد -- مجردة أو عبر --noir-arg. المرشحات مثل --exclude-path مقبولة؛ الأعلام التي قد تستبدل عقد JSON أو تُلغي فحوصات alibi لكل وجهة نظر (--format، --diff-*، --only-techs، …) يتم رفضها بحالة خروج 2.
- run: alibi scan . ./contracts -f sarif > alibi.sarif
- uses: github/codeql-action/upload-sarif@v3
with: { sarif_file: alibi.sarif }
يقول التقرير إن وجهات النظر تختلف؛ ويقول --endpoints ما احتوته كل واحدة منها.
$ alibi scan ./repo -f json --endpoints
تحصل كل وجهة نظر على قائمة: المفتاح، وأي وجهات نظر أكدته، والتقنيات التي تقف خلفه، والملفات، والكتابة قبل التطبيع — وهنا يكون الفرق دائمًا عندما يجب أن يتطابق صفان ولم يتطابقا. إنها ثلاثة إلى أربعة أضعاف بقية الحمولة، لذا فهي علم وليست الافتراضي.
أو قيّم مباشرة: alibi scan . ./contracts --fail-on high يخرج بقيمة غير صفرية عندما يصل اكتشاف إلى تلك الخطورة. الفحص الذي لم يتمكن noir من قراءته بالكامل يبلغ عن executionSuccessful: false، لذا فإن تشغيلًا متدهورًا لا يمر على أنه نظيف.
يحتفظ Noir بصيغة المسار الخاصة بكل إطار عمل بدلًا من اختراع صيغة مشتركة، لذا تصل نفس نقطة النهاية مكتوبة بعدة طرق:
python_flask /api/users/<int:user_id>
aiohttp /users/{id}
java_spring /api/catalog/{id}
oas3 /v1/pets/{petId}
rails /posts/:id
nginx /admin/.*
القاعدة التي تجعل هذه قابلة للمقارنة: اسم معامل المسار ليس جزءًا من هويته. {petId} و<int:user_id> يصفان نفس الفتحة؛ فقط موضعها وما إذا كانت تمتد عبر / هو ما يهم. يتم الاحتفاظ بالأسماء كدليل والإبلاغ عنها، لكنها لا تصل أبدًا إلى المفتاح.
تذكر الاكتشافات كيف تمت المطابقة:
| الدرجة | المعنى |
|---|---|
G1 | الكتابات اتفقت بالفعل |
G2 | تتفق بمجرد تطبيع صيغة المعامل |
G0 | وجهة نظر واحدة فقط لديها — لم تتم مطابقة أي شيء |
أداة كهذه تموت بالإبلاغ عن مئات الاكتشافات في أول تشغيل لها، أو بالإبلاغ عن تقدم لم يحققه أحد. ستة أشياء تدفع في الاتجاه المعاكس:
القواعد لا تُفعّل بدون وجهتي النظر معًا. افحص قاعدة برمجية بدون أي عقود في أي مكان وستكون كل نقطة نهاية مؤهلة تقنيًا كواجهة برمجية ظل غير موثقة. تلك الاكتشافات لا تقول شيئًا سوى أنك لم توفر أي توثيق، لذا فإن القاعدة تعمل فقط عندما تكون كل وجهة نظر تستنتج منها موجودة فعليًا في الفحص. يذكر التقرير القواعد التي لم تشارك.
الاقترابات القريبة تُبلغ عنها كشك، وليس كاكتشافات. "في الكود، ليس في الوثائق" لا يمكن تمييزه عن "في كليهما، لكن alibi فشل في محاذاتهما." لذا يتم فحص نقطة النهاية التي تقع في وجهة نظر واحدة مقابل الأخريات بحثًا عن اقتراب قريب — نفس المسار بفعل مختلف، أو على بعد مقطع واحد حيث يكون لدى أحد الجانبين معامل والآخر قيمة حرفية. يتم تخفيض الاكتشافات التي تحمل اقترابًا قريبًا ووضع علامة عليها للمراجعة. هذا العدد يجلس بجانب الإجماليات، لأن كل اكتشاف موثوق بقدر ما هو صغير.
وجهات النظر التي لم تلتقِ قط هي تشخيص واحد، وليست مئات الاكتشافات. يسجل Argo CD المسار /api في Go ويوثق 198 مسارًا تحته، لذا لا تشترك شيفرته ومواصفته في نقطة نهاية واحدة. قراءة حرفية لذلك تعني 58 واجهة برمجية ظل و198 عقدًا وهميًا، لا شيء منها حقيقي. صفر تأكيد متبادل بين وجهتي نظر مأهولتين يعني أن المقارنة لم تنجح — نقطة تركيب تمثل المسارات تحتها، أو حزمة لم يتمكن noir من قراءتها — لذا يتم حجب القواعد وطباعة السبب بدلًا من ذلك. المسارات التي يتبين أن لها العديد من نقاط النهاية من وجهات نظر أخرى تحتها يتم وسمها كنقاط تركيب محتملة.
عندما تتوافق وجهتا النظر بمجرد إزالة بادئة ثابتة من إحداهما، يذكر التشخيص ذلك ويسمي البادئة. تعلن مواصفة Gitea المولدة عن basePath: /GITEA-API-APP-SUBURL/api/v1 بينما يركّب موجّه Go الخاص بها /api/v1؛ لا تشترك وجهتا النظر في شيء، لكن 154 من 535 مسارًا موثقًا تطابق مسارًا برمجيًا بمجرد إزالة تلك المقاطع الثلاثة. هذا إما basePath في المواصفة، أو servers[].url، أو نقطة تركيب أسقطها قارئ الكود — ويتم الإبلاغ عنه، ولا يتم تطبيقه أبدًا، لأن إعادة محاذاة المسارات ستخفي الخطأ الذي وجدته.
يتم تسمية الفيضان الذي هو في الحقيقة شجرة فرعية واحدة مفقودة كواحد: 207 من 354 عقدًا وهميًا في NodeBB تقع تحت /api/v3، حيث لا تحتوي وجهة نظر الكود على أي شيء على الإطلاق.