
مساعدة محلية لتثبيت الحزم تثق كثيرًا في أسماء الحزم المقدمة من المستدعي. في بيئة yeoman-environment، يمكن تثبيت المولدات المفقودة دون تأكيد المستخدم، مما يحول بيانات المشروع الخاضعة لسيطرة المهاجم إلى مسار لتثبيت الحزم وتنفيذ التعليمات البرمجية.
أداة مساعدة لتثبيت الحزم المحلية وثقت كثيرًا في أسماء الحزم المقدمة من المتصل. في yeoman-environment، كان من الممكن تثبيت المولدات المفقودة دون تأكيد المستخدم، مما يحول بيانات المشروع الخاضعة لسيطرة المهاجم إلى مسار لتثبيت الحزم وتنفيذ الأكواد.
وجدت هذه المشكلة أثناء مراجعة generator-jhipster مع سؤال أمني بسيط في ذهني:
هل يمكن لبيانات المشروع الخاضعة لسيطرة المهاجم أن تجعل أداة المطور تجلب وتنفذ كودًا من طرف ثالث قبل أن يطلب المستخدم ذلك صراحةً؟
في هذه الحالة، كانت الإجابة نعم.
ما بدا في البداية كمشكلة في JHipster تحول إلى جذر أعمق في yeoman-environment.
السلوك الضعيف كان في تدفق تثبيت المولدات المحلية في Yeoman، حيث تم تثبيت الحزم المفقودة المقدمة من المتصل تلقائيًا دون تأكيد المستخدم. في مستهلك سفلي يمرر أسماء حزم يتحكم فيها المهاجم عبر هذا المسار، كان ذلك كافياً لإنشاء سلسلة حقيقية لتثبيت الحزم وتنفيذ الأكواد.
أصبحت تلك المشكلة CVE-2026-42089.
yeoman-environment: yeoman-environment على GitHub
الحزمة: yeoman-environment (npm)
CVE: CVE-2026-42089
أثر هذا على yeoman-environment، طبقة التشغيل وراء تدفق تحميل المولدات وتهيئتها في Yeoman. يصف المشروع الرسمي هذا المكون بأنه المسؤول عن دورة حياة المولد واكتشافه، واعتبارًا من 26 يونيو 2026، أدرجت صفحة الحزمة على npm 1,466,426 تنزيلًا أسبوعيًا، مما يجعل هذه حزمة منتشرة على نطاق واسع في النظام البيئي لأدوات JavaScript.
تكوين المشروع الخاضع لسيطرة المهاجم -> أسماء حزم المولدات المقدمة من المتصل -> yeoman-environment يقوم بتثبيت الحزم المفقودة بصمت -> الأداة السفلية تحمل كود المولد المثبت -> تثبيت الحزم وتنفيذ الأكواد أثناء تهيئة CLI
yeoman-environment هو طبقة التشغيل وتحميل المولدات وراء الأدوات المبنية على Yeoman.
من بين أمور أخرى، يتعامل مع:
وهذا يعني أنه يقع مباشرة على حد الثقة.
السؤال ذو الصلة ليس ما إذا كان Yeoman "مجرد أداة محلية".
السؤال ذو الصلة هو ما إذا كان الإدخال غير الموثوق يمكن أن يؤثر على سلوك تثبيت الحزم وتحميل الأكواد.
في هذه الحالة، كان بإمكانه ذلك.
لم أكن أبحث عن تلف الذاكرة أو أخطاء الانهيار فقط هنا.
الهدف الأقوى كان سطح التمديدات وحل الحزم.
أي نظام يقوم بـ:
يستحق فحصًا دقيقًا.
هذا صحيح بشكل خاص عندما يمكن للمستهلك السفلي استخلاص أسماء الحزم هذه من بيانات محلية للمشروع.
هذا هو بالضبط النوع من الأماكن حيث يمكن أن يصبح التكوين العادي بهدوء حدًا أمنيًا.
كان ذلك المكان المناسب للبحث.
أعدت إنتاج السلوك أولاً من خلال generator-jhipster.
المسار المهم كان:
.yo-rc.json محلي يعلن عن حزمة مخطط (blueprint)وهذا يعني أنه حتى الأمر البسيط مثل:
jhipster --help
كان بإمكانه الوصول إلى تثبيت الحزم قبل اكتمال الأمر المطلوب.
هذا فشل حقيقي في حد الثقة.
ساعد المشغل السفلي في كشفه، لكن السلوك الافتراضي غير الآمن كان في Yeoman.
الخلل كان بسيطًا.
في yeoman-environment، كانت الطريقة الضعيفة هي:
async installLocalGenerators(packages) {
const entries = Object.entries(packages);
const specs = entries.map(([packageName, version]) => `${packageName}${version ? `@${version}` : ''}`);
const installResult = await this.repository.install(specs);
const failToInstall = installResult.find(result => !result.path);
if (failToInstall) {
throw new Error(`Fail to install ${failToInstall.pkgid}`);
}
await this.lookup({ packagePaths: installResult.map(result => result.path) });
return true;
}
تلك الطريقة قامت بتثبيت أسماء الحزم المقدمة من المتصل مباشرةً عبر:
this.repository.install(specs)
دون مطالبة المستخدم أولاً.
هذا هو الثغرة الأساسية.
لأن أسماء الحزم لا يجب أن تأتي من مصدر موثوق.
إذا اشتقها مستهلك سفلي من بيانات مشروع يتحكم فيها المهاجم، فإن سلسلة الاستغلال تكون مباشرة:
هذا ليس مجرد "حدث تثبيت حزمة".
هذا هو إدخال غير موثوق يعبر إلى مصرف تثبيت الحزم دون حد موافقة صريحة.
التمييز المهم هو التثبيت الصامت من إدخال غير موثوق.
هناك فرق حقيقي بين:
هذا التمييز مهم أكثر عندما تصبح الحزمة قابلة للتحميل فورًا بعد ذلك.
المشكلة لم تكن أن المولدات من طرف ثالث موجودة.
المشكلة كانت أن Yeoman تعامل أسماء الحزم المقدمة من المتصل على أنها قابلة للتثبيت افتراضيًا دون تأكيد المستخدم.
هذا يجعل افتراضات الثقة السفلية غير الآمنة أسوأ بشكل ملموس.
هذا هو بالضبط سبب إضافة الإصلاح لبوابة تأكيد.
استخدمت طبقتين من الإثبات لأنهما أظهرتا كلاً من السبب الجذري والتأثير السفلي الحقيقي.
استخدم الإثبات الأول generator-jhipster غير معدّل.
أنشأت مشروعًا مع ملف .yo-rc.json جذري يشير إلى حزمة مخطط لم تكن مثبتة مسبقًا، ثم نفذت:
jhipster --help
هذا تسبب في قيام JHipster بتمرير المخطط المفقود إلى تدفق تثبيت المولدات المحلية في Yeoman قبل اكتمال المساعدة.
النتيجة المهمة كانت:
هذا أثبت حالة المشغل الحقيقية بوضوح.
استخدم الإثبات الثاني مستودعًا محليًا مسيطرًا عليه وحزمة مصممة لإظهار آثار جانبية في وقت الاستيراد بأمان.
كان ذلك مهمًا لأنني أردت إظهار القصة الأقوى:
كانت هذه أقوى سلسلة أدلة لأنها نقلت المشكلة إلى ما بعد:
"محاولة تثبيت غير متوقعة"
وإلى:
"مسار التحميل السفلي للكود مع التثبيت هو بالفعل قابل للوصول"
هذه هي النقطة التي يصبح فيها فشل حد الثقة أصعب بكثير في تجاهله.
يثبت PoC الأول سلوك التثبيت الصامت.
يثبت PoC الثاني لماذا هذا السلوك مهم.
كان هذا التقسيم مهمًا.
تقرير يتوقف عند:
"يمكن تثبيت حزمة"
أضعف من تقرير يظهر:
تلك هي القصة الكاملة.
أثناء معالجة الاستشارة والمراجعة المحلية، تم تتبع السلوك إلى إدخال installLocalGenerators() في:
yeoman-environment 2.9.0
وبالتالي فإن النطاق المتأثر كان:
>= 2.9.0 و < 6.0.1
الإصدار المُصلَح كان:
6.0.1
الإصلاح كان صحيحًا وبسيطًا.
في 6.0.1، تم تغيير installLocalGenerators() لإضافة خطوة تأكيد قبل التثبيت ما لم يتم طلب التثبيت القسري صراحةً.
الشكل المُصلَح بدا كالتالي:
async installLocalGenerators(packages, forceInstall = false) {
ثم:
const { aproveInstall } = await this.adapter.prompt({
message: `الحزم التالية تحتاج إلى التثبيت في المستودع المحلي: ${specs.join(', ')}. هل تريد المتابعة؟`,
type: 'confirm',
name: 'aproveInstall',
default: false,
});
إذا رفض المستخدم، يتم إلغاء التثبيت.
هذا هو الإصلاح الصحيح لأنه يعيد حدود الثقة المفقودة:
هذا الإصلاح تم إدخاله في:
78d2af7
عبر:
PR #753
هذا هو بالضبط نوع المعالجة التي تريدها في مشكلة أمنية كهذه:
تم أخذ هذه المشكلة على محمل الجد بشكل معقول لأن التأثير أكثر من مجرد سلوك تجميلي أو مفاجئ.
السلوك الضعيف يمكن أن يؤدي إلى:
متجه CVSS المرتبط بالمشكلة كان:
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H
هذا منطقي لقصة الاستغلال السفلي:
بدأت هذه المشكلة كتقرير خاص ضد generator-jhipster، لأن هذا كان مسار المشغل الحقيقي الذي قمت بالتحقق منه مبدئيًا.
أثناء الفرز، أشار مشرفو JHipster إلى أن سلوك التثبيت التلقائي نفسه كان في yeoman-environment وأشاروا إلى الإصلاح المنبع.
أدى ذلك إلى التحول الصحيح:
قام مشرفو Yeoman بمراجعة المشكلة، وتأكيد النطاق المتأثر، وتتبعها عبر استشارة خاصة.
تم تعيين التقرير لاحقًا:
CVE-2026-42089
كما وثقت تلك الاستشارة مسار المشغل السفلي الحقيقي عبر generator-jhipster.
كان هذا مثالًا جيدًا على لماذا يحتاج الإفصاح المنسق أحيانًا إلى خطوة إضافية:
هنا، كان التكاثر السفلي مفيدًا، لكن الحزمة المنبع كانت المكان الصحيح لـ CVE.
الدرس الرئيسي هنا بسيط:
تثبيت الحزم هو حد أمني، حتى في أدوات المطور المحلية
العديد من الناس يقللون تلقائيًا من مشاكل مثل هذه لأنها تحدث في أدوات CLI.
هذا خطأ.
السؤال الحقيقي ليس ما إذا كانت الأداة محلية.
السؤال الحقيقي هو:
هل يمكن للإدخال غير الموثوق أن يجعل الأداة تجلب وتثق في الكود دون قرار مستخدم صريح؟
في هذه الحالة، نعم.
هذا هو الاستنتاج الحقيقي.
تعزز هذه المشكلة أيضًا شيئًا مهمًا حول أبحاث الثغرات الجيدة:
كان هذا بالضبط شكل هذه CVE.
لم تكن هذه الثغرة حول حمولة مبهرجة.
كانت حول طرح سؤال حد الثقة الصحيح.
أداة سفلية تركت بيانات المشروع المحلية تؤثر على اختيار الحزمة. Yeoman قام بتثبيت الحزمة المفقودة دون تأكيد. بقية سلسلة تحميل الكود قامت بالباقي.
لهذا أصبحت CVE-2026-42089.
تم إصلاحها في yeoman-environment 6.0.1.