api-service नाम का एक Express API टेस्ट पास कर सकता है, OCI इमेज बना सकता है, और फिर भी ऐसा कोड शिप कर सकता है जिसे मंज़ूरी देने का किसी का इरादा नहीं था। एक डेवलपर package.json में express के बजाय expresss जोड़ देता है; पब्लिक रजिस्ट्री मिलते-जुलते नाम वाले पैकेज को रिज़ॉल्व करती है, GitHub Actions रनर उसे इंस्टॉल करता है, और रिलीज़ इमेज में वह शामिल हो जाता है। हमलावर को रिपॉज़िटरी में राइट एक्सेस, चोरी की गई डिप्लॉय की (deploy key), या प्रोडक्शन क्लस्टर तक एक्सेस की ज़रूरत नहीं पड़ी। बचाव करने वाले का सवाल ज़्यादा सीमित और उपयोगी है: हम डिपेंडेंसी रिज़ॉल्यूशन के ज़रिए बिना रिव्यू किए गए कोड को बिल्ड में आने से कैसे रोकें? हम api-service को उसके मैनिफ़ेस्ट से लेकर CI बिल्ड और पब्लिश हुई इमेज तक ट्रैक करेंगे।
इमेज के डिप्लॉयमेंट तक पहुँचने से पहले बिल्ड पाथ कई ट्रस्ट बाउंड्रीज़ (भरोसे की सीमाओं) को पार करता है। एक पब्लिक पैकेज रजिस्ट्री मैनिफ़ेस्ट में दिए गए नाम के आधार पर कोड सप्लाई करती है, CI रनर इंस्टॉलेशन करता है, और आर्टिफैक्ट रजिस्ट्री बाद में डिप्लॉयमेंट के लिए बनी इमेज को स्टोर करती है। एक मैलिशियस (हानिकारक) पैकेज बिल्ड में कोड एक्ज़ीक्यूशन की कोशिश कर सकता है, कोई नुकसानदायक ट्रांज़िटिव डिपेंडेंसी ला सकता है, या रनर के पास मौजूद पब्लिशिंग टोकन चुरा सकता है। नीचे दिया गया पाथ उन बाउंड्रीज़ को दिखाता है:
यह डायग्राम दिखाता है कि डिपेंडेंसी चुनना लोकल कोडिंग की डिटेल के बजाय सप्लाई-चेन का फ़ैसला क्यों है। जब रनर कोई पैकेज फ़ेच करता है, तो उसका कंटेंट बिल्ड इनपुट का हिस्सा बन जाता है और इंस्टॉलेशन हुक चला सकता है, जब तक कि पाइपलाइन उन्हें रोक न दे।
मैनिफ़ेस्ट पहली सीमा है क्योंकि npm install हर घोषित पैकेज नाम को कोड रिज़ॉल्व और फ़ेच करने के निर्देश के तौर पर देखता है। api-service में, वर्शन 4.18.3 वाला express अपेक्षित है, जबकि वर्शन 1.0.0 वाला बनाया गया नाम expresss एक टाइपो है जो किसी दूसरे पैकेज की ओर इशारा करता है। यह हमला टाइपोस्कवैटिंग है, जिसमें हमलावर ऐसा पैकेज पब्लिश करता है जिसका नाम भरोसेमंद डिपेंडेंसी जैसा होता है और वह इसे चुनने के लिए इंसानी गलती पर निर्भर करता है।
लॉकफ़ाइल किसी खास डिपेंडेंसी ग्राफ़ के लिए रिज़ॉल्व किए गए पैकेज वर्शन, रजिस्ट्री मेटाडेटा और इंटीग्रिटी डाइजेस्ट को रिकॉर्ड करती है। वह रिकॉर्ड बाद के इंस्टॉलेशन को तभी दोहराने लायक बनाता है जब गलत पैकेज पहले ही स्वीकार और कमिट कर लिया गया हो। इसलिए रिव्यू की विफलता दो जगहों पर दिखती है: package.json में एक अप्रत्याशित डायरेक्ट डिपेंडेंसी होती है, और package-lock.json में एक नई रिज़ॉल्व की गई एंट्री और इंटीग्रिटी वैल्यू होती है। किस पैकेज नाम और लॉकफ़ाइल एंट्री से expresss का जुड़ना रिव्यू में फेल हो जाना चाहिए?
यह कोड दिखाता है कि सफल इंस्टॉलेशन इस बात का सबूत नहीं है कि सही डिपेंडेंसी चुनी गई थी। इंस्टॉलर ने expresss को ठीक वैसे ही रिज़ॉल्व किया जैसा कहा गया था, और फिर नतीजे को लॉकफ़ाइल में रिकॉर्ड कर लिया; उस गलती को बार-बार दोहराना भी एक गलती ही है। डिपेंडेंसी में हुए बदलावों (diffs) की समीक्षा करने का मतलब है किसी अनजान नाम, अप्रत्याशित वर्शन, या नई जोड़ी गई इंटीग्रिटी एंट्री को ऐसे कोड के तौर पर देखना जो रिलीज़ में शामिल हो रहा है।
समीक्षा का नियम लॉकफ़ाइल यह साबित करती है कि इंस्टॉलर ने क्या चुना। यह साबित नहीं करती कि वह चुनाव अधिकृत या सुरक्षित था।
गलत स्पेलिंग वाला पब्लिक नाम मैनिफ़ेस्ट में शुरू होता है, लेकिन एक प्राइवेट पैकेज को तब भी बदला जा सकता है जब उसका नाम सही हो। डिपेंडेंसी कन्फ्यूज़न तब होता है जब कोई बिल्ड किसी ऐसे पैकेज को रिज़ॉल्व करता है जो अनचाही रजिस्ट्री से आता है; ऐसा अक्सर इसलिए होता है क्योंकि उसी नाम या उससे ऊँचे वर्शन वाला कोई पब्लिक पैकेज, इंटरनल पैकेज पर भारी पड़ जाता है। हमलावर को उस रजिस्ट्री में पब्लिश करने की क्षमता चाहिए होती है जिसका इस्तेमाल बिल्ड करता है, न कि प्राइवेट सोर्स रिपॉज़िटरी तक पहुँच की।
api-service के लिए, @acme/auth-client जैसी इंटरनल डिपेंडेंसी हमेशा ऑर्गनाइज़ेशन की प्राइवेट रजिस्ट्री से ही रिज़ॉल्व होनी चाहिए। वर्शन की एक बड़ी रेंज उन भविष्य के वर्शन की अनुमति देती है जो बताई गई रेंज को पूरा करते हैं, जबकि रजिस्ट्री की प्राथमिकता यह तय करती है कि इंस्टॉलर कहाँ देखता है। लॉकफ़ाइल कमिट किए गए बिल्ड के लिए नतीजे को सीमित करती है, लेकिन यह उस रजिस्ट्री मैपिंग को ठीक नहीं कर सकती जो रीबिल्ड को गलत सोर्स की ओर ले जाती है, और न ही यह लॉकफ़ाइल में किए गए जानबूझकर किए गए अपडेट से बचा सकती है जिसकी समीक्षा करने में समीक्षक चूक जाते हैं। कंट्रोल का लक्ष्य हर पैकेज स्कोप के लिए सही रजिस्ट्री से निश्चित रिज़ॉल्यूशन प्राप्त करना है।
यह तुलना उस सीमा को अलग करती है जिसे हर खतरा पार करता है, और उस सबूत को जो उसे उजागर करता है: