Skip to main content

Verbesserte Sicherheitstests für Git-basierte Gradle-Projekte mit Lockfiles

Artikel von

Antonio Gomes

Blog illustrations Gradle

7. Dezember 2020

0 Min. Lesezeit

Im 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:

Gradle-Abhängigkeitssperrdatei mit Warnungen zu generierten Dateien und gesperrten Einträgen für Log4j API und Core, Version 2.12.1.

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:

Dunkel gestalteter build.gradle-Editor mit Konfiguration für Dependency Locking und einer Aufgabe zum Auflösen und Schreiben von Dependency Locks

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.

Code-Editor mit der Datei settings.gradle und aktivierter Vorschau der Gradle-Funktion ONE_LOCKFILE_PER_PROJECT.

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:

Visual Studio Code zeigt ein Gradle-Projekt und die gradle.lockfile mit Abhängigkeitseinträgen sowie den Befehl `gradle resolveAndLockAll --write-locks`.

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 empty enthä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:

Dunkel gestalteter Code-Editor mit einer Gradle-Build-Datei, die Java, Maven Central und eine Log4j-Annotation-Processor-Abhängigkeit zeigt.

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.

Terminalausgabe mit der Gradle-Auflösung von Abhängigkeiten für die Konfiguration annotationProcessor, einschließlich Log4j Core und API in Version 2.13.3.

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.

Screenshot einer Gradle-Abhängigkeitssperrdatei mit dem Hinweis, dass manuelle Änderungen den Build beeinträchtigen können, gefolgt von Log4j-Abhängigkeitseinträgen.

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.

Snyk-Projekt-Dashboard mit der Datei „build.gradle“ des unparsed-gradle-repo und dem Tab „Dependencies“

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!

Abhängigkeitsansicht für das build.gradle-Projekt mit Gradle-Lockfile-Tests sowie Sicherheitslücken und Lizenzproblemen bei den aufgeführten Paketen

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.

Weiterlesen

feature insights context
Blog

Autonome Angriffe sind bereits Realität. Die Verteidigung muss Schritt halten.

Autonome Angreifer verkürzen das Zeitfenster für die Verteidigung. Erfahren Sie, wie kontinuierliches Erkennen, Beheben, Validieren und Verhindern Sicherheitsteams helfen kann, Schritt zu halten.

feature insights context
Blog

Ist Prävention im Grunde ein gelöstes Problem?

Die Prävention in von Agenten generiertem Code ist architektonisch gelöst – die Herausforderung besteht jedoch weiterhin darin, Kontrollen zu wählen, die die Sicherheit schützen, ohne die Entwicklung zu verlangsamen.

Live Stream

Remediation-Agenten verständlich erklärt: Warum Beheben besser ist als Finden

Erfahren Sie, wie der Remediation Agent von Snyk Sicherheitsinformationen, Analysen zur Ausnutzbarkeit und Validierung nutzt, um Schwachstellen in zusammenführbare Pull Requests zu verwandeln.