Zum Hauptinhalt springen
GitHub DevSecOps Teil 10: Branch Protection und Pull Requests
  1. Blogs/

GitHub DevSecOps Teil 10: Branch Protection und Pull Requests

Autor
Romano Roth
Ich bin überzeugt: Der nächste Wettbewerbsvorteil ist nicht AI selbst, sondern die Organisation drumherum. Als Chief AI Officer bei Zühlke arbeite ich mit C-Level-Führungskräften daran, Unternehmen zu bauen, die wahrnehmen, entscheiden und sich kontinuierlich anpassen. Seit über 20 Jahren mache ich diese Überzeugung zur Praxis.
Frag die KI über diesen Artikel

In den vorherigen neun Sessions haben Patrick Steger und ich eine GitHub DevSecOps-Pipeline gebaut mit Build, SCA, License Compliance, SAST, Container Scanning, Secret Detection und DAST. Alles nützlich — aber nur dann, wenn sie tatsächlich läuft, bevor Code in main landet, und nur dann, wenn der Merge blockiert wird, sobald etwas Ernstes auftaucht. In Teil 10 verdrahten wir dieses Gate mit Pull Requests und Branch Protection Rules.

Pull Requests sind Diskussion, kein Submit-Button
#

Um einen Pull Request zu verstehen, musst du zuerst verstehen, was ein Branch ist. Der Main-Branch hält das Projekt. Wenn ich eine Änderung machen will, erstelle ich einen Feature-Branch, committe darauf, und wenn ich fertig bin, öffne ich einen Pull Request, um diese Commits zurück in main zu pullen.

Patrick stellte die naheliegende Frage: Öffne ich den PR erst, wenn ich komplett fertig bin? Absolut nicht. Der ganze Sinn eines modernen Pull Requests ist die Diskussion. Öffne ihn früh, diskutiere mit dem Team, push weitere Commits, während die Diskussion läuft. Das “Pull” im Namen ist historisch. Heute ist der PR der Ort, an dem Reviews passieren, an dem Workflows ihre Ergebnisse melden und an dem schliesslich die Merge-Entscheidung fällt.

Wenn alles validiert ist — sowohl menschliche Reviews als auch automatisierte Workflows —, ist der letzte Schritt eines Pull Requests das Mergen zurück nach main.

Branch Protection ist das, was es durchsetzt
#

Branch Protection ist das Mittel, um diese Schritte durchzusetzen, bevor irgendetwas im Target-Branch landet. Die Regeln können verlangen, dass Workflows laufen, Approvals von einem Security-Experten oder Architekten vorhanden sind und bestimmte Status Checks bestanden werden.

Konfiguriert wird das unter Settings → Branches → Branch Protection rules. Es gibt viele Optionen, und die für DevSecOps wichtigen gehen wir in der Demo durch.

Best Practices
#

Vor der Demo stellen wir die Praktiken zusammen, die wir für Pull Requests empfehlen:

  • Pull Requests früh und oft öffnen, klein halten. Je kleiner die Batch Size, desto schneller der Feedback-Loop.
  • Ein Pull Request muss nicht gemerged werden. Er kann auch abgelehnt oder genutzt werden, um Alternativen im Team auszuloten.
  • Pull Request Templates definieren, damit Reviewer immer den nötigen Kontext bekommen.
  • Branch Protection Rules definieren — darum geht es in diesem Video.
  • Die verschiedenen Merge-Methoden (Merge, Squash, Rebase) gezielt einsetzen.

Die Demo: PR ohne Protection
#

Ich editiere das POM-File und hebe Spring Boot von 2.0.1 auf 2.0.9. Zusätzlich baue ich einen neuen MD5-Hash auf einer Message ein — bewusst ein unsicherer Crypto-Algorithmus, um SAST zu triggern. Patrick erinnert mich daran, dass ich vergessen habe, vorher einen Branch zu erstellen. Kein Problem in GitHub: Du erstellst einen neuen Branch und verschiebst die Änderungen direkt dorthin.

Wir öffnen einen Pull Request von fixes nach main. Wir sehen Platz für Conversation, die Commits, die geänderten Files — aber nur ein Security Check läuft, nicht die volle Pipeline. Der Grund: Als wir den Pipeline-Workflow gebaut haben, haben wir ihn auf on: push: branches: [main] beschränkt. Die Pipeline läuft nur auf main.

Auch sonst fehlt etwas: Nichts erzwingt eine Approval, nichts blockiert den Merge.

Branch Protection konfigurieren
#

