SCA hat unsere Dependencies abgedeckt. License Compliance hat geklärt, was wir ausliefern dürfen. SAST ist die Stelle, an der wir die Scanner auf den Code richten, den wir selbst geschrieben haben. In Teil 5 unserer GitHub DevSecOps Serie integrieren Patrick Steger und ich Static Application Security Testing in die Pipeline — und merken auf die harte Tour, dass es auf GitHub drei Actions braucht statt einer.
Warum drei Tools#
SAST geht darum, Security-Flaws in deinem eigenen Source Code zu finden, nicht in den Dependencies. Auf GitHub haben wir am Ende drei Scanner verdrahtet:
- CodeQL — die statische Analyse-Engine von GitHub selbst, als Standard-Action verfügbar.
- SpotBugs — ein etablierter Java-Scanner, stark bei klassischen Code-Quality- und Security-Regeln.
- Semgrep — ein neuerer Pattern-Scanner; die Action stammt direkt vom Semgrep-Team.
Drei Scanner für ein Beispielprojekt klingt viel. Für eine reale Firmencodebase ist das ein vernünftiger Startpunkt. Jedes Tool hat eigene Stärken und blinde Flecken — und selbst mit allen drei aktiv haben wir Findings übersehen, die andere Plattformen leicht entdecken. Es gibt aktuell keine einzelne GitHub-Action, die SAST end-to-end zu vernünftigen Kosten abdeckt, also ist das Stapeln von Tools der pragmatische Weg.
Eine zweite Hürde: Viele SAST-Scanner im Marketplace brauchen zusätzliche Accounts und schreiben ihre Findings nicht von Haus aus in den GitHub Security Tab. Damit SpotBugs-Ergebnisse im Security Tab erscheinen, brauchten wir eine custom Action — Juan Manuel von Microsoft hat uns geholfen, die SpotBugs-Ausgabe in SARIF zu konvertieren, das standardisierte Format, das der Security Tab versteht.
SAST in die Pipeline einhängen#
Wir nutzen dasselbe Muster wie zuvor: ein Workflow sast.yml, der aus der Main-Pipeline aufgerufen wird. Er definiert drei Jobs — Sast-CodeQL, Sast-SpotBugs und Sast-Semgrep — und kann manuell via workflow_dispatch oder über die Main-Pipeline gestartet werden.
CodeQL. Standard runs-on: ubuntu, die Permissions, die die Action braucht (die wir aus den Marketplace-Docs und etwas Trial-and-Error herausgefunden haben), fail-fast: false, damit der ganze Job durchläuft, und eine Matrix, die die Sprache aus Geschwindigkeitsgründen auf Java pinnt. Die Steps checken den Code aus, initialisieren CodeQL, setzen JDK 11 auf, bauen das Projekt und führen den Analyzer aus. Wir bauen hier neu, eine reale Pipeline könnte aber das Artefakt aus dem Build-Job wiederverwenden.
SpotBugs. Gleiches Skelett — Name, Runner, Permissions, Steps — plus ein needs: Sast-CodeQL, damit der Cache aus dem CodeQL-Job genutzt werden kann. Nach dem SpotBugs-Lauf laden wir die Artefakte hoch und führen anschliessend den speziellen SARIF-Upload-Step aus, damit die Findings im Security Tab erscheinen.
Semgrep. Läuft im eigenen Docker-Container von Semgrep, hat ein needs für den Cache, checkt den Code aus und führt den Scanner mit einem expliziten Ruleset aus. Für Java sind das rund 110 Regeln — genug, um echte Probleme zu finden, aber nur ein Bruchteil dessen, was dedizierte kommerzielle Tools mitbringen.
Was die Scanner gefunden haben#
Nach dem Pipeline-Lauf erscheinen die neuen Jobs unter Actions, und die SARIF-Artefakte (Semgrep.sarif, SpotBugs.sarif) hängen am Run. Filtert man im Security Tab nach Tool:
- CodeQL: zwei Findings. Use of broken or risky cryptography — alter MD5-Hash. Und Cross-Site Scripting — wir geben die Nachricht des Aufrufers an den Aufrufer zurück, ein lehrbuchhaftes Reflected XSS.
- Semgrep: drei Findings. Das MD5-Problem zweimal (es matcht zwei Regeln), dazu eine Docker-Empfehlung, den Container nicht als Root, sondern als dedizierten User laufen zu lassen.
- SpotBugs: keine Findings in diesem Projekt.
Was die Scanner übersehen haben#
Der absichtlich kaputte Controller enthält auch hardcoded Passwörter und Secrets — die erwarten wir vom Secret Scanning in einer späteren Session. Beunruhigender: ein offensichtlicher Einsatz von Random, um einen Schlüssel zu erzeugen, und eine keybytes-Konstante aus statischem Material. Beides leicht zu erkennende Schwächen, beides wurde nicht gemeldet. Mit kombinierter kommerzieller Tooling auf einer anderen Plattform haben wir das mühelos gefunden. Hier nicht.
Ehrliche Zusammenfassung: GitHub-SAST funktioniert, die SARIF-Integration in den Security Tab ist wirklich nützlich — aber rechne damit, dass du in zusätzliche Regeln oder eigene Scanner investieren musst, wenn du Parität mit dem brauchst, was andere Plattformen out of the box zeigen.
Key Takeaways#
Eine Action reicht nicht. Es gibt keine einzelne GitHub-SAST-Action, die alles bezahlbar abdeckt. Wir haben CodeQL, SpotBugs und Semgrep gestapelt — und übersahen trotzdem Findings, die andere Plattformen entdecken.
SARIF ist Eintrittspreis. Liefert ein Scanner kein SARIF, erscheinen seine Findings nicht im GitHub Security Tab. Für SpotBugs brauchten wir eine custom Action zur Konvertierung. Prüfe das, bevor du ein Tool auswählst.
Permissions sind Trial-and-Error. Jede Action deklariert die GitHub-Permissions, die sie braucht. Die Doku ist uneinheitlich; rechne mit ein paar Iterationen, bis der Workflow sauber läuft.
Cache zwischen den Scanner-Jobs teilen. Nutze
needs:und dieactions/cache-Action, um kompiliertes Java und Dependencies zwischen CodeQL, SpotBugs und Semgrep zu teilen. Sonst zahlst du den Build-Aufwand dreimal.Sprach-Matrix pinnen. CodeQL kann viele Sprachen scannen. Die Matrix auf das, was du tatsächlich ausspielst — bei uns Java — zu beschränken, hält die Pipeline-Zeit klein. Sprachen explizit ergänzen, wenn nötig.
Mit Lücken rechnen. MD5 und Reflected XSS wurden gefunden. Ein per
Randomerzeugter Schlüssel und statisches Schlüsselmaterial nicht. Behandle das GitHub-SAST-Default-Setup als Baseline, nicht als Vollabdeckung — und plane Budget für custom Rules oder kommerzielle Scanner ein, wo das Risiko es rechtfertigt.
