7 نصائح لتجنب الاختراق (npm و pnpm و bun)

BBetter Stack
Computing/SoftwareInternet Technology

Transcript

00:00:00إذا قمت بتثبيت حزم NPM، فأنت مستهدف. ربما ليس اليوم، ربما ليس هذا الأسبوع، لكن الأمر
00:00:05قادم بالتأكيد. لقد تعرضت المئات من الحزم للاختراق في الأشهر القليلة الماضية فقط،
00:00:09والأمر لا يتباطأ. لذا بدلاً من القلق في كل مرة يظهر فيها أحد هذه الأمور،
00:00:14قمت بالفعل بتأمين إعداداتي الخاصة عبر NPM و PNPM و BUN، وتبين أن معظم
00:00:18الأشياء التي كانت ستنقذك تستغرق حوالي 30 ثانية للإعداد. لذا في هذا الفيديو،
00:00:23سأطلعك على التغييرات السبعة التي أجريتها، من سطر واحد في التكوين
00:00:27يقتل الهجوم الأكثر شيوعاً مباشرة، إلى أدوات مجانية قدمها المهاجمون أنفسهم
00:00:30ستكتشفهم قبل أن تصل الحزمة حتى إلى جهازك. لنبدأ في ذلك.
00:00:39الأول والأكثر بساطة هو مجرد التوقف عن تنزيل حزم جديدة. معظم هجمات سلسلة
00:00:44التوريد يتم اكتشافها في غضون ساعات، لذا إذا لم تقم بتثبيت إصدارات جديدة تماماً في اللحظة
00:00:48التي يتم طرحها فيها، فإنك تتجنب معظم هجمات سلسلة التوريد هذه تماماً. جميع مديري حزم
00:00:52Node.js الرئيسيين يقدمون الآن شكلاً من أشكال تحديد عمر الإصدار. بالنسبة لـ NPM، تضبط هذا في ملف
00:00:57NPM RC، ويتم ضبطه بالأيام. بالنسبة لـ PNPM، يمكنك ضبطه عالمياً في ملف pnpm-config.yaml،
00:01:03أو يمكنك ضبطه على أساس كل مشروع باستخدام ملف مساحة عمل PNPM، وهذه القيمة يتم ضبطها بالفعل
00:01:08بالدقائق. ومن الجدير بالذكر أيضاً أنه منذ PNPM 11، أصبح هذا لديه افتراضي ليوم واحد،
00:01:14لذا حتى لو لم تقم بضبطه، فلا تزال لديك بعض الحماية. بالنسبة لـ BUN، تضبط هذا في
00:01:17ملف bunfig.toml، إما عالمياً أو مرة أخرى لكل مشروع، ويتم ضبط هذا بالثواني.
00:01:23يدهشني حقاً أنه عبر ثلاثة مديري حزم لنفس الإعداد، توصلنا إلى
00:01:27ثلاث قيم مختلفة، وهو ما يلخص هذا النظام البيئي بشكل جيد. بمجرد ضبط هذا،
00:01:32إذا قمت بعد ذلك بتثبيت حزمة مثل tanstack's react start، يمكنك رؤية أنها لا تثبت
00:01:36الإصدار، بل تثبت الإصدار الأحدث، الذي يتناسب مع معيار عمر الإصدار الخاص بي، والذي بالنسبة لي
00:01:41كان قبل أسبوع واحد. الآن إذا احتجت في أي وقت إلى تجاوز هذا، ربما كانت هناك مشكلة أمنية في
00:01:45الحزمة التي تقوم بتثبيتها، وتحتاج إلى تثبيت أحدث إصدار، فلا يزال بإمكانك القيام بذلك
00:01:49من سطر الأوامر، لكن احذر من النماذج اللغوية الكبيرة (LLMs) أيضاً، حيث رأيت حالات تقوم فيها هذه النماذج
00:01:54باستخدام هذا للتجاوز وتثبيت أحدث الإصدارات على أي حال. شيء آخر يجب مراعاته هو
00:01:59أن MPX و BUNX لا يحترمان هذا الإعداد، فهما لا يزالان يثبتان أحدث إصدار،
00:02:03وهناك طلب سحب (PR) مفتوح في BUN لإصلاح هذا. الآن بينما نحن في هذه الإعدادات، دعنا ننتقل
00:02:07ونوقف تشغيل نصوص التثبيت لـ NPM أيضاً. PNPM و BUN لديهما هذا السلوك افتراضياً،
00:02:12لذا لا يوجد شيء لضبطه لهما. إذا لم تكن تعلم، فعندما تقوم بتثبيت حزمة، يُسمح لهذه الحزمة
00:02:16بالفعل بتشغيل كودها الخاص مباشرة بعد تثبيتها، وقد تم ذلك لحالات استخدام مشروعة
00:02:20مثل تجميع الكود الأصلي (native code) أو تنزيل ملف ثنائي، لكن المشكلة هي أن كل هجوم تقريباً من
00:02:26هجمات سلسلة التوريد استخدم هذه الطريقة لتشغيل كود ضار على جهازك مباشرة بعد التثبيت.
00:02:30إذا وجدت حزمة لديها حاجة مشروعة لأحد هذه النصوص، فلا يزال بإمكانك تمكينه صراحةً.
00:02:34في PNPM، عندما تثبت حزمة تحتوي على نصوص ما بعد التثبيت، فسيخبرك بذلك في الواقع،
00:02:39مثل esbuild هنا، وبعد ذلك يمكنك تشغيل pnpm approved builds لاختيار ما تريد السماح به،
00:02:44وهذا يضبط تكوين pnpm workspace allow builds الخاص بك، ويمكنك أيضاً استخدام
00:02:48علامة allow build في أمر التثبيت للقيام بنفس الشيء. بالنسبة لـ BUN، كما قلت، فإنه يوقف
00:02:52نصوص التثبيت هذه افتراضياً أيضاً، لكن من الجدير بالمعرفة أنه يحتوي بالفعل على قائمة منسقة من
00:02:56الحزم المسموح لها بتشغيل هذه النصوص، ويشمل ذلك الحزم التي قمت بتثبيتها مثل
00:03:00esbuild. يمكنك إلغاء الاشتراك في هذا عن طريق إضافة trusted dependencies إلى package.json الخاص بك،
00:03:04وبهذه الطريقة فقط الحزم التي تسمح بها صراحةً ستكون قادرة على تشغيل نصوص ما بعد التثبيت.
00:03:09يقول أيضاً أنه إذا قمت بضبط المصفوفة على فارغة، فيجب أن يتجاوز ذلك القائمة الافتراضية
00:03:12أيضاً، لكنها لا تعمل على ما يبدو بالنسبة لي، ويبدو أنها خطأ برمجي وجدته على GitHub
00:03:17أيضاً. الحل البديل لهذا في الوقت الحالي هو فقط وضع قيمة واحدة في تلك القائمة، ثم يتم تجاهل القائمة
00:03:21الافتراضية. مع BUN، يمكنك أيضاً تشغيل bun pm untrusted لرؤية الحزم التي تريد تشغيل
00:03:26نصوص ولكنها غير موثوقة بعد، وبعد ذلك يمكنك تشغيل bun pm trust للسماح بواحدة، أو مجرد إضافتها إلى مصفوفة
00:03:30trusted dependencies. يمكنك أيضاً تعطيل النصوص تماماً في BUN عن طريق تعيين install scripts إلى
00:03:35false في ملف bunfig العام الخاص بك. بالنسبة لـ NPM، هذا أصعب قليلاً. بصدق، فقط لا تستخدم
00:03:40NPM، استخدم PNPM، ولكن إذا كان عليك حقاً استخدام NPM لسبب ما، يمكنك الحصول على سلوك قائمة سماح
00:03:45مماثل باستخدام حزمة Lavamote's allow scripts NPM. بهذه الطريقة، تكون مجرد قائمة سماح في
00:03:50package.json الخاص بك أيضاً. النصيحة الثالثة هي حظر التبعيات القائمة على git. مع NPM، يمكن
00:03:55لتبعية أن تُعلن فعلياً كعنوان git url، والذي يتجاوز السجل، ويمكنه حتى شحن ملف NPM RC الخاص به الذي
00:04:01يعيد تمكين نصوص دورة الحياة. هذه في الواقع واحدة من الحيل المستخدمة في هجوم سلسلة توريد NPM
00:04:05الأخير الذي ضرب tan stack. يمكنك إيقاف هذا عن طريق ضبط allow git على none في تكوين NPM الخاص بك،
00:04:10مما يحظرها تماماً، والخيار الآخر هو ضبط هذا على root، والذي يسمح بالفعل بـ git-based
00:04:15التبعات ليتم تثبيتها، ولكن فقط إذا تم الإعلان عنها في root package.json الخاص بك، لذا على الأرجح
00:04:19مضبوطة صراحةً من قبلك. بالنسبة لـ PNPM، هذا الخيار هو block exotic sub-dependencies. عندما يتم ضبط هذا على true،
00:04:25فقط التبعيات المباشرة، أي تلك التي أدرجتها في root package.json الخاص بك، يمكنها استخدام مصادر
00:04:29غريبة مثل مستودعات git أو عناوين URL المباشرة. هذا الخيار غير موجود في Bun حتى الآن، ولكن هناك
00:04:35طلب سحب (PR) مفتوح لإضافة هذا، لذا ربما سنراه قريباً. كمكافأة لإنهاء النصائح التي تتضمن تغييرات
00:04:40التكوين، يحتوي PNPM بالفعل على إعداد trust policy، والذي يمكننا ضبطه على no downgrade، وهذا يعني
00:04:45أن PNPM سيفشل إذا انخفض مستوى ثقة الحزمة مقارنة بإصدارها السابق. لذا إذا كانت الحزمة
00:04:50منشورة سابقاً بواسطة ناشر موثوق، ولكنها الآن لديها فقط دليل على المصدر أو
00:04:55لا يوجد دليل ثقة، فسيفشل التثبيت. يجب أن يساعد هذا في اكتشاف تلك الهجمات التي تجد طرقاً لـ
00:05:00تجاوز عمليات النشر المعتادة. النصيحة رقم أربعة هي على الأرجح الأقوى، وهي
00:05:04استخدام أداة تنظر فعلياً إلى الحزم التي تثبتها ويمكنها تدقيقها قبل أن
00:05:08تلمس جهازك. لهذا، لدينا أداتان قويتان ومجانيتان تماماً. الأولى هي
00:05:14MPQ. يمكنك إعداد هذا عن طريق إنشاء اسم مستعار (alias) لأوامر NPM أو PNPM أو Bun العادية الخاصة بك،
00:05:18بحيث أنه في كل مرة تقوم فيها بتشغيل تثبيت، فإنه يتحقق فعلياً من بعض الأشياء. سوف يبحث عن
00:05:23نقاط الضعف المعروفة مقابل قاعدة بيانات Snyk، ويضع علامة على أي حزمة عمرها أقل من 22 يوماً. سوف
00:05:28يكتشف الـ typo squatting، وهو المكان الذي نشر فيه شخص ما حزمة مثل express بحرف s مكرر، آملاً
00:05:32أن ترتكب خطأ مطبعياً وتثبت تلك بدلاً منها. سيتحقق من توقيع السجل و
00:05:37دليل المصدر. سيحذرك عندما تحتوي حزمة على نصوص ما قبل وما بعد التثبيت، وفوق كل ذلك
00:05:41فهو يتحقق من أشياء أساسية مثل عدد التنزيلات، إذا كان هناك ملف readme، ورخصة، وعنوان URL للمستودع،
00:05:46وما هو البريد الإلكتروني للمسؤول، وما إذا كان هذا النطاق لا يزال مسجلاً. يقوم بكل هذا
00:05:51ثم يقدم لك تقريراً تفاعلياً عن حزمك، حيث لا يزال بإمكانك تحديد ما إذا كنت
00:05:55تريد تثبيتها. الثانية هي socket firewall. هذه في الواقع هي التي أستخدمها،
00:05:59ومرة أخرى، تقوم بإنشاء اسم مستعار لها لكل مديري الحزم المعتادين لديك، وهذه تدعم
00:06:03أشياء خارج JavaScript أيضاً، مثل Python و Rust. مشابه لـ MPQ، عندما تقوم بتشغيل تثبيت مع
00:06:08هذا، فإنه سيتحقق من socket ويحظر أي حزم ضارة مؤكدة من قبل البشر، وسيحذرك أيضاً
00:06:12من أي تهديدات مكتشفة بواسطة الذكاء الاصطناعي، وهي التهديدات التي لم تتم مراجعتها من قبل البشر بعد. لكي أكون صادقاً،
00:06:16لقد اخترت socket firewall بشكل أساسي لأنها الأداة التي سمعت عنها أولاً، لكنني أثق أيضاً في socket لأنهم
00:06:21اكتشفوا الكثير من الحزم الضارة مؤخراً، وحتى أن المهاجمين أجروا مقابلة قالوا فيها
00:06:25إن socket ستكتشف البرامج الضارة قبل أن تصل الحزمة حتى إلى جهازك، وهو إعلان جيد جداً
00:06:30لهم، ويعجبني أيضاً أنها تدعم أكثر من مجرد JavaScript مع UV pip و cargo.
00:06:35إذا قمت بتثبيت أي منهما، فستحتاج إلى التأكد من مسح ذاكرة التخزين المؤقت لمدير الحزم
00:06:38للتأكد فقط من أن كل حزمة مثبتة يتم فحصها بواسطة جدار الحماية. النصيحة الخامسة تتعلق بملفات القفل (lock files) الخاصة بك.
00:06:42ملفات القفل الخاصة بك هي المكان الذي يوجد فيه عنوان التنزيل الفعلي لكل حزمة، والمشكلة هي،
00:06:47لا أحد يراجع ملفات القفل الخاصة به في طلب سحب (PR). لذا إذا قام شخص ما بفتح PR ضد
00:06:51المستودع الخاص بك، فيمكنه تعديل عنوان URL تم حله بهدوء ليشير إلى حزمة يتحكمون فيها، وأيضاً ضبط
00:06:56تجزئة النزاهة المطابقة (matching integrity hash) بحيث لا يبدو أي شيء غير طبيعي. الآن في المرة التالية التي تقوم فيها بتشغيل تثبيت، أنت تسحب
00:07:01الكود من خادم المهاجم. الخبر السار هو، إذا كنت تستخدم PNPM، فأنت غير عرضة
00:07:05لهذا الهجوم. فهو لا يحتفظ بمصادر الجدول تلك التي يمكن تبديلها، كما أنه لن يثبت
00:07:09أي شيء موجود في ملف القفل، ولكن لم يتم الإعلان عنه في package.json الخاص بك. لم أتمكن من العثور على أي
00:07:14معلومات ملموسة عن bun، لذا قد يظل هذا عرضة لهذا. الحل إذا لم تكن
00:07:18تستخدم PNPM هو استخدام أداة تسمى lockfileLint. تقوم بتثبيت هذا كتبعية تطوير، وهي تتحقق من
00:07:23ملف القفل الخاص بك، وتتحقق من حل كل حزمة من مضيف موثوق، مثل سجل NPM. هي تتحقق من
00:07:28عناوين URL المحلولة تطابق اسم الحزمة، وتتحقق أيضاً من أن تجزئات النزاهة حقيقية
00:07:32أيضاً. إذا اشتبهت في أن شيئاً ما قد تم العبث به، فسيفشل التثبيت. النصيحة 6 هي التوقف
00:07:37عن استخدام NPM install في CI والإنتاج، وبدلاً من ذلك استخدم clean install. الأمر لهذا في NPM هو
00:07:42مجرد npm ci، وبالنسبة لـ bun و PNPM، تقوم في الواقع بإضافة علامة frozen lockfile، لكن لديهم أيضاً أوامر
00:07:47CI كاسم مستعار تقوم بنفس الشيء. PNPM يستخدم هذا بالفعل افتراضياً إذا اكتشف أنه
00:07:52يعمل في بيئة CI. أمر التثبيت النظيف يثبت بالضبط ما هو موجود في ملف القفل،
00:07:57لا شيء آخر، وإذا لم يتفق ملف القفل و package.json، فإنه يتوقف فقط ويظهر خطأ،
00:08:01بدلاً من التخمين والتثبيت على أي حال. لذا يجب أن يمنع هذا مهاجماً من تسريب
00:08:05إصدار متبادل. لا شيء من هذا يعمل إذا لم تكن تقوم بـ commit لملف القفل الخاص بك في المقام
00:08:09الأول، لذا تأكد من وجود ذلك في التحكم في الإصدار وليس في gitignore الخاص بك، وسأعترف عندما
00:08:13بدأت لأول مرة في تعلم البرمجة كطفل، اعتقدت أنك من المفترض أن تتجاهل هذه، لذا أنا سعيد
00:08:17لأنني تعلمت القيام بـ commit لها. لذا تلك النصائح الست كانت في الغالب تكويناً وأدوات، ولكن هناك أيضاً بعض
00:08:21العادات التي يمكنك تبنيها لمساعدتك على تجنب الهجمات. الأولى هي التوقف عن التحديث الأعمى
00:08:25لكل شيء. ربما قمت بتشغيل npm update أو إصدارات أخرى من هذا الأمر من قبل، وقممت فقط
00:08:30بتحديث كل تبعية إلى أحدث إصدار لها دفعة واحدة، لكن هذا هو نوع السلوك الدقيق الذي يأمل فيه هؤلاء
00:08:35المهاجمون، لذا اذهب بالفعل من خلال هذه الترقيات واسأل نفسك لماذا تحتاج إلى
00:08:39ترقيتها. العادة الثانية تصبح أكثر صلة، فقط استخدم عدداً أقل من الحزم. كل
00:08:43تبعية تضيفها هي سطح هجوم آخر، ومعظم هذه الهجمات تنتشر من خلال تبعيات
00:08:48تبعية، لذا من الجدير حقاً التساؤل لماذا تحتاج إلى تلك المكتبة. بعض الأمثلة الشائعة التي أراها لـ
00:08:53هذا هي أشياء مثل Lodash للوظائف التي يمكن القيام بها بمقتطف صغير من الكود، أو Axios عندما
00:08:58fetch يمكن أن تفعل نفس الشيء. السبب الذي أقوله أن هذا أصبح أكثر صلة هو في عصر
00:09:03البرمجة الوكيلية (agentic coding)، من السهل جداً جعل الذكاء الاصطناعي يكتب بضع وظائف لك بدلاً من استخدام تبعية.
00:09:08العادة الثالثة هي تثبيت التبعيات على إصدار محدد، لذا فإن الترقية هي دائماً خيار متعمد
00:09:12، ولكن فقط اعلم أن هذا يقفل فقط الحزم التي تختارها في package.json الخاص بك،
00:09:17ويمكن لتبعات تبعياتك أن تستمر في استخدام نطاقاتها الخاصة، وهو بالضبط سبب أهمية
00:09:21فترة التبريد من النصيحة 1. كل هذه مجتمعة يجب أن تمنحك بعض راحة البال بأنك لن
00:09:25تثبت حزمة مخترقة عن طريق الخطأ. إنها ليست لا تقهر، لكنها أفضل بكثير من
00:09:29لا شيء. النصيحة النهائية هي فقط تشغيل كل شيء في حاوية تطوير (dev container) صلبة، لكنني وجدت دائماً
00:09:34أن ذلك يمثل بعض المتاعب، لذا ينتهي بي الأمر بالعودة إلى التطوير المحلي. إذا كنت تعرف أي طريقة أخرى
00:09:38لحماية نفسك من هذا النوع من الهجمات، فأخبرني في التعليقات أدناه، بينما أنت هناك
00:09:42اشترك، وكما هو الحال دائماً أراك في المرة القادمة.

