GitHub کا CodeQL 2.26.0 ایک بلٹ ان (built-in) کوئری شامل کرتا ہے جو AI prompt-injection کے پیٹرنز کو پہچانتی ہے، اور اس تبدیلی کی وجہ سے CI پائپ لائنز پہلے ہی نئے خطرات کو نشان زد (flag) کرنا شروع کر چکی ہیں۔ صرف اپ گریڈ کافی نہیں ہے—ٹیموں کو ایک ریگریشن ٹیسٹ سویٹ (regression test suite) کی ضرورت ہے جو اس بات کی ضمانت دے کہ کوڈ کے ارتقاء کے ساتھ ساتھ یہ رول مؤثر رہے۔
ریگریشن فکسچر (regression fixture) کیوں ضروری ہے
Prompt injection ایک حملہ آور کو اس قابل بناتا ہے کہ وہ کسی پرامپٹ (prompt) میں نقصان دہ ہدایات شامل کر سکے جن پر بعد میں لینگویج ماڈل عمل کرتا ہے۔ نئی کوئری کے ساتھ، اسٹیٹک اینالیسس (static analysis) غیر قابل اعتماد ذریعے (untrusted source) سے لے کر ماڈل کو کال کرنے والے سنک (model-calling sink) تک ڈیٹا کا سراغ لگا سکتا ہے۔ اگر اس رول کو صرف آن کر دیا جائے اور کبھی تصدیق نہ کی جائے، تو بعد میں ہونے والی ریفیکٹرنگ (refactor) ڈیٹا فلو کے راستے کو توڑ سکتی ہے اور الرٹ خاموشی سے غائب ہو سکتا ہے۔ ایک ریگریشن فکسچر ان عین راستوں کو محفوظ کرتا ہے جنہیں رول کو ٹرگر (trigger) کرنا چاہیے (یا نہیں کرنا چاہیے)، جس سے اسٹیٹک اینالیسس کا نتیجہ ایک ایسے معاہدے (contract) میں بدل جاتا ہے جسے بلڈ (build) نافذ کرتا ہے۔
ایک قابل اعتماد فکسچر کے تین اجزاء
- Untrusted source (غیر قابل اعتماد ذریعہ) – کوئی بھی فنکشن جو قابل اعتماد کوڈ بیس سے باہر سے ڈیٹا لاتا ہے (مثلاً، GitHub issue کی باڈی، یا webhook payload)۔
- Prompt construction (پرامپٹ کی تشکیل) – وہ کوڈ جو ماڈل کی درخواست کو ترتیب دیتا ہے، عام طور پر یہ کلائنٹ SDK کو کی جانے والی ایک کال ہوتی ہے۔
- Model sink (ماڈل سنک) – وہ SDK میتھڈ جو پرامپٹ کو ماڈل تک بھیجتا ہے۔ CodeQL کے ڈیٹا فلو انجن کو سنک کو پہچاننے کے لیے آپ کے پروڈکشن اسٹیک سے ایک حقیقی کال دیکھنے کی ضرورت ہوتی ہے۔
کوئری صرف اسی وقت چلتی ہے جب یہ تینوں موجود ہوں۔
ٹیسٹ فائلوں کی تنظیم
ایک روایتی لے آؤٹ سویٹ کو آڈٹ کرنے میں آسان رکھتا ہے:
security-fixtures/prompt-injection/
├─ positive/
│ ├─ direct-flow.ts
│ └─ helper-flow.ts
├─ negative/
│ └─ trusted-instruction.ts
└─ expected-alerts.json
Positive فائلوں میں وہ کوڈ ہوتا ہے جسے نشان زد (flag) کیا جانا چاہیے؛ negative فائلوں میں محفوظ پیٹرنز ہوتے ہیں جنہیں خاموش رہنا چاہیے۔
پازیٹو کیسز (positive cases) لکھنا
سادہ ترین مثال ایک غیر قابل اعتماد ویلیو سے ماڈل کال تک براہ راست فلو دکھاتی ہے:
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 سنک ہے، اور ڈیٹا بغیر کسی سینٹائزیشن (sanitisation) مرحلے کے گزر جاتا ہے—بالکل وہی جسے پکڑنے کے لیے کوئری ڈیزائن کی گئی ہے۔
دوسرا پازیٹو کیس ڈیٹا کو ایک ہیلپر فنکشن کے ذریعے بھیجنا چاہیے، جو یہ ثابت کرے کہ اینالیسس بالواسطہ راستوں (indirect paths) پر بھی عمل کرتا ہے:
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/ کے تحت ہونی چاہئیں۔
نیگیٹو کیس (negative case) لکھنا
نیگیٹو فکسچر کو یہ ثابت کرنا چاہیے کہ صارف کا ان پٹ ماڈل کی ہدایت کو تبدیل نہیں کر سکتا۔ ایک عام غلطی یہ سمجھنا ہے کہ 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 آؤٹ پٹ میں دکھائے گئے آئیڈنٹیفائر (identifier) سے بدل دیں۔ آئی ڈیز کا اندازہ نہ لگائیں؛ CI چیک کے لیے عین مطابق اسٹرنگ (string) اہم ہے۔
فکسچر کو CI کے ساتھ جوڑنا
- پائپ لائن میں استعمال ہونے والے CodeQL CLI ورژن کو 2.26.0 (یا اس سے جدید) پر پن (pin) کریں۔
- فکسچر چلانے سے پہلے موجودہ چیک آؤٹ سے ایک عارضی (disposable) ڈیٹا بیس بنائیں۔
- کوئری چلائیں، الرٹس حاصل کریں، اور ان کا موازنہ
expected-alerts.jsonسے کریں۔ - اگر کوئی مطلوبہ الرٹ غائب ہو جائے یا کوئی ممنوعہ راستہ الرٹ دینا شروع کر دے، تو بلڈ کو فیل (fail) کر دیں۔
پورے ریپوزٹری میں کل الرٹس کی تعداد کا دعویٰ نہ کریں—غیر متعلقہ تبدیلیاں تعداد بڑھا سکتی ہیں اور غلط فیلرز (false failures) کا سبب بن سکتی ہیں۔
اپ گریڈ کے بعد کن چیزوں پر نظر رکھنی چاہیے
جب آپ CodeQL کو نئے ورژن پر اپ گریڈ کرتے ہیں:
- مطلوبہ الرٹ اب بھی ظاہر ہو رہا ہے – معمول کے مطابق ریویو جاری رکھیں۔
- مطلوبہ الرٹ غائب ہو گیا ہے – بلڈ کو روک دیں؛ اس بات کی تحقیقات کریں کہ آیا نئے ورژن نے کوئری کی لاجک تبدیل کر دی ہے یا کوڈ کی تبدیلی نے ڈیٹا فلو کو توڑ دیا ہے۔
- نیا پازیٹو مقام ظاہر ہو رہا ہے – اس بات کی تصدیق کرنے کے بعد کہ یہ ایک حقیقی انجیکشن راستہ ہے، اسے
requiredفہرست میں شامل کریں۔ - نیگیٹو کنٹرول الرٹ دینا شروع کر دیتا ہے – مائٹی گیشن اسٹریٹیجی (mitigation strategy) پر نظر ثانی کریں؛ ہو سکتا ہے کہ رول مزید سخت ہو گیا ہو۔
اسٹیٹک اینالیسس یہ ثابت نہیں کر سکتا کہ ماڈل رن ٹائم (runtime) پر کیسا ردعمل دے گا۔ ریگریشن سویٹ کو ایڈورسرل ٹیسٹ (adversarial tests) کے ساتھ مکمل کریں جو حقیقت میں ماڈل کو تیار کردہ پرامپٹس بھیجتے ہیں اور جواب کی تصدیق کرتے ہیں۔
خلاصہ
CodeQL 2.26.0 آپ کو پرامپٹ انجیکشن بگ (prompt-injection bugs) کو ریلیز ہونے سے پہلے پکڑنے کی صلاحیت دیتا ہے، لیکن صرف اس صورت میں جب آپ اس صلاحیت کو ایک مرکوز ریگریشن فکسچر کے ذریعے محفوظ کر لیں۔ غیر قابل اعتماد ذرائع، حقیقی SDK سنکس، اور JSON معاہدے میں واضح توقعات کی تعریف کر کے، آپ اسٹیٹک اینالیسس کے رول کو ایک ایسے گیٹ (gate) میں بدل دیتے ہیں جو ریگریشنز کو روکتا ہے اور تیزی سے بدلتے ہوئے اٹیک سرفیس (attack surface) پر مسلسل توجہ مرکوز کرنے پر مجبور کرتا ہے۔
