GitHubs CodeQL 2.26.0 fügt eine integrierte Abfrage hinzu, die Muster für AI-Prompt-Injection erkennt, und die Änderung führt bereits dazu, dass CI-Pipelines neue Risiken melden. Das Upgrade allein reicht nicht aus – Teams benötigen eine Regressionstest-Suite, die garantiert, dass die Regel auch bei der Weiterentwicklung des Codes wirksam bleibt.
Warum eine Regression-Fixture wichtig ist
Prompt Injection ermöglicht es einem Angreifer, bösartige Anweisungen in einen Prompt einzuschleusen, denen ein Sprachmodell später folgt. Mit der neuen Abfrage kann die statische Analyse Daten von einer nicht vertrauenswürdigen Quelle (untrusted source) bis zu einem Modellaufruf-Sink (model-calling sink) zurückverfolgen. Wenn die Regel einfach nur aktiviert und nie verifiziert wird, könnte ein späterer Refactor den Datenflusspfad unterbrechen, wodurch die Warnung lautlos verschwindet. Eine Regression-Fixture erfasst die exakten Pfade, die die Regel auslösen (oder nicht auslösen) sollten, und verwandelt das Ergebnis der statischen Analyse in einen Vertrag (Contract), den der Build erzwingt.
Die drei Zutaten einer zuverlässigen Fixture
- Untrusted source (Nicht vertrauenswürdige Quelle) – jede Funktion, die Daten von außerhalb der vertrauenswürdigen Codebasis einbringt (z. B. den Body eines GitHub-Issues, ein Webhook-Payload).
- Prompt construction (Prompt-Konstruktion) – der Code, der die Modellanfrage zusammenstellt, typischerweise ein Aufruf eines Client-SDKs.
- Model sink (Modell-Sink) – die SDK-Methode, die den Prompt an das Modell sendet. Die Data-Flow-Engine von CodeQL muss einen echten Aufruf aus Ihrem Produktions-Stack sehen, um den Sink zu erkennen.
Nur wenn alle drei vorhanden sind, wird die Abfrage ausgelöst.
Organisation der Testdateien
Ein konventionelles Layout hält die Suite einfach auditierbar:
security-fixtures/prompt-injection/
├─ positive/
│ ├─ direct-flow.ts
│ └─ helper-flow.ts
├─ negative/
│ └─ trusted-instruction.ts
└─ expected-alerts.json
Positive Dateien enthalten Code, der markiert werden sollte; negative Dateien enthalten sichere Muster, die keine Warnung auslösen dürfen.
Schreiben der positiven Fälle
Das einfachste Beispiel zeigt einen direkten Fluss von einem nicht vertrauenswürdigen Wert zum Modellaufruf:
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,
});
}
Hier ist loadIssueBody die nicht vertrauenswürdige Quelle, model.generate der Sink, und die Daten werden ohne jeglichen Sanitization-Schritt weitergegeben – genau das, was die Abfrage erfassen soll.
Ein zweiter positiver Fall sollte die Daten über eine Hilfsfunktion leiten, um zu beweisen, dass die Analyse auch indirekte Pfade verfolgt:
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));
}
Beide Dateien gehören in das Verzeichnis positive/.
Schreiben des negativen Falls
Die negative Fixture muss zeigen, dass Benutzereingaben die Anweisung des Modells nicht verändern können. Ein häufiger Fehler besteht darin, anzunehmen, dass eine Funktion namens sanitize() Sicherheit garantiert. Der statische Analysator behandelt den Namen nicht als Beweis, daher sollte der Test jegliche irreführenden Sanitization-Stubs vermeiden:
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",
});
}
Da die nicht vertrauenswürdigen Daten das system-Feld nie erreichen, sollte die Regel stumm bleiben.
Deklarieren von Erwartungen in JSON
Der Vertrag der Suite liegt in expected-alerts.json. Er listet erforderliche Warnungen und explizit verbotene Pfade auf:
{
"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"
]
}
Ersetzen Sie USE_ACTUAL_RULE_ID durch die Kennung, die in der CodeQL-Dokumentation oder im SARIF-Output angezeigt wird. Raten Sie keine IDs; die exakte Zeichenfolge ist für den CI-Check entscheidend.
Einbindung der Fixture in die CI
- Fixieren Sie die in der Pipeline verwendete CodeQL CLI-Version auf 2.26.0 (oder neuer).
- Erstellen Sie eine temporäre Datenbank aus dem aktuellen Checkout, bevor Sie die Fixture ausführen.
- Führen Sie die Abfrage aus, erfassen Sie die Warnungen und vergleichen Sie diese mit
expected-alerts.json. - Schlagen Sie den Build fehl, wenn eine erforderliche Warnung verschwindet oder wenn ein verbotener Pfad Warnungen auslöst.
Überprüfen Sie nicht die Gesamtzahl der Warnungen im gesamten Repository – nicht verwandte Änderungen könnten die Anzahl erhöhen und zu Fehlalarmen führen.
Worauf nach einem Upgrade zu achten ist
Wenn Sie CodeQL auf eine neuere Version aktualisieren:
- Erforderliche Warnung erscheint weiterhin – fahren Sie mit der normalen Überprüfung fort.
- Erforderliche Warnung verschwindet – blockieren Sie den Build; untersuchen Sie, ob die neue Version die Logik der Abfrage geändert hat oder ob eine Codeänderung den Datenfluss unterbrochen hat.
- Neue positive Stelle erscheint – fügen Sie diese der
required-Liste hinzu, nachdem Sie bestätigt haben, dass es sich um einen echten Injection-Pfad handelt. - Negative Kontrolle löst Warnungen aus – überprüfen Sie die Minderungsstrategie (Mitigation Strategy); die Regel könnte strenger geworden sein.
Statische Analyse kann nicht beweisen, wie das Modell zur Laufzeit reagieren wird. Ergänzen Sie die Regressionstest-Suite durch Adversarial-Tests, die tatsächlich manipulierte Prompts an das Modell senden und die Antwort verifizieren.
Fazit
CodeQL 2.26.0 gibt Ihnen die Möglichkeit, Prompt-Injection-Bugs abzufangen, bevor sie veröffentlicht werden, aber nur, wenn Sie diese Fähigkeit mit einer gezielten Regression-Fixture absichern. Indem Sie nicht vertrauenswürdige Quellen, echte SDK-Sinks und klare Erwartungen in einem JSON-Vertrag definieren, verwandeln Sie eine statische Analyse-Regel in ein Gate, das Regressionen verhindert und die kontinuierliche Aufmerksamkeit auf eine sich schnell entwickelnde Angriffsfläche lenkt.