Key Takeaway

تأمين بيئة التطوير يتطلب تقييد عمر الإصدارات، وتعطيل نصوص التثبيت التلقائية، واستخدام أدوات تدقيق خارجية لضمان سلامة التبعيات قبل تثبيتها.

Highlights

  • تجنب تثبيت الإصدارات الجديدة فور طرحها يقلل بشكل كبير من هجمات سلسلة التوريد عبر مديري الحزم NPM وPNPM وBUN.

  • يؤدي تعطيل نصوص التثبيت (install scripts) إلى حظر تشغيل الكود الضار تلقائياً فور تثبيت الحزمة.

  • توفر أدوات مثل Socket Firewall وMPQ ميزة تدقيق الحزم والتحقق من التهديدات الأمنية قبل وصولها إلى الجهاز.

  • استخدام أمر npm ci بدلاً من npm install في بيئات الإنتاج يضمن تثبيت الحزم بدقة وفقاً لملف القفل (lock file).

  • يؤدي تقليل عدد التبعيات في المشاريع البرمجية إلى تقليص سطح الهجوم المتاح للمهاجمين.

Timeline

التحكم في تحديثات الحزم

  • يمنع تحديد عمر أدنى للإصدارات معظم هجمات سلسلة التوريد التي تستهدف الإصدارات الجديدة.
  • تختلف إعدادات ضبط عمر الإصدار بين الأيام في NPM، والدقائق في PNPM، والثواني في BUN.
  • لا تحترم أدوات التنفيذ مثل MPX وBUNX إعدادات عمر الإصدار الحالية.

