Im vorangegangenen Artikel haben wir uns mit der Notwendigkeit beschäftigt, Code automatisiert auf Lizenzen genutzter Software-Komponenten zu prüfen. Im Folgenden wollen wir uns anschauen, wie das in der Praxis aussehen kann.
Aus Gründen der Nachvollziehbarkeit führen wir hier den Einsatz des quelloffenen License Finders von Pivotal in der Pipeline der kostenlos nutzbaren Gitlab-CI/CD-Plattform vor. Anschließend wird der Einsatz des quelloffenen Porsche-Plugins im quelloffenen Static-Code-Analysetool SonarQube vorgeführt. Beide Umsetzungen sind kostenlos und ohne Rückgriff auf Fremdsysteme nutzbar.
Dafür erzeugen wir zunächst ein Projekt mit zwei Modulen: Ein Modul mit Maven- (pom.xml) und eines mit NPM-Abhängigkeiten (package.json). Wir hinterlegen das Projekt auf Gitlab, um die dortige CI/CD-Infrastruktur nutzen zu können.
License Finder in Gitlab CI
Zum Testen liegt ein Projekt bereit. Die einzige technische Voraussetzung ist Docker.
Der License-Finder ist ein quelloffenes Ruby-Programm zum Einsammeln der Lizenzinformationen von Abhängigkeiten. Um ihn in unserer Pipeline einzusetzen bauen wir uns zuerst ein Docker-Image, das alle Voraussetzungen mitbringt:
Dockerfile für den License Finder
FROM ruby:2.7 #1
RUN apt update
RUN apt install -y nodejs #2
RUN apt install -y npm #2
RUN apt install -y maven #2
RUN gem install license_finder #3
RUN mkdir /src
WORKDIR /src/
ENTRYPOINT ["/bin/sh"]
Neben einer Ruby-Umgebung (#1) bedeutet das auch, dass die Package Manager Maven und NPM (#2) installiert sein müssen. Anschließend können wir mit RubyGems den License-Finder (#3) installieren.
In unserem Gitlab-Pipeline-Job:
gitlab-ci.yml für die Jobs in der Pipeline
stages:
- analyze
- pages
license_finder:
image: ruby-maven-npm
stage: analyze
script:
- license_finder report -prepare --aggregate-paths=backend frontend --format html --save=license-report.html #1
- license_finder action-items --aggregate-paths=backend frontend #2
artifacts:
paths:
- license_report.html #3
allow_failure: true #4
pages: #3
stage: pages
script:
- mv license_report.html /public/license_report.html
artifacts:
paths:
- public
starten wir den License-Finder und lassen ihn einen HTML-Report erzeugen (#1), den wir von Gitlab veröffentlichen (#3) lassen. In diesem Schritt weisen wir den License-Finder auch durch den Parameter –prepare an, mithilfe von Maven und NPM alle Abhängigkeiten unseres Projekts zu installieren.
Mit –aggregate-paths=backend frontend listen wir die Ordner auf, in denen der License-Finder nach Dependendy-Management-Dateien suchen soll. Mit –recursive ließe sich die gesamte Ordner-Struktur durchsuchen.
Anschließend führen wir ihn erneut aus, damit er unbekannte oder verbotene Lizenzen durch einen Fehlercode an die Pipeline meldet.
Wir entscheiden uns in diesem Fall, die Pipeline nicht abbrechen zu lassen (#4), um ggf. weitere Build-Schritte durchzuführen. Der Job wird jedoch als fehlgeschlagen in der Pipeline hervorgehoben.

Unter der Pages-Seite von Gitlab können wir anschließend den Report betrachten. Hier fällt bereits auf, dass nicht nur die Abhängigkeiten, die wir in den Package-Managern direkt referenziert haben aufgelistet werden, sondern auch alle indirekten Abhängigkeiten und deren Lizenzen.

Um dem License-Finder nun mitzuteilen, welche Lizenzen zulässig sind, müssen wir diese in einer beigelegten Datei auflisten. In diesem Fall haben wir die MIT- und die Apache 2.0-Lizenzen zugelassen. Alle anderen Lizenzen werden im Report zur Überprüfung angezeigt.
dependency_decisions.yml
---
- - :permit
- MIT
- - :permit
- Apache 2.0
Bei Bedarf kann das Fehlschlagen des License Finder-Jobs in der Pipeline nun für Quality Gates als Kriterium gebraucht werden, um etwa die Release-Bereitschaft des Projekts oder die Qualität von Pull Requests zu beurteilen.
SonarQube und das Porsche-Plugin
Zur Demonstration des SonarQube-Porsche-Plugins liegt ebenfalls ein entsprechendes Projekt bereit.
SonarQube besteht dabei aus zwei Komponenten: dem Code-Scanner, der das Projekt auf verschiedenste Kriterien untersucht, und dem Management-System, das die Ergebnisse aufbereitet und darstellt.
Der Code-Scanner kann dabei als eigenständiges Kommandozeilen-Programm ausgeführt werden, muss dann aber entsprechend über die Projektstruktur unterrichtet werden. Da wir unser Projekt bereits mit Maven strukturiert haben, kann uns das Maven-Sonar-Plugin die meiste Arbeit abnehmen. Es kann mit mvn verify sonar:sonar gestartet werden, nachdem wir es in der pom.xml integriert haben.
Maven-Sonar-Plugin in der pom.xml
<build>
<plugins>
<plugin>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>4.0.0.4121</version>
</plugin>
</plugins>
</build>
Wir müssen nun lediglich noch in den pom.xml-Dateien der Module im Property sonar.sources die Dependendy-Management-Dateien des Moduls auflisten. Zum Beispiel im Frontend-Modul die package.json.
Damit Maven neben den Java-Abhängigkeiten auch die NPM-Abhängigkeiten lädt, nutzen wir im Frontend-Modul das Maven-Frontend-Plugin. So werden mit einem Kommando alle Abhängigkeiten geladen und der Code-Scanner ausgeführt. In anderen Setups müssen gegebenenfalls alle Abhängigkeiten in einem separaten Schritt heruntergeladen werden, bevor der Code-Scanner ausgeführt wird.
Wir starten SonarQube lokal in einem Docker-Container. Wichtig ist nur, dass wir das Porsche-Plugin am richtigen Ort hinterlegen, damit es von SonarQube eingebunden wird.
Dockerfile für ein SonarQube mit Porsche-Plugin
FROM sonarqube:latest
# Load and deploy the Porsche plugin
RUN wget https://github.com/porscheinformatik/sonarqube-licensecheck/releases/download/v6.0.1/sonarqube-licensecheck-plugin-6.0.1.jar -O /opt/sonarqube/extensions/plugins/sonarqube-licensecheck-plugin.jar
RUN chmod 774 /opt/sonarqube/extensions/plugins/sonarqube-licensecheck-plugin.jar
EXPOSE 9000
ENTRYPOINT ["/opt/sonarqube/docker/entrypoint.sh"]
Gestartet werden kann das SonarQube-Managementsystem nun mittels der docker-compose.yml: docker compose up -d sonarporsche
Bevor wir das Projekt scannen und auswerten können, muss das Projekt in Sonar angelegt und das Plugin aktiviert werden. Dafür sind einige Schritte erforderlich:
- Login am lokalen Sonar unter localhost:9000 (in unserem Fall mit admin:admin)
- Anlegen eines lokalen Projekts mit dem Namen Sonar-Porsche-Demo (Anleitung)
Die Regeln des Porsche-Plugins sind standardmäßig deaktiviert. Um sie zu aktiveren müssen die Standard-Quality-Profile von Sonar erweitert werden. Unser Projekt benötigt dies für Maven (Java) und NPM (JavaScript).
- Erweitern des Standard-Profils (Anleitung) für Java und Aktivieren der beiden Regeln
- Dependency has unknown license
- License is not allowed
- Dasselbe wiederholen für JavaScript
- Zuordnen der beiden neuen Quality Profiles zu unserem Projekt (Anleitung)
- Anlegen eines Projekt-Tokens (Anleitung) und hinterlegen für die Umgebungsvariable PROJECT_TOKEN in der docker-compose.yml.
Anschließend können wir den ersten Scan durchführen. Dafür starten wir den vorbereiteten Container aus der docker-compose.yml mit: docker compose up maven-sonar.
Die Ausgabe verrät uns bereits etwas über gefundene Lizenzen:
maven-sonar | [INFO] 22:26:50.368 No License found for Dependency package-json-from-dist (license from source: BlueOak-1.0.0)
maven-sonar | [INFO] 22:26:50.368 No License found for Dependency path-scurry (license from source: BlueOak-1.0.0)
maven-sonar | [INFO] 22:26:50.368 No License found for Dependency signal-exit (license from source: ISC)
maven-sonar | [INFO] 22:26:50.369 No License found for Dependency tslib (license from source: 0BSD)
Die Meldung „No License found“ meint hier lediglich, dass die Lizenz noch nicht in der Sonar-Konfiguration (als erlaubt oder verboten) registriert wurde.
In der Sonar-Projekt-Übersicht finden wir nun die gefundenen Lizenzen bereits unter Issues (lokal). Außerdem liefert das Plugin ein eigenes Dashboard (lokal).
Im nächsten Schritt werden wir bestimmte Lizenzen freigeben, so dass sie von Sonar nicht mehr als Problem identifiziert werden. Die Konfiguration des Plugins finden wir unter Administration > General Settings > License Check (lokal).
- Im Abschnitt Licenses lassen sich bekannte Lizenzen registrieren. Hier können diese außerdem bereits global über alle Projekte zugelassen werden.

- Im Abschnitt License Mapping können verschiedene Schreibarten der selben Lizenz zugeordnet werden. Dafür können reguläre Ausdrücke definiert werden.

- Im Abschnitt Dependency Mapping können die Lizenzen nachgetragen werden für Abhängigkeiten ohne Lizenzinformationen.
- Im Abschnitt Project Licenses können nun Entscheidungen über die Zulässigkeit von Lizenzen für einzelne Projekte getroffen werden.
Um die Änderungen anzuwenden muss nun ein neuer Scan durchgeführt werden. Im Anschluss können wir im License-Checker-Dashboard die Auswertung der Abhängigkeiten sehen.

Mit Hilfe der Quality Gates von SonarQube können in einem nächsten Schritt für Verstöße gegen Lizenz-Regelungen entsprechende Konsequenzen definiert werden, etwa die Beurteilung der Release-Bereitschaft des Projekts oder die Zulässigkeit von Pull-Requests.
Fazit
Neben dem License Finder und dem Porsche-Plugin gibt es zahllose andere Lösungen für die Lizenzverwaltung von Softwareprojekten. Einige werben damit dezidierte Lizenz-Datenbanken zu betreiben, die Lücken bei den öffentlich und maschinenlesbar zugänglichen Lizenzinformationen schließen. Andere Features sehen eine Beurteilung der Kritikalität von Lizenzen vor, werben mit intuitiver Bedienung oder modifizierbaren Workflows. Wie so oft steckt der Teufel jedoch im Detail. So haben verschiedene Lösungen Probleme, Softwarekomponenten korrekt einzuordnen, die unter mehreren Lizenzen gleichzeitig publiziert sind.
Da die meiste professionelle Software-Entwicklung bereits auf Static-Code-Analysetools und CI/CD-Systeme setzt, bietet es sich an, zuerst die License Compliance-Lösungen der bereits verfügbaren Infrastruktur zu nutzen.
In jedem Fall sollten sich Lizenz-Checks zu einer Selbstverständlichkeit in der modernen Softwareentwicklung entwickeln und fester Bestandteil des DevOps-Werkzeugkastens werden.

Kommentar verfassen