Settings → Branches → Add Branch Protection rule. Wir verwenden *, sodass die Regel für jeden Branch greift. Dann aktivieren wir:

  • Require a Pull Request before merging. Kein Push mehr direkt auf main.
  • Require approvals. Da nur Patrick und ich auf dem Repo sind, setzen wir die Anzahl auf 1.
  • Require status checks before merging, plus require branches to be up to date before merging.

Bei den Status Checks musst du jeden Job einzeln per Name hinzufügen: Build, SCA, License Compliance, SAST, Docker Image, Container Image Scan, DAST, License Compliance, Test Results. Patrick fand die Usability dieses Teils nicht gerade gelungen, und er hat recht — es ist Detailarbeit, ein Job nach dem anderen. Wir aktivieren ausserdem “Do not allow bypassing the above settings” — genau das, was du auf einem Production-Branch willst.

Zurück im Pull Request sind die Required Checks jetzt aufgelistet, laufen aber immer noch nicht — weil der Workflow weiterhin auf main gepinnt ist. Also editiere ich den Workflow, entferne den Branch-Filter, sodass die Pipeline auf jedem Branch läuft, und committe das auf main. Weil die Änderung auf main passierte, muss der bestehende PR geschlossen und neu geöffnet werden (und der Branch aktualisiert), bevor die neue Workflow-Definition greift.

Die Ergebnisse am PR lesen
#

Jetzt läuft die Pipeline gegen den PR. Build läuft, SCA läuft, Test Results erscheinen direkt am Pull Request, und der GitHub Code Scanning Report bringt unsere neuen Findings hoch — inklusive des MD5-Issues. Es taucht sogar zweimal auf, weil sowohl Semgrep als auch CodeQL es erkennen. Jedes Finding hat “Show paths” und “Dismiss this alert”, plus einen Conversation-Thread pro Finding.

Eine ehrliche Lücke: GitHub zeigt nicht, welche Findings ein Commit behoben hat — nur die, die er eingeführt hat. DAST ist auch fehlgeschlagen, in unserem Fall aber wegen eines Rate Limits beim Open-Source-Service, den wir nutzen, nicht wegen eines echten Issues.

Der Merge ist immer noch blockiert, weil wir noch nicht freigegeben haben. Patrick approved mit der Notiz, dass der MD5-Hash nur eine interne Hash-Funktion ist. Der Merge-Button entriegelt sich, wir bestätigen den Merge — der Pull Request ist geschlossen, die Änderungen sind in main, und der Workflow läuft erneut auf main, wie erwartet.

Recap
#

Pull Requests sind die Diskussionsfläche; Branch Protection ist die Durchsetzung. Ohne die Protection Rules ist der PR nur ein UI. Mit Protection Rules, die PRs, Approvals und das volle Set an Pipeline-Status-Checks erzwingen, wird der PR zu einem echten DevSecOps-Gate. Neue Findings sind pro PR sichtbar, Diskussionen passieren am Finding, und Merges sind blockiert, bis Mensch und Tooling übereinstimmen.

Nächste Session: Scheduled Pipelines — sicherstellen, dass Code, der bereits in Produktion läuft, weiter gescannt wird, auch wenn niemand committet.

Key Takeaways
#

  1. Ein Pull Request ist Diskussion, kein finaler Submit. Öffne ihn früh, halte Batches klein, und lass Diskussion und Workflows die Änderung formen, während sie entsteht.

  2. Branch Protection ist das, was den PR zum Gate macht. Ohne sie ist der PR ein hübsches UI auf einem Merge-Button. Required PRs, Required Approvals und Required Status Checks machen ihn zu etwas, das schlechte Änderungen blockt.

  3. Workflows auf die richtigen Branches pinnen. Beschränkt man die Pipeline auf main, laufen PR-Scans nie. Entweder den Branch-Filter entfernen oder explizite Pull-Request-Trigger ergänzen.

  4. Jeden Status Check namentlich hinzufügen. GitHub erlaubt kein “alle Checks”. Du listest sie auf: Build, SCA, License Compliance, SAST, Container Scan, DAST, Tests. Mühsam — aber explizit und auditierbar.

  5. PRs zeigen neue Findings, nicht behobene. Code Scanning zeigt, was dieser PR neu eingebracht hat, manchmal von mehreren Tools (Semgrep und CodeQL haben beide das MD5 gemeldet). Nützlich für Reviews; nur ist “was wurde behoben” nicht in der PR-Ansicht.

  6. “Do not allow bypassing the above settings” gibt es aus einem Grund. Schalte es für Production-Branches an, sodass auch Admins durch die Regeln müssen. Der ganze Sinn ist, dass niemand — auch nicht das eilige Future-You — das Gate umgehen kann.