معظم هجمات سلسلة التوريد تُكتشف في غضون ساعات قليلة. يؤدي فرض فترة انتظار قبل تثبيت أي تحديث إلى تجنب هذه الهجمات بشكل استباقي. توفر مديري الحزم إعدادات تكوين خاصة لكل منها للتحكم في هذا السلوك، مما يضمن تثبيت إصدارات ناضجة ومفحوصة بدلاً من الأحدث مباشرة.

تقييد نصوص التثبيت

  • يعد إيقاف تشغيل نصوص ما بعد التثبيت (post-install scripts) حماية أساسية ضد الأكواد الضارة.
  • يوفر PNPM وBUN آليات قائمة سماح (allowlist) لتحديد الحزم الموثوقة فقط التي يُسمح لها بتشغيل نصوص التثبيت.
  • يمكن لمستخدمي NPM استخدام حزم خارجية مثل Lavamote's allow scripts لتحقيق حماية مشابهة.

تستغل معظم الهجمات ميزة تشغيل الكود تلقائياً بعد التثبيت لأغراض ضارة. من خلال إلغاء الاشتراك في هذه الميزة افتراضياً وحصرها في الحزم الضرورية والموثوقة فقط، يتم شل قدرة أي حزمة مخترقة على تنفيذ أوامر غير مصرح بها على الجهاز.

