Aller au contenu principal

Protection des push

Chaque push vers un dépôt fremforge passe par un hook pre-receive Gitleaks sur le serveur Forgejo, y compris le tout premier push d’un nouveau dépôt. Si l’analyseur trouve un identifiant, le push est rejeté et le développeur voit la règle correspondante (aucune valeur ni aucun SHA de commit n’est renvoyé à un tiers).

Cette protection est distincte des résultats SAST et de l’analyse des images de conteneurs : la protection des push s’exécute au moment du git push, avant que les commits ne soient stockés à un endroit que fremforge doit protéger. Les autres analyseurs s’exécutent plus tard, sur le résultat stocké.

Ce qui est détecté

Le jeu de règles par défaut de Gitleaks, plus des ajouts de fremforge :

  • Clés des fournisseurs cloud (AWS, JSON de compte de service GCP, jetons SAS Azure, AK/SK T Cloud Public)
  • Clés d’API SaaS courantes (Stripe, Mollie, SendGrid, Twilio, Lettermint, OpenAI)
  • Motifs génériques à forte entropie (clés privées PEM, JWT, clés privées SSH)
  • Spécifiques à fremforge : jetons d’accès personnels (ffp_*), identifiants de client OAuth (ffc_*) et secrets (ffs_*), clés privées OIDC des runners, identifiants d’amorçage des buckets OBS, secrets HMAC de signature des accusés de réception

Le pack de signatures Gitleaks est épinglé, revu à chaque mise à jour et distribué via le même miroir SWR que nos autres outils de développement. Nous ne téléchargeons rien depuis le registre de contenu de GitHub au moment du push.

Votre organisation peut aussi ajouter ses propres motifs, pour tous les dépôts ou pour un seul. Voir Motifs personnalisés. Ils sont appliqués au push de la même manière, et un rejet indique l’identifiant de règle du motif, custom-<name>.

Ce que voit le développeur

Lorsqu’un push contient un secret détecté, le développeur voit :

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>

Aucune valeur d’identifiant n’est journalisée ni renvoyée. Le développeur a déjà le code source ouvert ; il peut retrouver lui-même le faux positif.

Toujours active, avec un seul paramètre

La protection des push ne peut pas être désactivée pour une organisation ni pour un dépôt. Le seul paramètre est le mode de défaillance de l’analyseur, sous Sécurité › Politiques › Protection des push. Il détermine ce qu’il advient d’un push lorsque l’analyseur lui-même est indisponible :

  • Bloquer (par défaut) : le push est rejeté avec une erreur explicite, et vous réessayez une fois l’analyseur rétabli.
  • Autoriser : le push passe et un événement allow-on-error est enregistré dans la piste d’audit. Les secrets poussés pendant cette fenêtre sont détectés plus tard par l’analyse des secrets hebdomadaire de tout l’historique.

La modification prend effet au push suivant et est enregistrée dans le journal d’audit.

Un nouveau dépôt est couvert dès son premier push. Le hook est attaché à la création du dépôt ; même un git push lancé juste après la création est donc analysé.

Dérogations

