Verbesserte Sicherheitstests für Git-basierte Gradle-Projekte mit Lockfiles
Antonio Gomes
7. Dezember 2020
0 Min. LesezeitIm vergangenen Jahr haben wir intensiv daran gearbeitet, unsere Tests für Gradle-Projekte aus Git-Repositories zuverlässiger, präziser und skalierbarer zu machen.
Uns war klar: Das Parsen eines Gradle-Manifests statt einer Gradle-Lockdatei wäre ein endloser Kampf, den wir immer verlieren würden. Der Versuch, das Komplexitätsmodell von Gradle durch das Parsen einer build.gradle-Datei zu interpretieren, ist ein nichtdeterministischer Ansatz, der auf zahlreichen Annahmen beruht. Konfigurationen, die eine bestimmte Version oder eine Höchstversion einer oder mehrerer Abhängigkeiten strikt erzwingen oder deren Versionen in anderen Dateien definiert sind, würden nicht immer berücksichtigt. Außerdem wird mit diesem Ansatz nie explizit festgelegt, was Gradle in unterschiedlichen Konfigurationskontexten wie den Phasen Compile, Test oder Runtime auflöst und verwendet. All dies kann zu ungenauen oder unvollständigen Ergebnissen führen.
Deshalb setzen verschiedene Tools für Abhängigkeiten auf Dependency Locking, damit Entwickler reproduzierbare Builds erstellen können. Beispiele sind npm (package-lock.json), yarn (yarn.lock), RubyGems (Gemfile.lock) und jetzt auch Gradle mit gradle.lockfile.
So erstellen Sie eine Gradle-Lockdatei
So sieht eine typische gradle.lockfile aus:

Sie enthält keine Baum- oder Graphstruktur. Stattdessen erfüllt sie ihren Zweck, indem sie alle verwendeten direkten und transitiven Abhängigkeiten einschließlich ihrer Versionen und zugehörigen Konfigurationen aufführt.
Um in Ihrem Einzelprojekt oder Monorepo (mit verschiedenen Teilprojekten) eine Lockdatei zu erstellen, ergänzen Sie Ihre build.gradle um die folgenden Anweisungen:

Wie Sie sehen, weisen wir Gradle an, dependencyLocking für alle verfügbaren Konfigurationen zu aktivieren, zum Beispiel annotationProcessor, Compile, Runtime usw.
Sobald wir den Code hinzugefügt haben, kann Gradle unsere Absichten nachvollziehen.
In diesem Beispiel haben wir eine einzelne Konfiguration. In der Regel können jedoch mehrere Konfigurationen vorliegen, sodass für jede Konfiguration eine eigene Lockdatei entstehen kann. Damit es eine einzige verlässliche Quelle gibt, sehen wir uns an, wie Sie pro Projekt eine einzelne Lockdatei erstellen können.
Eine einzelne Lockdatei erstellen
Mit einer Vorschaufunktion von Gradle 7.0, die bereits für Nutzer von Gradle 6.0 und höher verfügbar ist, können wir pro Projekt eine einzelne gradle.lockfile erstellen, die alle verschiedenen Lockzustände für unterschiedliche Konfigurationen enthält. Ab Gradle 7.0 ist dieser Ansatz mit einer einzelnen Lockdatei standardmäßig aktiviert.

Nachdem wir diese Regel festgelegt haben, führen wir im nächsten Schritt im Terminal den Befehl gradle resolveAndLockAll --write-locks aus. Dadurch wird die folgende Lockdatei erstellt:

Daran sehen wir:
Jede Zeile steht weiterhin für eine einzelne Abhängigkeit in der GAV-Notation
group:artifact:version.Für jede Abhängigkeit ist eine Liste der Konfigurationen aufgeführt, zu denen sie gehört.
Die letzte Zeile
emptyenthält die Liste der verfügbaren Konfigurationen ohne Abhängigkeiten.
So erleichtert Ihnen die Gradle-Lockdatei den Alltag
Dependency Locking kann unerwartete Fehler verhindern, die durch eine transitive Abhängigkeit entstehen, auf die Sie bei dynamischen Abhängigkeitsversionen keinen Einfluss haben (z. B. 1.+ oder [1.0,2.0). Das kann jederzeit unbemerkt passieren und ist ein häufiges Szenario.
Hier sehen Sie eine kurze build.gradle-Datei, in der wir eine Abhängigkeit mit einer dynamischen Version deklarieren:

Wenn Sie den Befehl gradle -q dependencies ausführen, lautet die Ausgabe weder 2.11 noch 2.14. Wie das Bild unten zeigt, hat Gradle die Abhängigkeiten auf 2.13 aufgelöst.

Wenn wir nun die verfügbaren Abhängigkeiten in der Lockdatei prüfen, können wir bestätigen, dass die Befehlsausgabe mit dem Lockdatei-Status unserer Beispielanwendung übereinstimmt.

Jetzt ist es Zeit, sich die Ergebnisse anzusehen!
Hier sehen wir ein Szenario, in dem das Parsen der build.gradle-Datei mit Abhängigkeiten mit dynamischen Versionen fehlgeschlagen ist. Vergleichen wir die Ergebnisse mit einer gradle.lockfile.

Wie die Ergebnisse zeigen, sind sie mit der gradle.lockfile deutlich besser: Wir konnten 24 statt 0 Abhängigkeiten im vorherigen Fall identifizieren, darunter direkte und transitive, und insgesamt 33 Probleme melden!

Wenn Sie sich weiter mit Dependency Locking in Gradle beschäftigen möchten, lesen Sie die offizielle Dokumentation.
Wir hoffen, Ihnen gefällt diese Snyk-Funktion! Sie haben noch kein Snyk-Konto? Registrieren Sie sich kostenlos.
Starten Sie mit Capture the Flag
Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Herausforderungen lösen.