حظر التبعيات القائمة على المصادر الخارجية

  • يؤدي حظر التبعيات التي تستخدم عناوين git إلى منع تجاوز سجل الحزم الموثوق.
  • يمنع خيار block exotic sub-dependencies في PNPM استخدام مصادر غير تقليدية للتبعيات الفرعية.
  • يساعد إعداد trust policy في PNPM على اكتشاف التغييرات المفاجئة في مستوى ثقة الحزم عند التحديث.

تسمح بعض مديري الحزم بتثبيت التبعيات مباشرة من عناوين URL أو مستودعات git، وهي ثغرة شائعة تُستخدم في الهجمات. تقييد هذه المصادر يضمن أن جميع الحزم تأتي من سجلات موثوقة ومفحوصة، مما يمنع المهاجمين من شحن ملفات تكوين ضارة خفية.

استخدام أدوات التدقيق الخارجية

  • تعتبر أدوات Socket Firewall وMPQ وسيلة قوية لفحص الحزم والتحقق من سلامتها قبل التثبيت.
  • تكتشف هذه الأدوات التهديدات مثل التلاعب بالأسماء (typo squatting) والبرمجيات الضارة المكتشفة بالذكاء الاصطناعي.
  • تدعم هذه الأدوات لغات برمجة متعددة تتجاوز مجرد JavaScript، بما في ذلك Python وRust.

