GitHub’s CodeQL 2.26.0 מוסיפה שאילתה מובנית שמזהה דפוסי prompt-injection של AI, והשינוי כבר גורם לכך שצינורות CI (CI pipelines) יסמנו סיכונים חדשים. השדרוג לבדו אינו מספיק – צוותים זקוקים למערך בדיקות רגרסיה (regression test suite) שמבטיח שהכלל יישאר אפקטיבי ככל שהקוד מתפתח.
למה fixture של רגרסיה הוא חשוב
Prompt injection מאפשר לתוקף להחדיר הוראות זדוניות לתוך prompt שמודל שפה יבצע מאוחר יותר. באמצעות השאילתה החדשה, ניתוח סטטי (static analysis) יכול לעקוב אחר נתונים ממקור לא מהימן (untrusted source) ועד ל-"sink" שמבצע קריאה למודל. אם פשוט מפעילים את הכלל מבלי לאמת אותו, refactor עתידי עלול לשבור את נתיב זרימת הנתונים (data-flow path) וההתראה תיעלם בשקט. fixture של רגרסיה לוכד את הנתיבים המדויקים שאמורים להפעיל (או לא להפעיל) את הכלל, ובכך הופך את תוצאת הניתוח הסטטי לחוזה (contract) שה-build אוכף.
שלושת המרכיבים של fixture אמין
- Untrusted source – כל פונקציה שמביאה נתונים מחוץ לבסיס הקוד המהימן (למשל, גוף של GitHub issue, webhook payload).
- Prompt construction – הקוד שמרכיב את בקשת המודל, בדרך כלל קריאה ל-client SDK.
- Model sink – מתודת ה-SDK ששולחת את ה-prompt למודל. מנוע ה-data-flow של CodeQL צריך לראות קריאה אמיתית מה-production stack שלכם כדי לזהות את ה-sink.
השאילתה תופעל רק כאשר שלושתם נוכחים.
ארגון קבצי הבדיקה
מבנה קונבנציונלי שומר על מערך הבדיקות קל לביקורת (audit):
security-fixtures/prompt-injection/
├─ positive/
│ ├─ direct-flow.ts
│ └─ helper-flow.ts
├─ negative/
│ └─ trusted-instruction.ts
└─ expected-alerts.json
קבצי Positive מכילים קוד שאמור לסומן; קבצי negative מכילים דפוסים בטוחים שאמורים להישאר שקטים.
כתיבת המקרים החיוביים (positive cases)
הדוגמה הפשוטה ביותר מראה זרימה ישירה מ-untrusted value לקריאה למודל:
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 הוא ה-untrusted source, 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/.
כתיבת המקרה השלילי (negative case)
ה-negative fixture חייב להדגים שקלט משתמש אינו יכול לשנות את ההוראה של המודל. טעות נפוצה היא להניח שפונקציה בשם sanitize() מבטיחה בטיחות. ה-static analyzer לא מתייחס לשם כהוכחה, לכן הבדיקה צריכה להימנע מכל stub של sanitisation מטעה:
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
ה-contract של מערך הבדיקות נמצא ב-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 במזהה (identifier) המופיע בתיעוד של CodeQL או בפלט ה-SARIF. אל תנחשו מזהים; המחרוזת המדויקת חשובה לבדיקת ה-CI.
חיבור ה-fixture ל-CI
- קבעו (Pin) את גרסת ה-CodeQL CLI המשמשת ב-pipeline לגרסה 2.26.0 (או מאוחר יותר).
- בנו database זמנית (disposable) מה-checkout הנוכחי לפני הרצת ה-fixture.
- הריצו את השאילתה, לכדו התראות והשוו אותן מול
expected-alerts.json. - הפילו את ה-build אם התראה נדרשת נעלמת או אם נתיב אסור מתחיל להפעיל התראות.
אל תבצעו assertion על סך כל ההתראות בכל ה-repository – שינויים לא קשורים עלולים לנפח את המספר ולגרום לכשלים שגויים (false failures).
מה לשים לב אליו לאחר שדרוג
כשאתם משדרגים את CodeQL לגרסה חדשה יותר:
- התראה נדרשת עדיין מופיעה – המשיכו בתהליך הסקירה הרגיל.
- התראה נדרשת נעלמה – חסמו את ה-build; בדקו האם הגרסה החדשה שינתה את לוגיקת השאילתה או ששינוי בקוד שבר את זרימת הנתונים (data flow).
- מיקום חיובי חדש מופיע – הוסיפו אותו לרשימת ה-
requiredלאחר שתאשרו שמדובר בנתיב injection אמיתי. - בקרת שלילית (Negative control) מתחילה להפעיל התראות – בחנו מחדש את אסטרטגיית המיגון (mitigation strategy); ייתכן שהכלל הפך להחמיר יותר.
ניתוח סטטי אינו יכול להוכיח כיצד המודל יגיב בזמן ריצה (runtime). השלימו את מערך הרגרסיה בבדיקות אדברסריות (adversarial tests) ששולחות בפועל prompts מתוכננים למודל ומוודאות את התגובה.
שורה תחתונה
CodeQL 2.26.0 מעניקה לכם את היכולת לתפוס באגים של prompt-injection לפני שהם מופצים, אך רק אם תנעלו את היכולת הזו באמצעות fixture רגרסיה ממוקד. על ידי הגדרת מקורות לא מהימנים, SDK sinks אמיתיים וציפיות ברורות בחוזה JSON, אתם הופכים כלל של ניתוח סטטי לשער (gate) שעוצר רגרסיות ומאלץ תשומת לב מתמדת למשטח תקיפה (attack surface) המתפתח במהירות.
