Push-Schutz
Jeder Push in ein fremforge-Repository durchläuft einen Gitleaks-Pre-Receive-Hook auf dem Forgejo-Server, auch der allererste Push in ein neues Repository. Findet der Scanner Zugangsdaten, wird der Push abgewiesen, und die Entwicklerin oder der Entwickler sieht die passende Regel (es wird weder ein Wert noch ein Commit-SHA an Dritte weitergegeben).
Das ist etwas anderes als SAST-Befunde und Container-Image-Scanning: Der Push-Schutz läuft zum Zeitpunkt des git push, bevor die Commits irgendwo gespeichert sind, das fremforge schützen muss. Die anderen Scanner laufen später auf dem gespeicherten Ergebnis.
Was erkannt wird
Der Standard-Regelsatz von Gitleaks plus Ergänzungen von fremforge:
- Schlüssel von Cloud-Anbietern (AWS, JSON für GCP-Dienstkonten, Azure-SAS-Token, AK/SK von T Cloud Public)
- Verbreitete SaaS-API-Schlüssel (Stripe, Mollie, SendGrid, Twilio, Lettermint, OpenAI)
- Allgemeine Muster mit hoher Entropie (private PEM-Schlüssel, JWT, private SSH-Schlüssel)
- fremforge-spezifisch: persönliche Zugriffstoken (
ffp_*), OAuth-Client-IDs (ffc_*) und -Secrets (ffs_*), private OIDC-Schlüssel der Runner, Bootstrap-Zugangsdaten für OBS-Buckets, HMAC-Secrets für die Signatur von Bestätigungen
Das Signaturpaket von Gitleaks ist auf eine Version festgelegt, wird bei jeder Anhebung geprüft und über denselben SWR-Mirror ausgeliefert wie unsere übrigen Entwicklerwerkzeuge. Zum Zeitpunkt des Pushs ziehen wir nichts aus der Content-Registry von GitHub.
Deine Organisation kann auch eigene Muster hinzufügen, für jedes Repository oder für ein einzelnes. Siehe Eigene Muster. Sie werden beim Push auf dieselbe Weise angewendet, und eine Abweisung nennt die Regel-ID des Musters, custom-<name>.
Was Entwickler sehen
Trifft ein Push auf ein erkanntes Secret, sieht der Entwickler:
remote: Push rejected by fremforge push-protection.
remote:
remote: Matched rule: aws_access_key_id
remote: Path: src/services/uploader.ts:42
remote: Commit: <commit-sha>
remote:
remote: If this is a false positive, ask an org owner to add an
remote: override at frem.sh/<your-org>/_admin/push-protection.
remote: Override IDs are auditable and time-boxed.
remote: Or ask for this commit to be let through:
remote: https://frem.sh/_app/settings/push-protection/bypass/new?repo=<org>/<repo>&commit=<commit-sha>Kein Wert der Zugangsdaten wird protokolliert oder ausgegeben. Der Entwickler hat den Quellcode ohnehin offen und kann den Fehlalarm selbst finden.
Immer an, und die eine Einstellung
Der Push-Schutz lässt sich für eine Organisation oder ein Repository nicht ausschalten. Die einzige Einstellung ist das Fehlerverhalten des Scanners unter Sicherheit › Richtlinien › Push-Schutz. Es entscheidet, was mit einem Push geschieht, wenn der Scanner selbst nicht verfügbar ist:
- Blockieren (Standard): Der Push wird mit einer klaren Fehlermeldung abgewiesen, und du versuchst es erneut, sobald der Scanner wieder läuft.
- Zulassen: Der Push geht durch, und ein Allow-on-Error-Ereignis wird im Audit-Trail erfasst. Secrets, die in diesem Zeitfenster gepusht werden, findet später das wöchentliche Secret-Scanning über die gesamte Historie.
Die Änderung wirkt ab dem nächsten Push und wird im Audit-Log erfasst.
Ein neues Repository ist ab seinem ersten Push geschützt. Der Hook wird beim Anlegen des Repositorys angehängt, sodass auch ein git push direkt nach dem Anlegen gescannt wird.
Ausnahmen
Admins des Mandanten können Ausnahmen für Fälle hinzufügen, in denen der Scanner danebenliegt (Test-Fixtures, Beispiel-Snippets aus SDKs, Dokumentation, die absichtlich die Form eines Schlüssels zeigt).
- Admin des Mandanten → Sicherheit › Richtlinien › Push-Schutz. Klappe Aktive Ausnahmen auf und klicke auf Ausnahme hinzufügen. Bei einem abgewiesenen Push füllt Freigeben… das Formular aus dieser Abweisung aus.
- Wähle, was sie abdeckt: Nur dieser Commit (eine Regel in einem gepushten Commit), Nur diese Regel überall (jeder Treffer einer Regel), Nur dieser Pfad global (ein Pfad-Glob wie
test/fixtures/**) oder eine eigene Kombination. Jedes Feld, das du setzt, muss passen; ein Feld, das du leer lässt, passt auf alles. Mindestens eines von Commit, Regel und Pfad ist erforderlich. - Beschränke sie optional auf ein Repository; andernfalls gilt sie für jedes Repository der Organisation.
- Lege das Ablaufdatum fest. Jede Ausnahme endet: Das Datum ist standardmäßig 90 Tage ab heute und darf höchstens 1 Jahr entfernt sein. Eine abgelaufene oder widerrufene Ausnahme wird nicht mehr berücksichtigt; wird sie noch gebraucht, legst du eine neue an.
- Gib einen Grund an (erforderlich, auditiert).
Wie ein Push beurteilt wird. Der Pre-Receive-Hook sammelt jedes Secret, das er in den Commits findet, die ein Push einbringt (Regel, Pfad und Zeile, nie den Wert), und fragt fremforge, ob jedes davon durch eine aktive Ausnahme abgedeckt ist. Der Push geht nur durch, wenn jeder Befund abgedeckt ist; ein einziger nicht abgedeckter Befund weist ihn wie bisher ab. Lässt eine Ausnahme einen Push durch, sieht der Entwickler, welche Ausnahme(n) gegriffen haben, und push-protection.override.used wird in das Audit-Log deiner Organisation geschrieben. Kann fremforge nicht gefragt werden (etwa während eines Ausfalls), wird der Push abgewiesen: Ausnahmen schlagen nie offen fehl. Eine Commit-Ausnahme deckt den gepushten Commit ab (die Spitze des Branches), also den Commit, den eine Abweisung nennt. Ein Secret in einem Archiv wird am Pfad des Archivs erkannt.
Ausnahmen aus der Zeit vor der Begrenzung. Ausnahmen, die vor dem 2026-10-18 angelegt wurden, konnten ohne Ablaufdatum bleiben. Sie funktionieren unverändert weiter, und die Konsole und das Audit-Log zeigen sie als Kein Ablaufdatum (vor der Begrenzung angelegt). Widerrufe eine solche Ausnahme und lege sie neu an, um ihr ein Ablaufdatum zu geben.
Ausnahmen gelten nur für den Push-Schutz. Ein Secret, das bereits in der Historie steht und vom wöchentlichen Durchlauf aufgeführt wird, behandelst du, indem du den Befund verwirfst, nicht mit einer Ausnahme.
Über die REST-API
Ausnahmen vom Push-Schutz, Abweisungen und das Fehlerverhalten lassen sich programmatisch verwalten. Das ist nützlich, wenn du das Onboarding von Mandanten skriptest, eine CI-Pipeline anbindest oder einen KI-Agenten steuerst.
- Ausnahmen auflisten
GET /_app/api/v1/orgs/:slug/push-protection/overrides - Ausnahme anlegen
POST /_app/api/v1/orgs/:slug/push-protection/overridesBody:{ "kind": "path", "path_pattern": "test/fixtures/**", "reason": "test fixtures", "expires_at": "2027-01-01T00:00:00Z" } - Ausnahme widerrufen
DELETE /_app/api/v1/orgs/:slug/push-protection/overrides/:id - Fehlerverhalten setzen
PUT /_app/api/v1/orgs/:slug/push-protection/failure-modeBody:{ "mode": "block" }
Ohne expires_at läuft eine neue Ausnahme 90 Tage nach ihrem Anlegen ab; die Antwort enthält das Datum. expires_at (ein Datum oder Zeitstempel nach ISO 8601) muss in der Zukunft und höchstens 1 Jahr entfernt liegen. Ein späteres Datum wird mit 400 expires-at-too-far abgelehnt, nicht gekürzt. In der Liste kennzeichnet no_expiry_legacy: true eine Ausnahme, die vor der Begrenzung angelegt wurde; das ist die einzige Art mit expires_at: null.
Das Anlegen oder Widerrufen einer Ausnahme braucht ein Token mit policy:write, weil eine Ausnahme Pushes vom Secret-Scanning ausnimmt; das Runner-Token eines CI-Jobs trägt diesen Scope nie. Auch das Setzen des Fehlerverhaltens braucht policy:write, weil es den Push-Schutz für die ganze Organisation ausschalten kann. Das Auflisten braucht findings:read, das Verwerfen einer Abweisung findings:write.
curl -X POST \
-H "Authorization: Bearer ${FREMFORGE_PAT}" \
-H "Content-Type: application/json" \
-d '{"kind":"path","path_pattern":"test/fixtures/**","reason":"test fixtures","expires_at":"2027-01-01T00:00:00Z"}' \
https://frem.sh/_app/api/v1/orgs/acme/push-protection/overridesDie vollständige OpenAPI-Spezifikation findest du in der Referenz der öffentlichen REST-API.
Eine Umgehung beantragen
Ein blockierter Entwickler muss keinen Admin suchen, der eine Ausnahme hinzufügt. Die Abweisung gibt einen Link zu einem Antragsformular für genau diesen Commit aus. Jeder in der Organisation mit Schreibzugriff auf das Repository kann ihn öffnen und einen Grund angeben. Der Link enthält kein Token, du meldest dich also an, um ihn zu nutzen.
- Wer entscheidet. Owner der Organisation und die Mitglieder, die deine Organisation zu Freigebenden für Ausnahmen gemacht hat. Wer den Antrag gestellt hat, kann ihn nie selbst freigeben. Die Entscheidenden erhalten eine E-Mail, wenn ein Antrag eingeht, und das Webhook-Ereignis
security.exception.requestedwird gesendet. - Was eine Freigabe bewirkt. Sie legt eine gewöhnliche Ausnahme vom Push-Schutz an, gebunden an dieses Repository und diesen Commit und 7 Tage gültig. Pushe denselben Commit innerhalb dieser Zeit erneut, und er geht durch. Ein anderer Commit mit demselben Secret wird weiterhin blockiert und braucht einen eigenen Antrag.
- Wie lange ein Antrag offen bleibt. Entscheidet niemand innerhalb von 7 Tagen, läuft der Antrag ab.
- Wer freigeben kann, und wie. Für eine Freigabe braucht es eine Person, die sich mit Zwei-Faktor-Authentifizierung (oder über dein SSO) angemeldet hat, auf der Antragsseite oder mit einem Forgejo-OAuth-Token über die API. Ein API-Token der Organisation kann über einen Antrag nicht entscheiden.
- Keine Sonderwege. Der blockierte Push eines Bots durchläuft denselben Ablauf. Ein Mitglied mit Schreibzugriff stellt den Antrag in seinem Namen.
- Missbrauchsgrenze. Eine Person kann in einer Organisation höchstens 10 Anträge pro Tag stellen.
- Was angezeigt wird. Das Repository, der Commit und die Regel-IDs, nie das Secret. Anträge, Entscheidungen und die Ausnahme werden im Audit-Log erfasst (
push-protection.bypass.requested,.approved,.rejected,.cancelled). Wer den Antrag gestellt hat, erfährt das Ergebnis per E-Mail und Webhook (security.exception.approvedodersecurity.exception.rejected).
Owner sehen offene und kürzliche Anträge auf der Seite Push-Schutz unter Umgehungsanträge. Über die API:
- Auflisten
GET /_app/api/v1/orgs/:slug/push-protection/bypass-requests(?status=requestedfür die offenen) - Beantragen
POST /_app/api/v1/orgs/:slug/push-protection/bypass-requestsBody:{ "repository": "app", "commit_sha": "<full sha>", "reason": "..." } - Entscheiden
POST .../bypass-requests/:id/approveoder.../reject, optional{ "comment": "..." } - Zurückziehen
POST .../bypass-requests/:id/cancel(nur die antragstellende Person)
Umgehung bei bereits geleakten Zugangsdaten
Eine Blockierung durch den Push-Schutz heißt nicht, dass die Zugangsdaten sicher sind. Wenn du auf die Blockierung triffst, liegen die Zugangsdaten bereits in einem Commit auf deinem Laptop. Rotiere die Zugangsdaten, bevor du irgendetwas anderes tust. Danach kannst du den Commit per Rebase aus dem Branch entfernen oder mit einer Ausnahme pushen; beide Wege funktionieren, sobald die Zugangsdaten ungültig sind.
Pushes und die Committer-Regel
Jeder Push wird auch mit dem Konto erfasst, das ihn authentifiziert hat (HTTPS-Token, OAuth-Token, SSH-Schlüssel, Deploy Key oder Actions-Job), und deine Organisation kann verlangen, dass neue Commits eine der verifizierten E-Mail-Adressen der pushenden Person verwenden: aus, warnen oder blockieren. Siehe Pushes und die Committer-Regel.
Was er nicht tut
- Er scannt die Historie nicht rückwirkend. Wurden Zugangsdaten committet, bevor der Push-Schutz eingeschaltet war, bleiben sie committet. Verwende ein Werkzeug zum Umschreiben der Historie (der Migrationsleitfaden führt durch die Schritte).
- Er erkennt nicht jede denkbare Form von Zugangsdaten. Eigene Regeln pro Mandant schließen die Lücken, die für deinen Stack zählen.
- Er läuft nicht bei
git push --forceeines Branches, der fremforge bereits bekannt ist (der Pre-Receive-Hook läuft zwar, aber der Vergleich erfolgt gegen die neue Spitze; ein Force-Push, der einen geleakten Commit mit einem sauberen überschreibt, ist erlaubt).