تعتمد هذه الأدوات على قواعد بيانات أمنية ومعالجة الذكاء الاصطناعي لتحليل سلوك الحزم. من خلال دمجها في أوامر التثبيت، يتلقى المطور تقريراً تفاعلياً يوضح التهديدات المحتملة، مما يسمح باتخاذ قرار واعي قبل السماح لأي حزمة بالوصول إلى نظام الملفات المحلي.

إدارة ملفات القفل والعادات الأمنية

  • يعد الالتزام بملفات القفل (lock files) في نظام التحكم بالإصدار خطوة ضرورية لمنع العبث بعناوين التنزيل.
  • يمنع استخدام أوامر التثبيت النظيف (npm ci) التغييرات غير المتوقعة في بيئات الإنتاج وCI.
  • يؤدي تقليل التبعيات غير الضرورية واعتماد إصدارات محددة إلى تقليل سطح الهجوم بشكل عام.

ملفات القفل هي المرجع الأساسي لما يتم تثبيته؛ التلاعب بها يعني سحب كود من خادم المهاجم. يساعد استخدام أوامر التثبيت الصارمة ومراجعة ملفات القفل في طلبات السحب (PR) على ضمان اتساق التبعيات. العادات البرمجية السليمة، مثل كتابة وظائف بسيطة بدلاً من استيراد مكتبات ضخمة، تُعزز بشكل طبيعي من أمان المشروع.

Community Posts

No posts yet. Be the first to write about this video!

Write about this video