المبالغة الدفاعية.
يقوم مساعدو الذكاء الاصطناعي بالتحوط ضد حالات فشل لا يمكن حدوثها، مما يلتف حول الكود الآمن بالفعل بفحوصات null زائدة عن الحاجة، وجمل حماية ميتة، وكتل try/catch شاملة تزيد من التعقيد دون تقديم أي حماية فعلية.
##Signs and Symptoms
يكتشف المراجع المبالغة الدفاعية عندما تقضي الدالة عدداً من السطور في الحماية ضد حالات مستحيلة أكثر مما تقضيه في إنجاز العمل الفعلي. العلامات المميزة:
- جمل حماية لشروط يضمنها نظام الأنواع (Types) بالفعل — مثل فحوصات null/undefined لمعامل معلن بأنه غير قابل للقيمة null، أو
typeof x === "string"لمتغير من النوعstring. - جمل حماية مكررة أو مضمنة في شروط أخرى — مثل
if (!user) returnمتبوعة مباشرة بـif (user === null || user === undefined) return. - كتلة
try/catchشاملة حول كود لا يمكنه إطلاق استثناء، وغالباً ما تبتلع الخطأ أو تعيد إطلاقه مجدداً ("فقط للاحتياط"). - منطق للتعامل مع حالات حافة "وهمية" — فروع تتعامل مع مدخلات لا يمكن لأي مستدعٍ توليدها. وجدت Ox Security أن الذكاء الاصطناعي يضيف بشكل روتيني "منطقاً لحالات حافة خيالية".
- نسخ ولصق الكتل الدفاعية بدلاً من استخراجها، بحيث تظهر نفس جملة الحماية في خمسة أماكن مختلفة.
// User هو نوع غير قابل للقيمة null: { profile: { name: string } }
function getDisplayName(user: User): string {
if (!user) return "Unknown"; // ميت: user غير قابل للقيمة null
if (user === null || user === undefined) return "?"; // مكرر، وميت أيضاً
try {
if (user.profile && typeof user.profile.name === "string") { // يضمن النوع هذا بالفعل
const name = user.profile.name;
if (name.length > 0) {
return name.trim() !== "" ? name.trim() : "Unknown";
}
}
return "Unknown";
} catch (e) {
console.error("could not get name", e); // هذه الكتلة لا يمكن أن تطلق استثناءً
return "Unknown";
}
}
تسعة سطور من التحوط تحيط بسطر واحد من الهدف الفعلي. التناقض الذي يلاحظه المراجعون هو أن نفس النموذج غالباً ما يغفل الفحص الوحيد المهم (مثل التحقق من صحة المدخلات الخارجية غير الموثوقة) بينما يفرط في حماية الاستدعاءات الداخلية — حيث تتوزع المبالغة الدفاعية وفقاً لشعور النموذج العام، وليس بناءً على نموذج التهديدات الفعلي.
##Reasons for the Problem
لماذا تنتجها النماذج؟
- النفور من المخاطرة للرمز التالي / ميزة "المساعدة" في RLHF. يتم ضبط النماذج لتبدو دقيقة وتتجنب أن تكون "مخطئة". إن إصدار جملة حماية إضافية أو كتلة
try/catchيمثل مخاطرة منخفضة لهدف متنبئ الرمز التالي ويظهر على أنه عمل دقيق، لذلك يتم تكراره بكثرة. أشار أحد المراجعين إلى أن سلاسلif (array && array.length > 0)المتكررة هي "علامة على أن النموذج ليس واثقاً تماماً من تدفق الكود" — حيث تمثل جمل الحماية تحوطاً ضد عدم يقين النموذج نفسه. - غياب سياق تدفق البيانات على مستوى المستودع بأكمله. لا يستطيع النموذج رؤية أن المستدعي قد تحقق بالفعل من صحة المعامل، أو أن نوع TypeScript يجعل فرعاً معيناً غير قابل للوصول. تُظهر ورقة arXiv 2509.20491 (روائح الكود الخاصة بالذكاء الاصطناعي) أن نماذج اللغة الكبيرة "تواجه صعوبة مع روائح الكود التي تعتمد على التدفق أو الحساسة للقيمة" — وعندما لا تتمكن من استنتاج التدفق، فإنها تلجأ افتراضياً إلى حماية كل شيء محلياً.
- المحاكاة العمياء للأوامر البرمجية الأمنية (Security-prompt cargo-culting). وجدت الأبحاث حول كتابة الأوامر لإنشاء كود آمن أنه عند الطلب من النماذج "جعل الكود آمناً"، فإنها "تضيف كتل try-catch كتدبير أمني مستقل دون تحسينات أمنية أخرى... ويحدث هذا عادةً عندما لا تتمكن من تحديد ثغرات أمنية معينة". فتحل المبالغة الدفاعية محل الفهم الفعلي.
- انحياز بيانات التدريب نحو كود مطول بأسلوب تعليمي يوضح كل فحص كجزء من عملية الشرح البرمجي، بالإضافة إلى رفض إعادة الهيكلة: وجدت Ox Security تجنباً لإعادة الهيكلة بنسبة 80-90% في أكواد الذكاء الاصطناعي وإفراطاً في التخصيص بنسبة 80-90% — فالنموذج يضيف فقط، ونادراً ما يزيل.
لماذا يسبب ضرراً؟
- قابليته الصيانة وعبء المراجعة. تصنف ورقة بحثية حول عدم كفاءة نماذج اللغة الكبيرة مع لغة بايثون (arXiv 2503.06327) التحقق المكرر من المدخلات، والمعالجة المفرطة والدفاعية للأخطاء، وشروط الحماية غير الضرورية، وتخلص إلى أن "هذه الأنماط الدفاعية تزيد من التعقيد دون تقديم فوائد أمان متناسبة". يجب على المراجعين قراءة كل فرع ميت للتأكد من أنه ميت بالفعل.
- الصحة، وليس مجرد الفوضى. تخفي الكتل الشاملة التي تبتلع الأخطاء أو تعالجها بشكل عام حالات الفشل الحقيقية؛ فكتلة
try/catchالتي تعيد قيمة افتراضية تحول الخطأ إلى سلوك خاطئ صامت. وتخلق جمل الحماية أيضاً فروعاً ميتة تجبر الاختبارات على تغطيتها، مما يؤدي إلى تضخيم نسبة تغطية الاختبارات باختبارات عديمة المعنى (وهي نتيجة أخرى توصلت إليها Ox). - مضاعفة الديون التقنية. وجد تحليل GitClear لعام 2025 أن حصة إعادة الهيكلة من السطور المتغيرة انخفضت من 25% (2021) إلى أقل من 10% (2024) بينما ارتفعت السطور المنسوخة/الملصقة إلى 12.3% ونمت الكتل المكررة بمقدار 8 أضعاف تقريباً. الكود الجاهز الدفاعي هو بالضبط نوع الكود الذي يتم استنساخه بدلاً من استخراجه، وترتبط الكتل المستنسخة بـ 15-50% عيوب إضافية.
- التعقيد الإدراكي المرتفع لكل دالة يجعل العثور على المنطق الحقيقي أصعب، مما يبطئ أي تغيير مستقبلي.
##Treatment
تكتيكات المراجعة وصياغة الأوامر
- اجعل العقد واضحاً لتصبح جمل الحماية غير ضرورية بشكل مثبت. قل للنموذج: "
userغير قابل للقيمة null وقد تم التحقق من صحته بالفعل من قبل المستدعي — لا تعيد فحصه مجدداً". اعتمد على الأنواع: مع تشغيل خيارstrictNullChecks، ستقوم القاعدة@typescript-eslint/no-unnecessary-conditionبتحديد جمل الحماية الميتة بالنيابة عنك. - اطلب دفاعاً متناسباً، وليس دفاعاً شاملاً. وجّه النموذج: "لا تعالج سوى الأخطاء التي يمكنك وصف محفز ملموس لها. تحقق من صحة المدخلات غير الموثوقة/الخارجية عند الحدود؛ وثق بالاستدعاءات الداخلية". هذا يفصل التحقق الحقيقي من صحة المدخلات (احتفظ به) عن حالات الحافة الوهمية (احذفها).
- اطلب من النموذج تشغيل أداة الفحص/مصحح الأنواع وإزالة ما يحدده من علامات تحذيرية قبل إرجاع الكود — مما يغلق حلقة التقييم بالطريقة التي يوصي بها ممارسو الوكلاء البرمجية (استخدام أدوات الفحص، ومصححات الأنواع، والاختبارات كإشارات تقييم تلقائية).
- اطلب منه الحذف، وليس الإضافة فقط: "أعد الهيكلة للحصول على الحد الأدنى من الكود الذي يلبي المواصفات؛ وأزل الفروع غير القابلة للوصول وكتل catch التي لا يمكن أن تنطلق". هذا يواجه الانحياز الموثق لـ "تجنب إعادة الهيكلة".
إعادة الهيكلة
حدد الحركات الكلاسيكية: إزالة الكود الميت (Remove Dead Code)، ودمج التعبيرات الشرطية (Consolidate Conditional Expression)، واستبدال الشروط المتداخلة بجمل حماية (Replace Nested Conditional with Guard Clauses)، و(بالنسبة للمدخلات التي تحتاج بالفعل إلى فحص) تقديم تأكيد / التحقق مرة واحدة عند الحدود (Introduce Assertion / validate once at the boundary) بدلاً من التكرار. وحيثما تم نسخ ولصق نفس جملة الحماية، استخدم استخراج الدالة (Extract Function).
// بعد — يضمن النوع عدم كونه null؛ التحقق لمرة واحدة، ولا توجد كتلة catch وهمية
function getDisplayName(user: User): string {
const name = user.profile.name.trim();
return name || "Unknown";
}
إذا كانت القيمة غير موثوقة بالفعل، فتحقق من صحتها مرة واحدة عند الحدود ودع بقية الكود يثق بالنوع الذي تم تضييقه الآن:
// الحدود: تحليل/التحقق من صحة المدخلات غير الموثوقة لمرة واحدة
const user = UserSchema.parse(rawInput); // يطلق استثناءً عند وجود بيانات سيئة، هنا، عن قصد
// ...كل ما يتبع يأخذ User متحقق من صحته ولا يحتاج إلى إعادة حماية
قاعدة ذهبية للمراجعين: يجب أن تجيب كل جملة حماية وكل كتلة catch على السؤال التالي: "ما هو المستدعي أو المدخل الملموس الذي يحفز هذا؟" إذا كانت الإجابة "لا شيء — فقط للاحتياط"، فقم بحذفها.
##Detected by
- typescript-eslint @typescript-eslint/no-unnecessary-condition — منع الشروط غير الضرورية (no-unnecessary-condition)
- ESLint no-useless-catch — منع كتل catch عديمة الفائدة (no-useless-catch)
- SonarSource (SonarQube / eslint-plugin-sonarjs) RSPEC-2589 — Boolean expressions should not be gratuitous (always-true/false conditions) — منع التعبيرات المجانية (no-gratuitous-expressions)
- SonarSource (SonarQube / eslint-plugin-sonarjs) RSPEC-3776 — Cognitive Complexity (nested defensive guards inflate it; proxy detector) — التعقيد الإدراكي (cognitive-complexity)