
अपनी attack surface के दृश्यों का क्रॉस-चेक करें और वे endpoints खोजें जो एक-दूसरे की पुष्टि नहीं कर सकते।
अपने अटैक सरफेस के दृश्यों का क्रॉस-चेक करें और वे एंडपॉइंट खोजें जो एक-दूसरे की पुष्टि नहीं कर सकते।
एक एंडपॉइंट को अपना हिसाब दे पाना चाहिए। यह कोड में है, तो एक कॉन्ट्रैक्ट को इसका वर्णन करना चाहिए। यह कॉन्ट्रैक्ट में है, तो किसी को इसे इम्प्लीमेंट करना चाहिए। यह असली ट्रैफ़िक लेता है, तो बेहतर होगा कि यह कहीं मौजूद हो। जब एक दृश्य किसी एंडपॉइंट के बारे में जानता है और बाकी नहीं, तो वह अंतर ही निष्कर्ष है।
alibi OWASP noir चलाता है, उसका JSON पढ़ता है, और दृश्यों की एक-दूसरे से तुलना करता है।
Noir पहले से ही एक ही सरफेस के पाँच स्वतंत्र दृश्य पढ़ता है:
| दृश्य | यहाँ से पढ़ा जाता है |
|---|---|
| code | 33 भाषाओं में 200 से अधिक एनालाइज़र |
| 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 फ़ॉर्मैट पार्स नहीं करता। इसका एकमात्र इनपुट noir का JSON है।
PATH पर noir 1.0.0 या नया आवश्यक है -- यही वह रिलीज़ है जहाँ 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 में यह सब होता है।
- 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 | केवल एक दृश्य में है — कुछ भी मैच नहीं हुआ |
इस तरह का टूल अपने पहले रन में सैकड़ों निष्कर्ष रिपोर्ट करके, या ऐसी प्रगति रिपोर्ट करके मर जाता है जो किसी ने की ही नहीं। छह चीज़ें इसका विरोध करती हैं:
दोनों दृश्यों के बिना नियम नहीं चलते। कहीं भी कॉन्ट्रैक्ट के बिना एक कोडबेस स्कैन करें और हर एंडपॉइंट तकनीकी रूप से एक अनडॉक्युमेंटेड शैडो API के योग्य हो जाता है। वे निष्कर्ष इसके अलावा कुछ नहीं कहते कि आपने कोई डॉक्युमेंटेशन नहीं दिया, इसलिए एक नियम तभी चलता है जब वह जिस हर दृश्य पर तर्क करता है वह वास्तव में स्कैन में था। रिपोर्ट उन नियमों के नाम बताती है जो बाहर बैठे।
नियर मिस को निष्कर्ष के रूप में नहीं, संदेह के रूप में रिपोर्ट किया जाता है। "कोड में है, डॉक्स में नहीं" और "दोनों में है, लेकिन alibi उन्हें मिला नहीं सका" में कोई अंतर नहीं किया जा सकता। इसलिए एक एंडपॉइंट जो एक दृश्य में आता है, उसे नियर मिस के लिए बाकी दृश्यों के विरुद्ध जाँचा जाता है — एक ही पथ एक अलग वर्ब के साथ, या एक सेगमेंट का अंतर जहाँ एक तरफ़ पैरामीटर है और दूसरी तरफ़ लिटरल। नियर मिस वाले निष्कर्ष डिमोट किए जाते हैं और समीक्षा के लिए फ़्लैग किए जाते हैं। वह गिनती कुल के बगल में बैठती है, क्योंकि हर निष्कर्ष उतना ही भरोसेमंद है जितना वह छोटा है।
जो दृश्य कभी मिले ही नहीं वे एक डायग्नोस्टिक हैं, सैकड़ों निष्कर्ष नहीं। Argo CD Go में /api रजिस्टर करता है और उसके नीचे 198 पथ डॉक्युमेंट करता है, इसलिए उसका कोड और उसका स्पेसिफिकेशन एक भी एंडपॉइंट साझा नहीं करते। शाब्दिक रूप से पढ़ें तो वह 58 शैडो API और 198 फैंटम कॉन्ट्रैक्ट हैं, जिनमें से कोई भी असली नहीं। दो भरे हुए दृश्यों के बीच शून्य पुष्टि का मतलब है कि तुलना काम नहीं कर पाई — एक माउंट पॉइंट जो अपने नीचे के रूट्स की जगह खड़ा है, या एक स्टैक जिसे noir पढ़ नहीं सका — इसलिए नियम रोक दिए जाते हैं और इसके बजाय कारण प्रिंट किया जाता है। जो पथ निकलते हैं कि उनके नीचे अन्य दृश्यों से कई एंडपॉइंट हैं, उन्हें संभावित माउंट के रूप में लेबल किया जाता है।
जब दोनों दृश्य एक स्थिर प्रीफ़िक्स हटाने पर मेल खाते हैं, तो डायग्नोस्टिक यह बताता है और प्रीफ़िक्स का नाम देता है। Gitea का जनरेट किया गया स्पेसिफिकेशन basePath: /GITEA-API-APP-SUBURL/api/v1 घोषित करता है जबकि उसका Go राउटर /api/v1 माउंट करता है; दृश्य कुछ भी साझा नहीं करते, लेकिन 535 डॉक्युमेंटेड पथों में से 154 उन तीन सेगमेंट हटाने पर एक कोड पथ से मेल खाते हैं। वह एक स्पेक basePath, एक servers[].url, या एक माउंट है जिसे कोड रीडर ने छोड़ दिया — और यह रिपोर्ट किया जाता है, कभी लागू नहीं किया जाता, क्योंकि पथों को फिर से संरेखित करना उस बग को छिपा देगा जो इसने खोजा।
एक बाढ़ जो वास्तव में एक गायब सबट्री है, उसे एक के रूप में नाम दिया जाता है: NodeBB के 354 फैंटम कॉन्ट्रैक्ट में से 207 /api/v3 के नीचे बैठते हैं, जहाँ कोड दृश्य में कुछ भी नहीं है।
एक गायब दृश्य और एक खाली दृश्य का मतलब उल्टा होता है। Noir रिपोर्ट करता है कि वह क्या नहीं पढ़ सका, और alibi उसे निष्कर्षों के ऊपर प्रिंट करता है। NetBox एक 12.35MB का OpenAPI डॉक्युमेंट 308 पथों के साथ भेजता है; noir इसे उसकी फ़ाइल-साइज़ सीमा से अधिक होने के कारण छोड़ देता है, और उस रिपोर्ट के बिना alibi कहता है कि प्रोजेक्ट कुछ भी डॉक्युमेंट नहीं करता — केवल अपूर्ण नहीं, बल्कि आत्मविश्वास से कहा गया गलत उत्तर।
जो नियम चलना बंद हो गया उसने कुछ हल नहीं किया। स्कैन रिकॉर्ड करना और उनकी तुलना करना उसी गलती को दूरी पर फिर से ले आता है: एक रन में कॉन्ट्रैक्ट्स डायरेक्टरी भूल जाएँ और SHADOW कुछ भी मूल्यांकित नहीं करता, जो एक भोले अंतर के लिए बिल्कुल वैसा ही दिखता है जैसे हर शैडो API बंद हो गया हो। पाँच-दृश्य फ़िक्स्चर पर, एक आर्ग्युमेंट छोड़ने से सात स्थायी निष्कर्ष "हल" में बदल गए। स्नैपशॉट रिकॉर्ड करते हैं कि कौन से नियम मूल्यांकित हुए, अंतर केवल उन नियमों पर विचार करते हैं जो दोनों स्कैन में चले, और बाकी NOT COMPARED के अंतर्गत नामित किए जाते हैं।
एक अनुपस्थिति तभी साक्ष्य है जब सिग्नल मौजूद हो। Noir के auth टैगर उन फ़्रेमवर्क को कवर करते हैं जिन्हें वे जानते हैं। जिस स्टैक में वे कवर नहीं करते, वहाँ कुछ भी auth टैग नहीं रखता, और उसे "अनऑथेंटिकेटेड" मानना हर निष्कर्ष को बढ़ा देगा और गंभीरता कॉलम को अर्थ से खाली कर देगा। गायब टैग पर चलने वाले समायोजनों के लिए आवश्यक है कि वह टैग पहले स्कैन में कहीं दिखे।
इसके बाद गंभीरता इस पर बदलती है कि noir के टैगरों ने क्या पाया: व्यक्तिगत डेटा, फ़ाइल अपलोड, प्रमाणीकरण का कोई संकेत नहीं, या एक ऐसी मेथड जो स्थिति बदलती है।
दोनों, दृश्य मैप (views.yml) और नियम (rules.yml), कोड नहीं बल्कि डेटा हैं।
एक location /api/ अपने नीचे की हर चीज़ के लिए खड़ा है, इसलिए गेटवे और इंफ्रास्ट्रक्चर नियम क्या यह उस एंडपॉइंट तक पहुँचता है का उत्तर देते हैं, न कि क्या इसमें वह है का। सेट के रूप में तुलना करने पर, हर प्रीफ़िक्स नियम ऐसा रूट दिखता है जिसे किसी ने इम्प्लीमेंट नहीं किया और हर इम्प्लीमेंटेड रूट अगम्य दिखता है।
कवरेज जानबूझकर उदार है। Noir वह पथ रिपोर्ट करता है जिससे एक नियम मेल खाता है, लेकिन यह नहीं कि वह प्रीफ़िक्स के रूप में मेल खाता है या ठीक-ठीक (location = /x, एक Ingress pathType: Exact), इसलिए सटीकता पुनःप्राप्त नहीं की जा सकती — और हर नियम को प्रीफ़िक्स मानना निष्कर्षों को गढ़ने के बजाय दबा देता है।
रिपोर्ट बताती है कि हर रूटिंग दृश्य कोड के कितने हिस्से तक पहुँचता है, क्योंकि "34 एंडपॉइंट जिन तक कोई गेटवे नहीं पहुँचता" असली है या नहीं, यह इस पर निर्भर करता है कि वह कॉन्फ़िग सेवा के सामने वाला है या नहीं। कोई थ्रेशोल्ड इन्हें ईमानदारी से अलग नहीं करता: Argo CD का e2e टेस्ट फ़िक्स्चर अपने कोड के 39% तक पहुँचता है और NetBox का असली कॉन्फ़िग 100% तक।
एक कैच-ऑल — location /, / पर एक Ingress, एक RewriteRule ^(.*)$ — किसी भी तरह साक्ष्य नहीं है। यह सब कुछ रूट करता है या कुछ भी नहीं, हर एंडपॉइंट के लिए समान, इसलिए यह किसी तक नहीं पहुँचने के रूप में गिना जाता है। एक गेटवे दृश्य जिसमें इसके अलावा कुछ नहीं है, उसके पास देने के लिए कोई सिग्नल नहीं है, और UNEXPOSED बाहर बैठता है और यह बताता है, बजाय हर एंडपॉइंट को अगम्य रिपोर्ट करने के। Casdoor का Helm चार्ट बिल्कुल यही है: / पर एक Ingress नियम, जिसे साक्ष्य के रूप में पढ़ने पर 365 निष्कर्ष निकले।
एक HAR कैप्चर उन रिक्वेस्ट को रिकॉर्ड करता है जो हुईं। एक Postman कलेक्शन उन रिक्वेस्ट को रिकॉर्ड करता है जिन्हें किसी ने करने का इरादा किया। ORPHAN, LIVE_UNDOC और COLD सभी इस पर तर्क करते हैं कि क्या चला, इसलिए उन्हें एक ऐसा दृश्य चाहिए जिसे किसी ने वास्तव में देखा हो और जब वे बाहर बैठते हैं तो यह बताते हैं।
Noir CLI आर्ग्युमेंट, Kafka टॉपिक और मोबाइल डीप लिंक एक ही सूची में रिपोर्ट करता है। cli://gitops-engine/agent को HTTP में समतल करने पर /agent बन जाता है — यह उस नाम के किसी भी वेब रूट से टकराता है और इससे पूछा जाता है कि कोई गेटवे इस तक रूट करता है या नहीं। प्रोटोकॉल एंडपॉइंट की पहचान का हिस्सा है; http और https एक ही स्पेस हैं और बाकी सब अपना-अपना रखता है।
कुछ अंतराल इच्छित स्थिति हैं। स्रोत के बगल में एक .alibi.yml रखें:
ignore:
- path: "^/internal/"
why: internal-only admin surface
- rule: UNEXPOSED
path: "^/debug/"
why: not fronted by the gateway in this repo
या एक बार के लिए --ignore REGEX पास करें। दबाए गए निष्कर्ष गिने जाते हैं और गिनती प्रिंट की जाती है — एक टूल जो चुपचाप निष्कर्ष गिरा देता है, उससे बुरा है जो बहुत सारे प्रिंट करता है, क्योंकि अब यह बताने का कोई तरीका नहीं बचता कि इसने क्या रोका।
शुरुआती, लेकिन सभी पाँच दृश्यों की तुलना की जाती है।
पाँच रिपॉज़िटरी के विरुद्ध मापा गया:
Casdoor सबसे साफ़ मामला है: इसके 235 डॉक्युमेंटेड एंडपॉइंट में से 230 कोड से मेल खाए, बिना किसी पथ नॉर्मलाइज़ेशन विफलता के। सभी 19 नियर मिस एक अलग वर्ब के तहत एक ही पथ थे — noir एक Go कैच-ऑल हैंडलर पर हर मेथड रजिस्टर कर रहा था, कोई मैचिंग समस्या नहीं।
NetBox शिक्षाप्रद है। यह एक ही रिपॉज़िटरी में दो सरफेस रखता है: एक सर्वर-रेंडर किया गया वेब UI और एक DRF-राउटर REST API जिसका केवल दूसरा डॉक्युमेंटेड है। पूरा स्कैन करने पर यह 746 निष्कर्ष रिपोर्ट करता है, जिनमें से ज़्यादातर यह सच लेकिन बेकार अवलोकन है कि एक वेब UI किसी API स्पेसिफिकेशन में नहीं है। जिस सरफेस का कॉन्ट्रैक्ट वर्णन करता है उस तक सीमित करने पर, यह उसी में सिमट जाता है जो वास्तव में कहने लायक था:
$ alibi scan ./netbox --ignore '^/(?!api(/|$))'
→ 3 शैडो API: /api/plugins, /api/schema/redoc, /api/schema/swagger-ui,
तीनों वास्तव में सर्व की जाती हैं और वास्तव में स्कीमा से अनुपस्थित हैं। जो 397
फैंटम बचते हैं वे बल्क ऑपरेशन हैं जो NetBox का अपना राउटर सबक्लास हर लिस्ट एंडपॉइंट में जोड़ता है, जिन्हें कोई urlconf वॉक नहीं देख सकता।
बाकी तीन रोके गए हैं, हर एक ऐसे कारण से जो जानने लायक है:
/api रजिस्टर करता है और उसके नीचे 198 पथ डॉक्युमेंट करता है — एक ही सरफेस दो ग्रैन्युलैरिटी पर।urls मॉड्यूल को इम्पोर्ट करके जोड़ता है, जिसे कोई स्टैटिक रीडर फ़ॉलो नहीं कर सकता।.proto एनोटेशन में इम्प्लीमेंटेड है, यही कारण है कि grpc कोड दृश्य के लिए बोलता है: वहाँ फ़ाइल करने पर, उसके 36 में से 36 डॉक्युमेंटेड पथ पुष्टि करते हैं।जो इस टूल की सीमा है, स्पष्ट रूप से कही गई: यह उसकी तुलना करता है जो noir पढ़ सकता है, और गलत ग्रैन्युलैरिटी पर पढ़ा गया दृश्य बिल्कुल न पढ़े गए दृश्य से बुरा है। ऊपर की ज़्यादातर मशीनरी इन्हें दोष के रूप में रिपोर्ट करने के बजाय अलग पहचानने के लिए मौजूद है।
MIT
| नियम | शर्त | गंभीरता |
|---|
ORPHAN | असली रिक्वेस्ट ले रहा है, कोड में अनुपस्थित | high |
LIVE_UNDOC | असली रिक्वेस्ट ले रहा है, किसी कॉन्ट्रैक्ट द्वारा वर्णित नहीं | high |
SHADOW | कोड में है, किसी कॉन्ट्रैक्ट में नहीं | medium |
DANGLING | एक गेटवे नियम जो किसी इम्प्लीमेंटेड चीज़ तक नहीं पहुँचता | medium |
DRIFT | डिप्लॉयमेंट के लिए घोषित, कोड से गायब | medium |
PHANTOM | एक कॉन्ट्रैक्ट में है, कोड में नहीं | low |
UNEXPOSED | इम्प्लीमेंटेड, लेकिन कोई गेटवे नियम इस तक नहीं पहुँचता | low |
COLD | इम्प्लीमेंटेड, कभी रिक्वेस्ट लेते नहीं देखा गया | info |
| रिपॉज़िटरी | code | doc | corroborated | findings | code↔doc |
|---|
| casdoor | 372 | 235 | 230 (98%) | 139 | compared |
| netbox | 1146 | 1193 | 796 (67%) | 746 | compared |
| argo-cd | 59 | 198 | 1 | 31 | held back |
| authentik | 231 | 1193 | 1 | 192 | held back |
| flipt | 2 | 42 | 0 | 0 | held back |