Les administrateurs de l’organisation peuvent ajouter des dérogations pour les cas où l’analyseur se trompe (fixtures de test, exemples de code de SDK, documentation qui montre volontairement la forme d’une clé).

  1. Administration de l’organisation → Sécurité › Politiques › Protection des push. Dépliez Dérogations actives et cliquez sur Ajouter une dérogation. Depuis un push rejeté, Approuver… remplit le formulaire à partir de ce rejet.
  2. Choisissez ce qu’elle couvre : Uniquement ce commit (une règle dans un commit poussé), Uniquement cette règle partout (chaque correspondance d’une règle), Uniquement ce chemin, globalement (un glob de chemin tel que test/fixtures/**), ou une combinaison Personnalisé (avancé). Chaque champ renseigné doit correspondre ; un champ laissé vide correspond à tout. Au moins l’un des champs commit, règle et chemin est obligatoire.
  3. Vous pouvez la limiter à un seul dépôt ; sinon, elle s’applique à tous les dépôts de l’organisation.
  4. Définissez la date d’expiration. Toute dérogation prend fin : la date est par défaut fixée à 90 jours à compter d’aujourd’hui et peut être au plus à 1 an. Une dérogation expirée ou révoquée n’est plus prise en compte ; vous en ajoutez une nouvelle si elle reste nécessaire.
  5. Indiquez un motif (obligatoire, audité).

Comment un push est évalué. Le hook pre-receive collecte chaque secret trouvé dans les commits qu’un push introduit (règle, chemin et ligne ; jamais la valeur) et demande à fremforge si chacun est couvert par une dérogation active. Le push ne passe que si chaque résultat est couvert ; un seul résultat non couvert le rejette comme avant. Lorsqu’une dérogation laisse passer un push, le développeur voit quelle(s) dérogation(s) s’est (se sont) appliquée(s), et push-protection.override.used est écrit dans le journal d’audit de votre organisation. Si fremforge ne peut pas être interrogé (par exemple pendant une panne), le push est rejeté : les dérogations n’échouent jamais en mode ouvert. Une dérogation de commit couvre le commit poussé (la pointe de la branche), c’est-à-dire le commit qu’indique un rejet. Un secret contenu dans une archive est détecté au chemin de l’archive.

Dérogations antérieures à la limite. Les dérogations créées avant le 2026-10-18 pouvaient être laissées sans expiration. Elles continuent de fonctionner telles quelles, et la console et le journal d’audit les affichent comme Aucune expiration (créée avant la limite). Révoquez-en une et recréez-la pour lui donner une expiration.

Les dérogations ne s’appliquent qu’à la protection des push. Un secret déjà présent dans l’historique et signalé par l’analyse hebdomadaire se traite en ignorant le résultat, et non par une dérogation.

Via l’API REST

Les dérogations, les rejets et le mode de défaillance de la protection des push peuvent être gérés par programme : utile pour scripter l’intégration d’une organisation, s’intégrer à un pipeline de CI ou piloter depuis un agent IA.

  • Lister les dérogations GET /_app/api/v1/orgs/:slug/push-protection/overrides
  • Créer une dérogation POST /_app/api/v1/orgs/:slug/push-protection/overrides corps : { "kind": "path", "path_pattern": "test/fixtures/**", "reason": "test fixtures", "expires_at": "2027-01-01T00:00:00Z" }
  • Révoquer une dérogation DELETE /_app/api/v1/orgs/:slug/push-protection/overrides/:id
  • Définir le mode de défaillance PUT /_app/api/v1/orgs/:slug/push-protection/failure-mode corps : { "mode": "block" }

Sans expires_at, une nouvelle dérogation expire 90 jours après sa création ; la réponse indique la date. expires_at (une date ou un horodatage ISO 8601) doit être dans le futur et au plus à 1 an. Une date plus lointaine est refusée avec 400 expires-at-too-far, et non raccourcie. Dans la liste, no_expiry_legacy: true signale une dérogation créée avant la limite, la seule sorte qui a expires_at: null.

Créer ou révoquer une dérogation nécessite un jeton avec policy:write, car une dérogation exempte des push de l’analyse des secrets ; le jeton de runner d’un job de CI ne la porte jamais. Définir le mode de défaillance nécessite aussi policy:write, car cela peut désactiver la protection des push pour toute l’organisation. Lister nécessite findings:read, et ignorer un rejet nécessite 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/overrides

Consultez la référence de l’API REST publique pour la spécification OpenAPI complète.

Demander un contournement

Un développeur bloqué n’a pas besoin de trouver un administrateur pour ajouter une dérogation. Le rejet affiche un lien vers un formulaire de demande pour exactement ce commit. Toute personne de l’organisation disposant d’un accès en écriture au dépôt peut l’ouvrir et indiquer un motif. Le lien ne contient aucun jeton ; vous devez donc vous connecter pour l’utiliser.

  • Qui décide. Les propriétaires de l’organisation et les membres que votre organisation a désignés approbateurs d’exceptions. La personne qui a fait la demande ne peut jamais approuver sa propre demande. Les décideurs reçoivent un e-mail lorsqu’une demande arrive, et l’événement webhook security.exception.requested est envoyé.
  • Ce que fait une approbation. Elle crée une dérogation de protection des push ordinaire, liée à ce dépôt et à ce commit, valable 7 jours. Poussez à nouveau le même commit pendant ce délai et il passe. Un autre commit contenant le même secret reste bloqué et nécessite sa propre demande.
  • Durée d’ouverture d’une demande. Si personne ne décide dans les 7 jours, la demande expire.
  • Qui peut approuver, et comment. L’approbation nécessite une personne connectée avec l’authentification à deux facteurs (ou via votre SSO), sur la page de la demande ou avec un jeton OAuth Forgejo sur l’API. Un jeton d’API d’organisation ne peut pas trancher une demande.
  • Aucune exemption. Le push bloqué d’un bot suit le même parcours. Un membre disposant d’un accès en écriture fait la demande en son nom.
  • Limite anti-abus. Une personne peut déposer au plus 10 demandes par jour dans une même organisation.
  • Ce qui est affiché. Le dépôt, le commit et les identifiants de règle, jamais le secret. Les demandes, les décisions et la dérogation sont enregistrées dans votre journal d’audit (push-protection.bypass.requested, .approved, .rejected, .cancelled). Le demandeur est informé du résultat par e-mail et par webhook (security.exception.approved ou security.exception.rejected).

Les propriétaires voient les demandes ouvertes et récentes sur la page Protection des push, sous Demandes de contournement. Via l’API :

  • Lister GET /_app/api/v1/orgs/:slug/push-protection/bypass-requests (?status=requested pour les demandes ouvertes)
  • Demander POST /_app/api/v1/orgs/:slug/push-protection/bypass-requests corps : { "repository": "app", "commit_sha": "<full sha>", "reason": "..." }
  • Décider POST .../bypass-requests/:id/approve ou .../reject, avec en option { "comment": "..." }
  • Retirer POST .../bypass-requests/:id/cancel (le demandeur uniquement)

Contournement pour des identifiants déjà divulgués

Un blocage par la protection des push ne signifie pas que l’identifiant est en sécurité. Au moment où vous êtes bloqué, vous avez un identifiant dans un commit sur votre ordinateur. Faites la rotation de l’identifiant avant toute autre chose. Une fois la rotation faite, vous pouvez retirer le commit de la branche par un rebase ou pousser avec une dérogation ; les deux voies conviennent une fois l’identifiant révoqué.

Push et règle du committer

Chaque push est également enregistré avec le compte qui l’a authentifié (jeton HTTPS, jeton OAuth, clé SSH, clé de déploiement ou job Actions), et votre organisation peut exiger que les nouveaux commits utilisent l’une des adresses e-mail vérifiées de l’auteur du push : désactivée, avertir ou bloquer. Voir Push et règle du committer.

Ce qu’elle ne fait pas

  • Elle n’analyse pas l’historique a posteriori. Un identifiant committé avant l’activation de la protection des push reste committé. Utilisez un outil de réécriture de l’historique (le guide de migration explique la démarche).
  • Elle ne détecte pas toutes les formes possibles d’identifiants. Des règles personnalisées par organisation comblent les lacunes qui comptent pour votre stack.
  • Elle ne s’exécute pas lors d’un git push --force d’une branche déjà connue de fremforge (le hook pre-receive s’exécute toujours, mais la comparaison porte sur la nouvelle pointe ; un push forcé qui remplace un commit divulgué par un commit propre est autorisé).