api-service نامی ایک Express API ٹیسٹس میں کامیاب ہو سکتی ہے، OCI امیج بنا سکتی ہے، اور پھر بھی ایسا کوڈ شامل کر سکتی ہے جسے منظور کرنے کا کسی کا ارادہ نہیں تھا۔ ایک ڈویلپر package.json میں express کے بجائے expresss شامل کر دیتا ہے؛ پبلک رجسٹری اس ملتے جلتے نام والے پیکیج کو تلاش کر کے منتخب کر لیتی ہے، GitHub Actions کا رنر اسے انسٹال کر دیتا ہے، اور ریلیز امیج میں یہ شامل ہو جاتا ہے۔ حملہ آور کو ریپوزٹری میں ‘رائٹ ایکسیس’ (write access)، چوری شدہ ‘ڈپلائے کی’ (deploy key)، یا پروڈکشن کلسٹر تک رسائی کی ضرورت نہیں پڑتی۔ دفاع کرنے والے کا سوال زیادہ مخصوص اور مفید ہے: ہم ‘ڈیپینڈنسی ریزولیوشن’ (dependency resolution) کے ذریعے بلڈ (build) میں غیر جانچے گئے کوڈ کے داخلے کو کیسے روکیں؟ ہم api-service کے سفر کا جائزہ لیں گے جو اس کی مینیفسٹ (manifest) فائل سے شروع ہو کر CI بلڈ اور پھر شائع شدہ امیج تک جاتا ہے۔
امیج کے ڈپلائمنٹ (deployment) تک پہنچنے سے پہلے بلڈ کا عمل کئی ‘اعتمادی حدود’ (trust boundaries) سے گزرتا ہے۔ ایک پبلک پیکیج رجسٹری مینیفسٹ میں موجود نام کی بنیاد پر کوڈ فراہم کرتی ہے، CI رنر انسٹالیشن کا عمل مکمل کرتا ہے، اور آرٹیفیکٹ رجسٹری (artifact registry) نتیجے میں بننے والے امیج کو بعد میں ڈپلائمنٹ کے لیے محفوظ کر لیتی ہے۔ ایک نقصان دہ پیکیج بلڈ کے دوران کوڈ چلانے کی کوشش کر سکتا ہے، کوئی مضر ‘ٹرانزیٹیو ڈیپینڈنسی’ (transitive dependency) متعارف کروا سکتا ہے، یا رنر کو دستیاب ‘پبلشنگ ٹوکن’ (publishing token) چرا سکتا ہے۔ ذیل میں دیا گیا راستہ ان حدود کو واضح کرتا ہے:
یہ خاکہ ظاہر کرتا ہے کہ ‘ڈیپینڈنسی’ (dependency) کا انتخاب محض کوڈنگ کی مقامی تفصیل نہیں بلکہ سپلائی چین (supply-chain) سے متعلق ایک فیصلہ کیوں ہے۔ جب ‘رنر’ (runner) کسی پیکیج کو حاصل کرتا ہے، تو اس کا مواد ‘بلڈ ان پٹ’ (build input) کا حصہ بن جاتا ہے اور ‘انسٹالیشن ہکس’ (installation hooks) کو چلا سکتا ہے، الا یہ کہ پائپ لائن انہیں محدود کرے۔
‘مینیفسٹ’ (manifest) پہلی حد ہے کیونکہ npm install ہر اعلان کردہ پیکیج کے نام کو کوڈ تلاش کرنے اور حاصل کرنے کی ہدایت کے طور پر لیتا ہے۔ api-service میں، express کے ورژن 4.18.3 کی توقع کی جاتی ہے، جبکہ خود ساختہ نام expresss (ورژن 1.0.0) ایک ٹائپنگ کی غلطی ہے جو کسی مختلف پیکیج کی طرف اشارہ کرتا ہے۔ یہ حملہ ٹائپو اسکواٹنگ (typosquatting) کہلاتا ہے، جس میں حملہ آور ایک ایسا پیکیج شائع کرتا ہے جس کا نام کسی قابلِ اعتماد ڈیپینڈنسی سے ملتا جلتا ہو اور وہ اس کے انتخاب کے لیے انسانی غلطی پر انحصار کرتا ہے۔
‘لاک فائل’ (lockfile) کسی مخصوص ڈیپینڈنسی گراف کے لیے طے شدہ پیکیج ورژن، رجسٹری میٹا ڈیٹا، اور انٹیگریٹی ڈائجسٹ (integrity digest) کو ریکارڈ کرتی ہے۔ یہ ریکارڈ بعد کی تنصیبات کو اسی صورت میں قابلِ اعادہ (reproducible) بناتا ہے جب غلط پیکیج پہلے ہی قبول اور کمٹ (commit) ہو چکا ہو۔ لہٰذا، جائزے (review) میں ناکامی دو جگہوں پر نظر آتی ہے: package.json میں ایک غیر متوقع براہِ راست ڈیپینڈنسی موجود ہوتی ہے، اور package-lock.json میں ایک نئی طے شدہ انٹری اور انٹیگریٹی ویلیو شامل ہوتی ہے۔ پیکیج کا کون سا نام اور لاک فائل کی کون سی انٹری expresss کے اضافے کو جائزے میں ناکام بناتی ہے؟
یہ کوڈ ظاہر کرتا ہے کہ کامیاب تنصیب (installation) اس بات کا ثبوت نہیں ہے کہ مطلوبہ ڈیپینڈنسی (dependency) کا انتخاب کیا گیا تھا۔ انسٹالر نے expresss کو بالکل اسی طرح ریزولو (resolve) کیا جیسا کہ درخواست کی گئی تھی، اور پھر نتیجے کو لاک فائل (lockfile) میں محفوظ کر لیا؛ اس غلطی کا یقینی (deterministic) اعادہ بھی بہرحال ایک غلطی ہی ہے۔ ڈیپینڈنسی کے فرق (diffs) کا جائزہ لینے کا مطلب ہے کسی غیر مانوس نام، غیر متوقع ورژن، یا حال ہی میں شامل کی گئی انٹیگریٹی انٹری (integrity entry) کو ایسے کوڈ کے طور پر دیکھنا جو ریلیز میں شامل ہو رہا ہے۔
جائزے کا اصول لاک فائل اس بات کا ثبوت دیتی ہے کہ انسٹالر نے کیا منتخب کیا۔ یہ اس بات کا ثبوت نہیں دیتی کہ وہ انتخاب مجاز یا محفوظ تھا۔
غلط ہجے والا عوامی (public) نام مینیفسٹ (manifest) سے شروع ہوتا ہے، لیکن نجی (private) پیکیج کو اس وقت بھی تبدیل کیا جا سکتا ہے جب اس کا نام درست ہو۔ ڈیپینڈنسی کنفیوژن (dependency confusion) تب پیدا ہوتی ہے جب کوئی بلڈ (build) کسی غیر مطلوبہ رجسٹری سے پیکیج کو ریزولو کرتی ہے، جس کی عام وجہ یہ ہوتی ہے کہ اسی نام یا زیادہ ورژن نمبر والا کوئی عوامی پیکیج اندرونی پیکیج پر سبقت لے جاتا ہے۔ حملہ آور کو اس رجسٹری میں پبلش کرنے کی صلاحیت درکار ہوتی ہے جس سے بلڈ رجوع کرتی ہے، نہ کہ نجی سورس ریپوزٹری تک رسائی کی۔
api-service کے لیے، اندرونی ڈیپینڈنسی جیسے کہ @acme/auth-client کو ہمیشہ تنظیم کی نجی رجسٹری سے ہی ریزولو ہونا چاہیے۔ ورژن کی وسیع رینج ان مستقبل کے ورژنز کی اجازت دیتی ہے جو بیان کردہ رینج پر پورا اترتے ہوں، جبکہ رجسٹری کی ترجیح (precedence) یہ طے کرتی ہے کہ انسٹالر کہاں تلاش کرے گا۔ لاک فائل کمٹ شدہ (committed) بلڈ کے لیے نتیجے کو محدود کرتی ہے، لیکن یہ رجسٹری میپنگ کو درست نہیں کر سکتی جو ری بلڈ (rebuild) کو غلط سورس کی طرف لے جاتی ہے، اور نہ ہی یہ لاک فائل کی اس دانستہ اپ ڈیٹ سے تحفظ دے سکتی ہے جس کا جائزہ لینے میں نظرثانی کنندگان (reviewers) ناکام رہیں۔ کنٹرول کا ہدف ہر پیکیج اسکوپ کے لیے مطلوبہ رجسٹری سے یقینی (deterministic) ریزولوشن کا حصول ہے۔
یہ موازنہ اس حد کو، جسے ہر خطرہ عبور کرتا ہے، اس ثبوت سے الگ کرتا ہے جو اسے ظاہر کرتا ہے: