يضيف CodeQL 2.26.0 من GitHub استعلاماً مدمجاً يكتشف أنماط حقن الأوامر (Prompt Injection) الخاصة بالذكاء الاصطناعي، وبدأ هذا التغيير بالفعل في جعل خطوط أنابيب CI تكتشف مخاطر جديدة. الترقية وحدها ليست كافية؛ إذ تحتاج الفرق إلى مجموعة اختبارات تراجع (regression test suite) تضمن بقاء القاعدة فعالة مع تطور الكود.
لماذا تهم أداة اختبار التراجع (regression fixture)
يسمح حقن الأوامر للمهاجم بتمرير تعليمات خبيثة داخل أمر (prompt) يتبعه النموذج اللغوي لاحقاً. باستخدام الاستعلام الجديد، يمكن للتحليل الساكن تتبع البيانات من مصدر غير موثوق إلى نقطة وصول لاستدعاء النموذج (model-calling sink). إذا تم تفعيل القاعدة ببساطة دون التحقق منها أبداً، فقد يؤدي أي إعادة هيكلة (refactor) للكود لاحقاً إلى كسر مسار تدفق البيانات، مما يؤدي إلى اختفاء التنبيه بصمت. تقوم أداة اختبار التراجع بالتقاط المسارات الدقيقة التي يجب أن تُطلق (أو لا تُطلق) القاعدة، مما يحول نتيجة التحليل الساكن إلى "عقد" تفرضه عملية البناء (build).
المكونات الثلاثة لأداة اختبار موثوقة
- مصدر غير موثوق (Untrusted source) – أي دالة تجلب بيانات من خارج قاعدة الكود الموثوقة (مثل نص مشكلة في GitHub، أو حمولة webhook).
- بناء الأمر (Prompt construction) – الكود الذي يجمع طلب النموذج، وعادة ما يكون استدعاءً لـ client SDK.
- نقطة وصول النموذج (Model sink) – طريقة الـ SDK التي ترسل الأمر إلى النموذج. يحتاج محرك تدفق البيانات في CodeQL إلى رؤية استدعاء حقيقي من بيئة الإنتاج الخاصة بك للتعرف على نقطة الوصول.
لا يتم إطلاق الاستعلام إلا عند وجود المكونات الثلاثة معاً.
تنظيم ملفات الاختبار
يحافظ التنسيق التقليدي على سهولة مراجعة المجموعة:
security-fixtures/prompt-injection/
├─ positive/
│ ├─ direct-flow.ts
│ └─ helper-flow.ts
├─ negative/
│ └─ trusted-instruction.ts
└─ expected-alerts.json
تحتوي الملفات الإيجابية (Positive) على كود يجب التنبيه إليه؛ بينما تحتوي الملفات السلبية (Negative) على أنماط آمنة يجب أن تظل صامتة.
كتابة الحالات الإيجابية
يوضح أبسط مثال تدفقاً مباشراً من قيمة غير موثوقة إلى استدعاء النموذج:
import { model } from "./supported-client";
declare function loadIssueBody(id: number): Promise<string>;
export async function summarize(id: number) {
const untrusted = await loadIssueBody(id);
return model.generate({
system: "Summarize the issue",
user: untrusted,
});
}
هنا loadIssueBody هو المصدر غير الموثوق، و model.generate هو نقطة الوصول (sink)، وتمر البيانات دون أي خطوة تنقية (sanitisation)—وهذا هو بالضبط ما صُمم الاستعلام لاكتشافه.
يجب أن تقوم حالة إيجابية ثانية بتوجيه البيانات عبر دالة مساعدة، لإثبات أن التحليل يتبع المسارات غير المباشرة:
function wrapUserInput(input: string) {
return { system: "Summarize the issue", user: input };
}
export async function summarizeViaHelper(id: number) {
const raw = await loadIssueBody(id);
return model.generate(wrapUserInput(raw));
}
ينتمي كلا الملفين إلى المجلد positive/.
كتابة الحالة السلبية
يجب أن تثبت أداة الاختبار السلبية أن مدخلات المستخدم لا يمكنها تغيير تعليمات النموذج. الخطأ الشائع هو افتراض أن دالة تسمى sanitize() تضمن الأمان. لا يعامل المحلل الساكن الاسم كدليل، لذا يجب أن يتجنب الاختبار أي "دالة تنقية وهمية" (sanitisation stub) مضللة:
export async function safeSummarize(id: number) {
const trusted = "Summarize the issue";
const user = await loadIssueBody(id); // not used in the system prompt
return model.generate({
system: trusted,
user: "Static placeholder",
});
}
بما أن البيانات غير الموثوقة لا تصل أبداً إلى حقل system ، يجب أن تظل القاعدة صامتة.
تحديد التوقعات في ملف JSON
يعيش "عقد" المجموعة في ملف expected-alerts.json. وهو يسرد التنبيهات المطلوبة والمسارات المحظورة صراحةً:
{
"required": [
{
"ruleId": "USE_ACTUAL_RULE_ID",
"pathSuffix": "positive/direct-flow.ts"
},
{
"ruleId": "USE_ACTUAL_RULE_ID",
"pathSuffix": "positive/helper-flow.ts"
}
],
"forbiddenPathSuffixes": [
"negative/trusted-instruction.ts"
]
}
استبدل USE_ACTUAL_RULE_ID بالمعرف الموضح في وثائق CodeQL أو مخرجات SARIF. لا تخمن المعرفات؛ فالسلسلة النصية الدقيقة مهمة لعملية فحص CI.
ربط أداة الاختبار بـ CI
- قم بتثبيت إصدار CodeQL CLI المستخدم في خط الأنابيب على الإصدار 2.26.0 (أو أحدث).
- قم ببناء قاعدة بيانات مؤقتة من النسخة الحالية (checkout) قبل تشغيل أداة الاختبار.
- قم بتشغيل الاستعلام، والتقط التنبيهات، وقارنها بملف
expected-alerts.json. - افشل عملية البناء (build) إذا اختفى أي تنبيه مطلوب أو إذا بدأ مسار محظور في إطلاق تنبيهات.
لا تقم بالتحقق من إجمالي عدد التنبيهات عبر المستودع—فالتغييرات غير المرتبطة قد تزيد العدد وتتسبب في فشل خاطئ.
ما يجب مراقبته بعد الترقية
عند ترقية CodeQL إلى إصدار أحدث:
- التنبيه المطلوب لا يزال يظهر – تابع المراجعة العادية.
- التنبيه المطلوب يختفي – أوقف عملية البناء؛ وتحقق مما إذا كان الإصدار الجديد قد غير منطق الاستعلام أو ما إذا كان تغيير في الكود قد كسر تدفق البيانات.
- يظهر موقع إيجابي جديد – أضفه إلى قائمة
requiredبعد التأكد من أنه مسار حقن حقيقي. - بدء إطلاق تنبيهات في التحكم السلبي – أعد النظر في استراتيجية التخفيف؛ فقد تكون القاعدة قد أصبحت أكثر صرامة.
لا يمكن للتحليل الساكن إثبات كيفية تفاعل النموذج في وقت التشغيل. استكمل مجموعة اختبارات التراجع باختبارات هجومية (adversarial tests) ترسل بالفعل أوامر مصممة خصيصاً إلى النموذج وتتحقق من الاستجابة.
الخلاصة
يمنحك CodeQL 2.26.0 القدرة على اكتشاف أخطاء حقن الأوامر قبل إرسالها، ولكن فقط إذا قمت بتأمين هذه القدرة باستخدام أداة اختبار تراجع مركزة. من خلال تحديد المصادر غير الموثوقة، ونقاط وصول SDK الحقيقية، والتوقعات الواضحة في عقد JSON، فإنك تحول قاعدة التحليل الساكن إلى بوابة تمنع تراجع الأداء وتفرض الاهتمام المستمر بسطح هجوم يتطور بسرعة.
