Ausführen von CodeQL in einer Datenbank
Wenn Ihr Code in eine Datenbank extrahiert wurde, können Sie ihn jetzt mithilfe von CodeQL-Abfragen analysieren. GitHub-Experten, Sicherheitsforscher und Communitymitwirkende schreiben und verwalten die standardmäßigen CodeQL-Abfragen. Sie können auch eigene Abfragen schreiben.
Sie können CodeQL-Abfragen in der Codescananalyse verwenden, um Probleme in Ihrem Quellcode zu finden und potenzielle Sicherheitsrisiken zu finden. Sie können auch benutzerdefinierte Abfragen schreiben, um Probleme für jede Sprache zu identifizieren, die Sie in Ihrem Quellcode verwenden.
Es gibt zwei wichtige Arten von Abfragen:
- Warnungsabfragen heben Probleme an bestimmten Stellen Ihres Codes hervor.
- Pfadabfragen beschreiben den Informationsfluss zwischen einer Quelle und einer Spüle in Ihrem Code.
Einfache CodeQL-Abfrage
Die grundlegende CodeQL-Abfragestruktur weist die Dateierweiterung .ql auf und enthält eine select Klausel. Hier ist eine Beispielabfragestruktur:
/**
*
* Query metadata
*
*/
import /* ... CodeQL libraries or modules ... */
/* ... Optional, define CodeQL classes and predicates ... */
from /* ... variable declarations ... /
where / ... logical formula ... /
select / ... expressions ... */
Abfragemetadaten
Die Verwendung von CodeQL mit Codescans konvertiert Ergebnisse auf eine Weise, die die potenziellen Probleme hervorhebt, die die Abfragen erkennen sollen. Abfragen enthalten Metadateneigenschaften, die angeben, wie die Ergebnisse interpretiert werden sollen. Verwenden Sie Abfragemetadaten, um zu:
- Identifizieren Sie Ihre benutzerdefinierten Abfragen, wenn Sie sie ihrem GitHub-Repository hinzufügen.
- Geben Sie Informationen zum Zweck der Abfrage an.
Metadateninformationen können eine Beschreibung der Abfrage, eine eindeutige ID und die Art des Problems enthalten( Warnung oder Pfad). Metadaten geben außerdem an, wie die Abfrageergebnisse interpretiert und angezeigt werden.
GitHub verfügt über eine empfohlene Stilrichtlinie für Abfragemetadaten. Sie finden ihn in der CodeQL-Dokumentation.
Dieses Beispiel zeigt Metadaten für eine der Standardmäßigen Java-Abfragen:
CodeQL interpretiert keine Abfragen, die keine Metadaten enthalten. Sie zeigt diese Ergebnisse als Tabelle an und zeigt sie nicht im Quellcode an.
QL-Syntax
QL ist eine deklarative, objektorientierte Abfragesprache. Es ist optimiert, eine effiziente Analyse hierarchischer Datenstrukturen und insbesondere Datenbanken zu ermöglichen, die Softwareartefakte darstellen.
Die Syntax von QL ähnelt SQL, die Semantik von QL basiert jedoch auf Datalog. Datalog ist eine deklarative Logik-Programmiersprache, die häufig als Abfragesprache verwendet wird. Da QL in erster Linie eine Logiksprache ist, sind alle Vorgänge in QL logische Vorgänge. QL erbt auch rekursive Prädikate aus Datalog. QL fügt Unterstützung für Aggregate hinzu, um sogar komplexe Abfragen präzise und einfach zu machen.
Die QL-Sprache besteht aus logischen Formeln. Es verwendet allgemeine logische Bindemittel wie and, orund not, und , zusammen mit Quantifizierern wie forall und exists. Da QL rekursive Prädikate erbt, können Sie auch komplexe rekursive Abfragen schreiben, indem Sie grundlegende QL-Syntax und Aggregate wie count, sum, und average verwenden.
Weitere Informationen zur QL-Sprache finden Sie in der CodeQL-Dokumentation.
Pfadabfragen
Die Art und Weise, wie Informationen durch ein Programm fließen, ist wichtig. Daten, die harmlos erscheinen, können auf unerwartete Weise weitergegeben werden und dann bösartig genutzt werden.
Das Erstellen von Pfadabfragen kann Ihnen helfen, den Informationsfluss über eine Codebasis zu visualisieren. Eine Abfrage kann den Pfad verfolgen, den Daten von möglichen Ausgangspunkten (source) zu möglichen Endpunkten (sink) nehmen. Um Pfade zu modellieren, muss Ihre Abfrage Informationen über die Quelle, die Spüle und die Datenflussschritte bereitstellen, mit denen sie verknüpft werden.
Die einfachste Möglichkeit, mit dem Schreiben Einer eigenen Pfadabfrage zu beginnen, besteht darin, eine der vorhandenen Abfragen als Vorlage zu verwenden. Informationen zum Abrufen dieser Abfragen für unterstützte Sprachen finden Sie in der CodeQL-Dokumentation.
Ihre Pfadabfrage erfordert bestimmte Metadaten, Abfrage-Prädikate und select Anweisungsstrukturen. Viele der integrierten Pfadabfragen in CodeQL folgen einer grundlegenden Struktur. Die Struktur hängt davon ab, wie CodeQL die sprache modelliert, die Sie analysieren.
Hier ist eine Beispielvorlage für eine Pfadabfrage:
/**
* ...
* @kind path-problem
* ...
*/
import <language>
// For some languages (Java/C++/Python/Swift), you need to explicitly import the data-flow library, such as
// import semmle.code.java.dataflow.DataFlow or import codeql.swift.dataflow.DataFlow
...
module Flow = DataFlow::Global<MyConfiguration>;
import Flow::PathGraph
from Flow::PathNode source, Flow::PathNode sink
where Flow::flowPath(source, sink)
select sink.getNode(), source, sink, "<message>"
In dieser Vorlage:
MyConfigurationist ein Modul, das die Prädikate enthält, die definieren, wie Daten zwischensourceundsinkfließen.Flowist das Ergebnis der Datenflussberechnung basierend aufMyConfiguration.Flow::Pathgraphist das resultierende Datenflussdiagrammmodul, das Sie importieren müssen, um Pfaderklärungen in die Abfrage einzuschließen.sourceundsinksind Knoten im Diagramm, wie in der Konfiguration definiert, undFlow::PathNodeist deren Typ.DataFlow::Global<..>ist ein Aufruf des Datenflusses. Sie können stattdessenTaintTracking::Global<..>verwenden, um einen Standardsatz von Taint-Schritten einzuschließen.
So schreiben Sie eine Pfadabfrage
Ihre Abfrage muss ein Pfaddiagramm berechnen, um Pfaderklärungen zu generieren. Definieren Sie dazu ein Abfrage-Prädikat namens edges. Ein Abfrage-Prädikat ist ein Prädikat ohne Mitgliedschaft mit einer query Anmerkung. Die Abfrageanmerkung gibt alle Tupel zurück, die das Prädikat auswertet.
Das edges Prädikat definiert die Randbeziehungen des Diagramms, das Sie berechnen. Es wird verwendet, um die Pfade zu den einzelnen Ergebnissen zu berechnen, die ihre Abfrage generiert. Sie können auch ein vordefiniertes edges Prädikat aus einem Pfaddiagrammmodul in einer der Standarddatenflussbibliotheken importieren.
Die Datenflussbibliotheken enthalten die anderen Klassen, Prädikate und Module, die häufig in der Datenanalyse verwendet werden, zusätzlich zum Pfaddiagrammmodul. Die CodeQL-Datenflussbibliotheken funktionieren durch Modellieren des Datenflussdiagramms oder implementieren der Datenflussanalyse. Normale Datenflussbibliotheken werden verwendet, um den Informationsfluss zu analysieren, in dem Datenwerte bei jedem Schritt beibehalten werden.
Hier ist eine Beispiel-Anweisung, die das pathgraph Modul aus der Datenflussbibliothek importiert (DataFlow.qll), in der edges definiert ist:
import DataFlow::PathGraph
Sie können viele andere Bibliotheken importieren, die in CodeQL enthalten sind. Sie können auch Bibliotheken importieren, die speziell für die Implementierung von Datenflussanalysen in verschiedenen gängigen Frameworks und Umgebungen entwickelt wurden.
Die Klasse PathNode wurde entwickelt, um die Datenflussanalyse zu implementieren. Es handelt sich um einen Node, der um einen Aufrufkontext (mit Ausnahme von Senken), einen Zugriffspfad und eine Konfiguration erweitert wurde. Es werden nur PathNode Werte generiert, die aus einer Quelle erreichbar sind.
Hier ist ein Beispiel für den Importpfad:
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl
Optional können Sie ein nodes Abfrage-Prädikat definieren, das die Knoten des Pfaddiagramms für alle Sprachen angibt. Wenn Sie definieren nodes, definieren die ausgewählten Knoten nur Kanten mit Endpunkten. Wenn Sie nodes nicht definieren, müssen Sie alle möglichen Endpunkte von edges auswählen.
Datenbankanalyse
Wenn Sie Abfragen verwenden, um eine CodeQL-Datenbank zu analysieren, erhalten Sie aussagekräftige Ergebnisse im Kontext des Quellcodes. Die Ergebnisse werden als Warnungen oder Pfade in SARIF oder einem anderen interpretierten Format formatiert.
Hier ist ein Beispiel für einen CodeQL-Datenbankbefehl, der die Datenbank analysiert, indem ausgewählte Abfragen dafür ausgeführt und die Ergebnisse interpretiert werden:
codeql database analyze --format=<format> ---output=<output> [--threads=<num>] [--ram=<MB>] <options>... -- <database> <query|dir|suite>...
Dieser Befehl kombiniert die Auswirkungen der Plumbing-Befehle codeql database run-queries und codeql database interpret-results.
Alternativ können Sie Abfragen ausführen, die die Anforderungen für die Interpretation als Quellcodewarnungen nicht erfüllen. Verwenden Sie dazu codeql-database run-queries oder codeql query run. Verwenden Sie dann codeql bqrs decode, um die Rohergebnisse in eine lesbare Schreibweise umzuwandeln.
Sie können eine vollständige Liste der verfügbaren CodeQL CLI-Befehle im CodeQL CLI-Handbuch abrufen.
Verwenden einer SARIF-Datei mit Kategorien
CodeQL unterstützt SARIF für die Freigabe statischer Analyseergebnisse. SARIF ist so konzipiert, dass die Ausgabe einer Vielzahl von statischen Analysetools dargestellt wird.
Sie müssen eine Kategorie angeben, wenn Sie die SARIF-Ausgabe für die CodeQL-Analyse verwenden. Kategorien können mehrere Analysen unterscheiden, die im selben Commit-Repository und in verschiedenen Sprachen oder verschiedenen Teilen des Codes ausgeführt werden. SARIF-Dateien mit derselben Kategorie überschreiben jedoch einander.
Sie können jede SARIF-Ausgabedatei mithilfe von CodeQL scannen, um verschiedene Sprachen innerhalb derselben Codebasis zu analysieren, wenn der Kategoriewert zwischen der Analyse konsistent ist. Es wird empfohlen, die Sprache zu verwenden, die als Bezeichner für die Kategorie gescannt wird.
Hier ist ein Beispiel. Dieser Kategoriewert wird (mit einem angefügten nachstehenden Schrägstrichen, falls nicht bereits vorhanden) als <run>.automationId-Eigenschaft in SARIF v1, <run>.automationLogicalId-Eigenschaft in SARIF v2 und <run>.automationDetails.id-Eigenschaft in SARIF v2.1.0 angezeigt.
Veröffentlichen der SARIF-Ergebnisse auf GitHub
Nachdem die Datenbank fertig ist, können Sie sie interaktiv abfragen. Sie können auch eine Reihe von Abfragen ausführen, um eine Reihe von Ergebnissen im SARIF-Format zu generieren und die Ergebnisse in ein Ziel-Repository auf GitHub.com hochzuladen:
codeql github upload-results --sarif=<file> [--github-auth-stdin] [--github-url=<url>] [--repository=<repository-name>] [--ref=<ref>] [--commit=<commit>] [--checkout-path=<path>] <options>...
Um Ergebnisse auf GitHub hochzuladen, stellen Sie sicher, dass jeder CI-Server über eine GitHub-App oder ein persönliches Zugriffstoken für die CodeQL CLI verfügt. Sie müssen ein Zugriffstoken oder eine GitHub-App mit der security_events Schreibberechtigung verwenden.
Sie könnten möglicherweise zulassen, dass die CodeQL CLI dasselbe Token verwendet, wenn CI-Server bereits ein Token mit diesem Berechtigungsumfang verwenden, um Repositorys von GitHub auszulesen. Erstellen Sie andernfalls ein neues Token mit der security_events Schreibberechtigung, und fügen Sie dieses Token dem geheimen Speicher des CI-Systems hinzu. Eine bewährte Methode für die Sicherheit besteht darin, das --github-auth-stdin Kennzeichen festzulegen und das Token über die Standardeingabe an den Befehl zu übergeben.
SARIF-Ergebnisse hochladen
Damit Codescans Ergebnisse aus einem nicht von Microsoft stammenden statischen Analysetool in Ihrem GitHub-Repository anzeigen können, müssen Ihre Ergebnisse in einer SARIF-Datei gespeichert werden, die eine bestimmte Teilmenge des SARIF 2.1.0-JSON-Schemas unterstützt. Sie können die Ergebnisse mithilfe der Codescan-API oder der CodeQL CLI hochladen.
Jedes Mal, wenn Sie die Ergebnisse einer neuen Codeüberprüfung hochladen, verarbeitet CodeQL die Ergebnisse und fügt dem Repository Warnungen hinzu. Um doppelte Warnungen für dasselbe Problem zu verhindern, verwendet die Codeüberprüfung die SARIF-Eigenschaft partialFingerprints , um Ergebnisse in verschiedenen Läufen abzugleichen, sodass sie nur einmal in der neuesten Ausführung für die ausgewählte Verzweigung angezeigt werden. Durch das Entfernen von Duplikaten können Warnungen mit der richtigen Codezeile abgeglichen werden, wenn Dateien bearbeitet werden.
Die Regel-ID für ein Ergebnis muss für Analysen gleich sein. Fingerabdruckdaten werden automatisch in SARIF-Dateien eingeschlossen, die über den CodeQL-Analyseworkflow oder den CodeQL-Runner erstellt wurden.
SARIF-Spezifikationen verwenden den JSON-Eigenschaftsnamen partialFingerprints, ein Wörterbuch aus benannten Fingerabdrucktypen bis zum Fingerabdruck. Diese Eigenschaft enthält mindestens einen Wert für primaryLocationLineHash, der einen Fingerabdruck bereitstellt, basierend auf dem Kontext des Primärstandorts.
GitHub versucht, das partialFingerprints Feld aus den Quelldateien aufzufüllen, wenn Sie eine SARIF-Datei mithilfe der upload-sarif Aktion hochladen und diese Daten fehlen. Wenn Sie eine SARIF-Datei ohne Fingerabdruckdaten mithilfe des /code-scanning/sarifs API-Endpunkts hochladen, werden benutzern möglicherweise doppelte Warnungen angezeigt, wenn Codescanbenachrichtigungen verarbeitet und angezeigt werden.
Um doppelte Warnungen zu vermeiden, während Sie mit statischen Analysetools arbeiten, berechnen Sie Fingerabdruckdaten, und füllen Sie die partialFingerprints Eigenschaft auf, bevor Sie die SARIF-Datei hochladen. Ein hilfreicher Ausgangspunkt ist die Verwendung desselben Skripts wie die upload-sarif Aktion.