Sécurité et chaîne d’approvisionnement
fremforge fournit une pile de sécurité de la chaîne d’approvisionnement en couches, incluse dans chaque formule par utilisateur. Les contrôles s’exécutent à quatre points de passage : le push (analyse des secrets), la PR (analyse des dépendances, SAST), l’artefact (analyse des conteneurs, SBOM, provenance SLSA) et le commit (signature par clé SSH ou GPG).
Pour la posture contractuelle (BSI C5, ISO 27001/27017/27018, TISAX, RGPD, l’analyse Schrems II / CLOUD Act), voir frem.sh/trust.
Où les résultats sont listés
Sécurité › Résultats s’ouvre sur Tous : chaque résultat ouvert de chaque analyseur dans une seule liste, les plus importants en premier (exploités connus, puis gravité, puis les plus anciens). Filtrez-la par type et par gravité ; chaque puce indique combien de résultats elle listerait. Une ligne renvoie au résultat dans son propre onglet (SAST, Secrets, Dépendances, Images de conteneurs, Licences, Logiciels malveillants), où se trouvent ses détails et ses actions. La même liste est disponible dans l’API sous GET /orgs/{org}/security/findings ; voir API des résultats.
Analyse des secrets (protection des push)
Gitleaks s’exécute comme hook de pré-réception sur chaque push, avant que le commit ne soit accepté dans le dépôt, dès le tout premier push d’un nouveau dépôt. La protection des push est toujours active : aucune organisation ni aucun dépôt ne peut désactiver l’analyse. Le seul paramètre est le mode de défaillance de l’analyseur sous Sécurité › Politiques › Protection des push : lorsque l’analyseur lui-même est indisponible, un push est soit rejeté (par défaut), soit laissé passer et enregistré comme événement d’autorisation sur erreur dans la piste d’audit ; les secrets qui passent pendant cette fenêtre sont détectés plus tard par le balayage hebdomadaire de l’historique. Voir Protection des push.
Ce qui est bloqué
Gitleaks détecte plus de 120 motifs à haut niveau de confiance, notamment :
| Catégorie | Exemples de motifs |
|---|---|
| Identifiants de fournisseurs cloud | Clés d’accès/secrètes AWS, JSON de compte de service GCP, chaînes de connexion de stockage Azure |
| Jetons de gestion de code source | PAT GitHub, PAT GitLab, mots de passe d’application Bitbucket |
| Clés de paiement | Clés secrètes Stripe, secrets de signature de webhook Stripe |
| Jetons de communication | Jetons de bot Slack, URL de webhook Slack, jetons d’authentification Twilio |
| Clés privées | Clés privées RSA/EC/Ed25519, certificats encodés en PEM |
| Secrets génériques | Chaînes à forte entropie correspondant au motif generic-api-key, secrets de signature JWT |
| URL de bases de données | Chaînes de connexion PostgreSQL, MySQL, MongoDB, Redis contenant des identifiants |
Quand un push est bloqué
Le push est rejeté par le serveur distant avec un message d’erreur qui indique le type de motif détecté ainsi que le fichier et le numéro de ligne. Le commit n’est pas accepté.
Ne vous contentez pas de supprimer le fichier et de commiter à nouveau. Le secret se trouve dans l’historique git et sera de nouveau bloqué. La bonne remédiation :
- Faites immédiatement la rotation du secret auprès du service émetteur (AWS IAM, tableau de bord Stripe, etc.). Considérez-le comme compromis.
- Réécrivez l’historique pour retirer le secret de chaque commit concerné :
# Install git-filter-repo (https://github.com/newren/git-filter-repo)
pip install git-filter-repo
# Create a replacements file
echo '<secret-value>==>REDACTED' > replacements.txt
# Rewrite history
git filter-repo --replace-text replacements.txt
# Force-push the cleaned branch
git push --force-with-lease origin <branch>- Prévenez toute personne susceptible d’avoir cloné le dépôt avant la réécriture.
Procédure de dérogation
Si un secret bloqué est intentionnel (jeton d’outillage interne, identifiant de test publié), un propriétaire de l’organisation peut ajouter une dérogation ciblée :
- Allez dans Sécurité › Politiques › Protection des push › Dérogations actives → Nouvelle dérogation.
- Indiquez : le dépôt (ou sélectionnez « All repos »), le motif de secret (d’après l’identifiant de règle Gitleaks) et la justification (texte libre, consignée dans la piste d’audit).
- Enregistrez. La dérogation prend effet au push suivant.
Toutes les dérogations sont visibles par les autres propriétaires de l’organisation et figurent dans le journal d’audit avec l’auteur, l’horodatage et la justification. Les dérogations peuvent être révoquées à tout moment depuis la même page.
Déroger à un rejet de push
Lorsqu’un push est rejeté par le contrôle d’analyse des secrets, le développeur voit dans la sortie du terminal le motif du rejet, le type de motif détecté, le chemin du fichier et le numéro de ligne.
Un propriétaire de l’organisation peut approuver une dérogation sous Sécurité › Politiques › Protection des push › Rejets récents. Chaque rejet indique : le dépôt, l’auteur du commit, l’horodatage, le type de secret détecté et le chemin du fichier concerné.
Portées de dérogation (à choisir lors de l’approbation) :
- Ce commit uniquement : approuve le SHA de commit précis. Le même secret dans un commit ultérieur sera de nouveau rejeté.
- Ce chemin de fichier : approuve le secret à ce chemin dans tout commit ultérieur. À utiliser pour les fixtures de test ou des valeurs fictives volontairement non sensibles.
- Ce dépôt : approuve toute occurrence de ce type de secret dans le dépôt. À utiliser avec parcimonie.
- Toute l’organisation : approuve le type de secret dans tous les dépôts. Uniquement pour des faux positifs déclarés (par exemple un format de jeton personnalisé qui correspond au motif d’une règle mais n’est pas un vrai secret).
Une raison de dérogation est obligatoire (texte libre). Elle est écrite dans le journal d’audit.
Le développeur doit pousser à nouveau une fois la dérogation accordée. La dérogation n’accepte pas rétroactivement le push rejeté.
Liste d’autorisation personnalisée
Pour faire taire des faux positifs connus dans un dépôt donné (fixtures de test, clés d’exemple dans la documentation, identifiants publics intentionnels), ajoutez un .gitleaks.toml à la racine du dépôt :
[extend]
useDefault = true
[[allowlists]]
description = "Test fixture AWS keys"
paths = ["tests/fixtures/.*", "docs/examples/.*"]
[[allowlists]]
description = "Example key in README"
regexes = ["AKIAIOSFODNN7EXAMPLE"]La liste d’autorisation est évaluée par dépôt pendant le hook de pré-réception. Elle ne prévaut pas sur la protection des push au niveau de l’organisation pour les motifs absents de la liste.
Vérifications des pull requests : seuls les nouveaux résultats bloquent
Les vérifications des dépendances (fremforge/dep-scan), SAST (fremforge/sast), des licences (fremforge/license-scan), des secrets (fremforge/secret-scan) et des images (fremforge/image-scan) analysent l’ensemble du dépôt, mais chacune ne fait échouer une pull request que pour les résultats que cette pull request introduit. Un résultat déjà présent sur la branche cible ne bloque pas : une pull request qui ne change rien de vulnérable peut donc être fusionnée dans un dépôt qui a des résultats ouverts. Le statut indique alors, par exemple, No new findings at or above critical; 8 already on main.
fremforge compare les résultats de la pull request avec la dernière analyse de cette branche par le même analyseur. Un résultat est le même lorsque :
| Vérification | Même résultat lorsque |
|---|---|
| Dépendances | l’écosystème, le paquet et l’avis correspondent. Passer un paquet à une version qui a toujours le même avis n’est pas nouveau ; un nouvel avis l’est |
| SAST | la règle, le fichier et le code détecté correspondent. Un résultat qui a seulement changé de ligne n’est pas nouveau |
| Licences | l’écosystème, le paquet et la licence correspondent |
| Secrets | la règle et les lignes détectées correspondent, comparées au moyen d’un hachage à clé calculé par l’analyseur. Le secret lui-même n’est jamais stocké ni transmis. Un secret qui a seulement changé de ligne n’est pas nouveau |
| Images | l’avis, le paquet et le fichier correspondent |
- Les copies comptent : une seconde occurrence d’un résultat que la branche possède une fois est nouvelle.
- Tout ce qui décide déjà si un résultat bloque s’applique d’abord : la gravité de blocage, les rejets, les exceptions et la politique de licences. Un nouveau paquet malveillant ou une nouvelle vulnérabilité exploitée connue bloque toujours, que votre organisation ait choisi ou non que les résultats non atteignables ne bloquent pas.
- Les résultats existants ne sont pas masqués. Ils sont listés dans le commentaire de la pull request et sous Sécurité › Résultats, comptent pour votre SLA et déclenchent des alertes comme avant.
La branche est analysée après un push sur votre branche par défaut, au plus une fois par heure, le push le plus récent l’emportant. Un résultat arrivé sur la branche depuis sa dernière analyse compte donc encore comme celui de la pull request jusqu’à l’analyse suivante ; un nouveau résultat n’est jamais pris pour un résultat existant. Tant qu’il n’existe aucune analyse de la branche, chaque résultat bloque, comme avant, et l’analyse de la pull request en demande une. Dès que la première analyse de la branche est enregistrée, fremforge analyse de nouveau, de lui-même, les pull requests ouvertes vers cette branche dont la vérification a échoué entre-temps (au plus 20 par dépôt, les plus récemment mises à jour, par vérification), afin qu’elles passent au vert, ou ne restent rouges que pour ce qu’elles ajoutent, sans nouvelle exécution ni nouveau push. Une pull request vers une branche autre que votre branche par défaut compte toujours chaque résultat.
Analyse des dépendances
osv-scanner vérifie chaque pull request, ainsi que la branche par défaut après chaque push, par rapport à la base d’avis OSV. Il lit les fichiers de verrouillage et les manifestes hors ligne, sans résoudre les dépendances, et publie le statut de commit fremforge/dep-scan ainsi qu’un commentaire sur la pull request. Les résultats sont listés sous Sécurité › Résultats › Dépendances.
Paramètres et ce qui bloque une fusion
Sous Sécurité › Résultats › Dépendances, un propriétaire peut désactiver l’analyse des dépendances pour l’organisation et définir la gravité de blocage : la gravité la plus basse qui fait échouer fremforge/dep-scan. La valeur par défaut est critical. Sur une pull request, seuls les résultats que la pull request introduit bloquent. Le fait qu’une vérification en échec bloque réellement la fusion dépend du mode de la vérification (enforce, audit ou off) sous Sécurité › Prioriser › Vérifications des pull requests, et de l’obligation de cette vérification dans la protection de branche.
Pour qu’un résultat ne bloque pas, corrigez-le ou enregistrez une exception. Une organisation peut aussi choisir que les résultats non atteignables ne bloquent pas. Ce choix ne couvre jamais les paquets malveillants ni les vulnérabilités exploitées connues.
Pull requests de correctif
Deux mécanismes ouvrent des pull requests de correctif pour les dépendances vulnérables :
- Correctifs de sécurité : Ouvrir une demande de correctif sur un résultat (ou la politique automatique) ouvre une pull request par paquet, à la version la plus basse qui corrige tous les avis ouverts. Voir Correctifs de sécurité.
- Mises à jour des dépendances : les alertes de vulnérabilité propres à Renovate, pour les organisations inscrites aux Mises à jour des dépendances. Les dépôts de chaque organisation inscrite sont traités chaque nuit, et Exécuter maintenant lance une exécution hors cycle.
Manifestes pris en charge
Les fichiers de verrouillage et manifestes lus, ainsi que les écosystèmes qui bénéficient de pull requests de correctif et de l’atteignabilité, sont listés à un seul endroit : Écosystèmes pris en charge.
Analyse des images de conteneurs
Trivy analyse chaque image OCI poussée vers le registre de paquets fremforge. Les résultats apparaissent sous Sécurité › Résultats › Images de conteneurs.
Trivy s’exécute de manière centralisée, sans rien à installer : trivy fs sur chaque pull request (le statut fremforge/image-scan, qui ne bloque que sur les résultats introduits par la pull request), trivy config comme vérification distincte (fremforge/iac-scan), et trivy image sur chaque image de votre registre et chaque image tirée via le proxy de dépendances, avec de nouvelles analyses régulières. Les résultats de mauvaise configuration de trivy config ont leur propre onglet, Sécurité › Résultats › IaC ; voir Analyse IaC.
Chaque résultat comprend :
| Champ | Description |
|---|---|
| ID CVE | par ex. CVE-2024-12345, avec un lien vers la NVD |
| Gravité | CRITICAL / HIGH / MEDIUM / LOW / UNKNOWN |
| Paquet concerné | Nom du paquet et version installée |
| Version corrective | Première version qui corrige la CVE, si elle existe |
| Score EPSS | Probabilité selon l’Exploit Prediction Scoring System |
Analyse dans votre propre workflow (facultatif)
Il n’y a rien à activer : les analyses ci-dessus s’exécutent sans fichier de workflow. Si vous voulez aussi que votre propre build échoue avant de pousser une image, vous pouvez exécuter Trivy vous-même dans un workflow, par exemple :
name: Image scan
on:
push:
branches: [main]
pull_request:
jobs:
trivy:
runs-on: fremforge
steps:
- uses: actions/checkout@<full-40-char-sha> # pin by SHA, never a tag or branch
- name: Build image
run: docker build -t ${{ github.repository }}:${{ github.sha }} .
- name: Scan image
run: |
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy:0.74.0@sha256:<digest> \
image --exit-code 1 --severity HIGH,CRITICAL \
${{ github.repository }}:${{ github.sha }}Réglez --severity sur le seuil auquel vous voulez que le build échoue.
Pourquoi cet exemple épingle par SHA et par digest
Une version antérieure de cette page utilisait aquasecurity/trivy-action@master et
actions/checkout@v4. Ce sont deux références mutables, et le 19 mars 2026 cette
distinction a cessé d’être théorique : un attaquant a réécrit le tag v0.69.4 de
trivy-action pour le faire pointer vers une version malveillante. Les pipelines qui
référençaient l’action par tag ou par branche ont tiré le code de l’attaquant à leur
exécution suivante, et les identifiants ainsi récoltés ont servi à publier sur PyPI deux
versions piégées d’un autre projet.
Un tag est un pointeur que quelqu’un d’autre peut déplacer. Un SHA de commit et un digest d’image ne le sont pas.
- Actions : référencez le SHA de commit complet de 40 caractères, pas
@v4et jamais@master. Renovate propose toujours les mises à niveau ; il réécrit le SHA et son commentaire ensemble. - Analyseurs : préférez l’image de conteneur de l’outil épinglée par digest à une action d’encapsulation. Moins de pièces mobiles, et ce que vous avez épinglé est ce qui s’exécute.
- Mettez en miroir ce dont vous dépendez : fremforge n’atteint aucun registre amont depuis un chemin de production ; chaque image provient de notre propre registre, épinglée par digest.
Cet incident n’a exigé aucune action du côté de fremforge : nous n’avons jamais utilisé
trivy-action, et notre propre Trivy est une image épinglée par digest. Le projet compromis
installait Trivy depuis apt sans version épinglée. L’épinglage n’a rien de prestigieux,
et c’est le contrôle qui l’aurait arrêté.
Génération de SBOM
Syft génère des SBOM CycloneDX 1.6 pour chaque image de conteneur poussée vers le registre et pour chaque tag de release, avec un jumeau SPDX 2.3 issu de la même exécution (au mieux ; voir SBOM). Les envois sont acceptés en CycloneDX 1.5–1.7 ou SPDX 2.x/3.x. La génération de SBOM est activée par défaut pour chaque organisation ; il n’y a aucune étape d’activation.
Téléchargement
Depuis l’interface du registre de paquets : ouvrez l’image → onglet Attestations → Download SBOM.
Via l’API :
curl -H "Authorization: token <your-pat>" \
https://frem.sh/_app/api/v1/orgs/<org>/findings/attestations \
-o sbom.jsonLe SBOM est au format JSON CycloneDX et inclut toutes les dépendances transitives avec leurs identifiants PURL, leurs expressions de licence et leurs plages de versions.
SAST
OpenGrep exécute l’analyse statique de manière centralisée sur chaque pull request et sur la branche par défaut. Elle est activée par défaut pour chaque organisation. Les résultats publient un statut fremforge/sast et un commentaire sur la pull request, et sont listés sous Sécurité › Résultats › SAST. Les langages, règles et paramètres sont décrits sur la page SAST. Les règles personnalisées ne sont pas prises en charge.
Commits signés
fremforge prend en charge deux voies de signature des commits, toutes deux vérifiées nativement par le badge « Verified » de Forgejo dans l’interface web :
- Signature par clé SSH (valeur par défaut recommandée) : réutilise la clé SSH que vous avez déjà enregistrée pour
git push. Aucun nouveau matériel de clé à gérer, aucun sous-traitant tiers, aucune dépendance à un journal de transparence, aucun transit par une juridiction américaine. C’est la voie canonique de fremforge. (Pour les nouvelles organisations créées après le 2026-05-22, le transport SSH est désactivé par défaut ; la signature par clé SSH fonctionne quand même, que vous utilisiez SSH ou HTTPS pourgit push.) - Signature GPG : clé OpenPGP classique. Utilisez-la si votre organisation dispose déjà d’une politique de gestion des clés GPG (carte à puce, HSM, serveur de clés central).
La signature des commits sans clé EST disponible chez fremforge via une pile Sigstore auto-hébergée : une CA Fulcio à sign.frem.sh et une TSA à tsa.frem.sh, toutes deux exécutées sur T Cloud Public en eu-de, avec la CA racine dans DEW KMS. L’instance Sigstore publique de la Linux Foundation n’est PAS utilisée : fremforge ne fait transiter aucun trafic de signature ou de vérification par sigstore.dev / rekor.sigstore.dev, si bien que l’engagement « aucun sous-traitant américain » tient. Il n’y a délibérément pas de Rekor / journal de transparence pour la signature des commits ; l’horodatage ancré par la TSA est la primitive d’intégrité à la place. (fremforge exploite bien un journal de transparence, à log.frem.sh, mais il ne couvre que les attestations de provenance de build et n’est pas Rekor ; voir journal de transparence. Les deux sont indépendants : un commit signé n’y est pas inscrit.) Voir signature des commits sans clé pour la configuration de gitsign côté client. (Remarque : la provenance de build/release est une voie distincte ; la provenance SLSA de fremforge est signée avec une clé Ed25519 détenue par la plateforme, PAS via Fulcio. Voir la section SLSA ci-dessous.)
Signature par clé SSH : configuration
# Tell Git to use SSH for signing (Git ≥ 2.34)
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true
git config --global tag.gpgsign true
# (Optional) point Git at your local allowedSignersFile for verify
git config --global gpg.ssh.allowedSignersFile ~/.ssh/allowed_signersAjoutez la clé SSH correspondante à votre profil utilisateur fremforge sous Paramètres utilisateur → Clés SSH / GPG → Ajouter une clé (Forgejo accepte la même clé pour le push et la signature, ou une clé distincte réservée à la signature).
Au prochain git commit, Git signe avec votre clé SSH. Forgejo vérifie la signature par rapport au allowedSignersFile de l’organisation (dérivé automatiquement des clés de signature enregistrées par les membres) et affiche le badge « Verified » dans l’interface web.
Vérifier
git log --show-signature HEAD~5..HEADChaque commit affiche une ligne Good "git" signature for <user>@<org>. L’interface web de Forgejo affiche un badge vert « Verified » à côté du hachage du commit.
Signature GPG : configuration
Si vous préférez GPG, la procédure standard git config --global commit.gpgsign true + user.signingkey <gpg-key-id> fonctionne telle quelle ; téléversez votre clé publique sous Paramètres utilisateur → Clés SSH / GPG → Ajouter une clé. Forgejo vérifie et affiche le badge « Verified ».
Exiger des commits signés (politique de l’organisation)
Imposez des commits signés sur les branches protégées avec Signature des commits sous Sécurité › Politiques › Règles des dépôts (voir Protection des branches), ou par dépôt sous Dépôt → Paramètres → Branches → <branch> → Exiger des révisions (commits) signées. Les commits non signés sont rejetés au moment du push. Les signatures par clé SSH comme GPG satisfont l’exigence.
Provenance SLSA
La provenance de build/release est générée côté serveur par fremforge : aucun fichier de workflow dans vos dépôts et aucune minute de runner consommée. À chaque événement Forgejo release.published et package, l’api de fremforge calcule le hachage de chaque ressource publiée, construit une déclaration (Statement) in-toto enveloppée dans une enveloppe DSSE, la signe avec la clé de builder de la plateforme, conserve l’attestation dans un stockage résidant dans l’UE propre à votre tenant, et téléverse l’enveloppe à côté de la ressource sous le nom <asset>.intoto.jsonl. Elle est aussi listée dans la console sous Sécurité › Composants › Attestations de build.
Le modèle de workflow autonome
slsa-provenance.yamlque les clients étaient auparavant censés appeler depuis leurs pipelines de build a été retiré le 2026-06-16 : un audit de la flotte n’a trouvé aucun appelant, et la provenance est désormais déclenchée automatiquement côté serveur, sans aucun câblage côté client.
Souverain européen par conception. Les clés de signature, la racine de confiance, l’autorité de certification et le stockage se trouvent tous dans le compte T Cloud Public de fremforge (eu-de). Il n’y a aucun Sigstore public, aucun Rekor et aucune consultation de tuf-repo-cdn.sigstore.dev nulle part dans le chemin de vérification.
Deux signatures, toutes deux sur chaque attestation. La principale est une clé Ed25519 détenue par la plateforme, et c’est celle que vérifie la procédure Vérifier ci-dessous : simples curl / jq / openssl / python3, rien d’autre n’est nécessaire. En outre, chaque attestation est signée sans clé auprès du Sigstore auto-hébergé de fremforge : un certificat Fulcio de courte durée dont le SAN est l’identité de la source du build, horodaté par la TSA RFC3161 de fremforge. Les deux sont décrites dans la racine de confiance, qui contient fulcio_root_ca_pem, tsa_cert_chain_pem, certificate_oidc_issuer et certificate_identity_pattern, de sorte que la signature sans clé est elle aussi vérifiable hors ligne.
Il n’y a toujours aucun journal de transparence — la racine de confiance indique "transparency_log": "none" —, la signature sans clé est donc validée par rapport à l’horodatage RFC3161 plutôt que par Rekor (cosign --use-signed-timestamps --insecure-ignore-tlog). Ce que cela vous apporte et ne vous apporte pas est exposé dans §Vérifiabilité indépendante.
(La signature des commits de fremforge est une voie distincte sur la même CA auto-hébergée ; voir signature des commits sans clé.)
Racine de confiance : https://www.frem.sh/.well-known/slsa-trust-root.json, un petit fichier JSON qui liste les clés publiques du builder. Sa forme est propre à fremforge :
{
"trusted_keys": [
{
"kid": "fremforge-slsa-builder-v1",
"alg": "ed25519",
"pem": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----",
"valid_from": "2026-05-17T00:00:00Z",
"valid_to": "2026-09-12T00:00:00Z",
"builder_id": "https://frem.sh/runner-controller/v1"
},
{
"kid": "fremforge-slsa-builder-v2",
"alg": "ed25519",
"pem": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----",
"valid_from": "2026-09-12T00:00:00Z",
"builder_id": "https://frem.sh/runner-controller/v1"
}
]
}fremforge-slsa-builder-v2 est la clé de signature active depuis la rotation du 2026-09-12. fremforge-slsa-builder-v1 a signé tout ce qui précède et n’est conservée que pour la vérification : son valid_to est l’instant où elle a cessé de signer, pas une date d’expiration des signatures qu’elle a produites. Vérifiez toujours avec la clé désignée par le keyid de l’enveloppe que vous détenez, jamais avec un nom fixe.
Épinglez le fichier (ou une copie) dans votre CI pour figer la confiance pendant la fenêtre de vérification. Les rotations de clés sont annoncées 30 jours à l’avance conformément à la règle de notification des sous-traitants de la page trust ; une signature ne cesse d’être vérifiable que si sa clé est retirée de trusted_keys.
Forme de l’enveloppe et de la déclaration (enveloppe DSSE contenant une Statement in-toto) :
| Champ | Valeur |
|---|---|
payloadType de l’enveloppe | application/vnd.in-toto+json |
signatures[0].keyid de l’enveloppe | fremforge-slsa-builder-v2 pour tout ce qui est signé depuis le 2026-09-12, fremforge-slsa-builder-v1 avant (correspond à un trusted_keys[].kid de la racine de confiance) |
_type de la Statement | https://in-toto.io/Statement/v1 |
predicateType de la Statement | https://slsa.dev/provenance/v1 |
predicate.buildDefinition.buildType | La voie qui a produit la déclaration ; voir §Ce que signifie chaque buildType. L’une de …/buildtypes/forgejo-action-run/v1, …/container-image/v1, …/forgejo-release/v1, …/forgejo-package/v1 |
predicate.buildDefinition.internalParameters.run_id | L’exécution Actions qui a produit l’artefact. Présent sur la provenance de build ; absent sur une déclaration de publication |
predicate.buildDefinition.internalParameters.attestation_kind | publication lorsque la déclaration observe des octets publiés plutôt qu’un build. Absent sur la provenance de build |
predicate.buildDefinition.externalParameters.repository | <org>/<repo> |
predicate.buildDefinition.externalParameters.ref | La ref git (par ex. refs/tags/v1.2.3) |
predicate.runDetails.builder.id | https://frem.sh/runner-controller/v1 (contrôlé par le serveur et imposé au moment de la signature : un runner compromis ne peut pas le falsifier) |
subject[].name / subject[].digest.sha256 | Le nom de fichier de la ressource et le SHA-256 que fremforge a calculé pour elle |
Ce que c’est, précisément. Jusqu’au 2026-09-11, cette section revendiquait SLSA Build L2. Nous l’avons réexaminée au regard de la spécification v1.0, la revendication ne tenait pas, et nous l’avons retirée. L’écart qui la rendait fausse — une provenance qui ne décrivait aucun build — a été comblé le 2026-09-12. Nous n’annonçons toujours pas de niveau, pour une raison donnée à la fin de cette section.
Deux statements différentes sont désormais signées avec la même clé, et vous pouvez les distinguer à partir de l’enveloppe seule.
Ce que signifie chaque buildType
buildType se termine par | Ce qu’il affirme | Nomme une exécution de build ? |
|---|---|---|
forgejo-action-run/v1 | Le plan de contrôle de fremforge a lu les artefacts téléversés de cette exécution dans les propres enregistrements de la plateforme de build, a récupéré les octets et en a calculé le hachage | Oui — run_id |
container-image/v1 | Une image poussée par une exécution, attestée avec l’identité d’exécution tirée d’un jeton OIDC de courte durée que fremforge a émis pour ce job | Oui — run_id, lié cryptographiquement |
forgejo-release/v1 | Une ressource de release publiée. Hérite du commit d’un build lorsque le digest identique a déjà été attesté comme construit ; voir ci-dessous | Uniquement en cas de jointure |
forgejo-package/v1 | Une version de paquet apparue dans le registre | Non |
La provenance de build (les deux premiers) décrit comment l’artefact a été produit : elle nomme l’exécution, le workflow et le commit, et l’identité d’exécution provient de la plateforme plutôt que de quoi que ce soit que le build a affirmé sur lui-même.
L’attestation de publication (les deux derniers, sans jointure) dit quelque chose de plus restreint et le dit
explicitement : attestation_kind: publication. Elle signifie fremforge a observé ce contenu, à l’octet près,
publié ici, et signe cette observation avec une clé que le build ne peut pas atteindre. C’est une
propriété réelle et vérifiable. Ce n’est pas une affirmation sur la manière dont l’artefact a été construit, et
elle ne prétend plus l’être : lorsqu’aucun commit source n’est connu, commit_sha vaut littéralement
no-source-commit plutôt qu’une valeur ayant l’apparence d’un commit git. Si vous y voyez 40 caractères
hexadécimaux, c’est un vrai commit.
La jointure par digest. Une ressource de release dont le sha256 a déjà une attestation de build hérite
du commit de ce build, et la déclaration y renvoie (source_commit_from: digest-join,
plus l’identifiant de l’attestation de build et sa clé d’objet). Le lien est le digest de l’artefact : « les mêmes
octets » est donc un fait cryptographique plutôt qu’une convention de nommage, et publier un artefact
plus tard ne change pas ce qui l’a produit. Un target_commitish explicite sur la release l’emporte toujours
sur l’inférence.
Les propriétés de plateforme derrière tout cela :
- Les exécutions ne peuvent pas s’influencer les unes les autres. Une VM ECS éphémère par job, détruite à la fin du job, avec une ENI par VM et un identifiant par runner qui disparaît avec le job ; voir §Modèle d’isolation du noyau.
- La clé de signature est hors de portée du build. Elle réside dans l’api, jamais sur un runner, et l’identité du builder est imposée côté serveur au moment de la signature, si bien qu’un build compromis ne peut pas la falsifier.
- Aucun câblage côté client pour les voies déclenchées par webhook. Rien à appeler depuis un workflow, et aucune minute de runner consommée.
Pourquoi nous n’annonçons toujours pas de niveau SLSA. Ces deux propriétés d’isolation correspondent à ce que SLSA v1.0 Build L3 exige, et la provenance de build décrit désormais un build réel. Nous préférons que vous lisiez le tableau ci-dessus et décidiez de ce qu’il vaut plutôt que d’accepter un chiffre fourni par la partie auditée. (Les builds hermétiques, les artefacts reproductibles et les journaux de transparence sont parfois présentés comme des exigences L3. Ils ne le sont pas : c’étaient des concepts de SLSA v0.1 L4, absents de tout niveau en v1.0, qui les range parmi les orientations futures.)
Ce qui manque encore, c’est un journal de transparence, qui est un axe distinct de tout niveau ; voir §Vérifiabilité indépendante.
Vérifier
N’utilisez pas
slsa-verifier verify-artifact --trusted-rootde l’amont. Cet outil n’a pas d’option--trusted-rootsurverify-artifactet ne peut consommer ni le JSON de racine de confiance personnalisé de fremforge ni une enveloppe DSSE Ed25519 brute : il attend un bundle de style Sigstore. La provenance de fremforge est une simple enveloppe DSSE signée en Ed25519 ; vous la vérifiez donc directement aveccurl/jq/openssl/python3, tous présents par défaut dans les distributions, sans aucun outillage fremforge.
La vérification comporte trois contrôles indépendants : la signature DSSE est valide pour la clé de builder publiée, le SHA-256 de la ressource correspond à ce qui a été signé, et la déclaration nomme le bon dépôt et le bon builder.
1. Récupérer la racine de confiance et en extraire la clé publique du builder
curl -sSfL https://www.frem.sh/.well-known/slsa-trust-root.json -o trust-root.json
jq -r '.trusted_keys[] | select(.kid=="fremforge-slsa-builder-v2") | .pem' \
trust-root.json > builder.pubPour une attestation signée avant le 2026-09-12, sélectionnez plutôt fremforge-slsa-builder-v1. jq -r '.signatures[0].keyid' <asset>.intoto.jsonl vous indique laquelle.
2. Vérifier la signature DSSE, le digest de la ressource et les champs de la déclaration
Exécutez depuis un répertoire contenant la ressource téléchargée et son enveloppe <asset>.intoto.jsonl. Définissez ASSET sur le nom de fichier de la ressource :
ASSET=myapp python3 - <<'PY'
import base64, json, hashlib, os, subprocess, sys, pathlib
asset = os.environ["ASSET"]
env = json.loads(pathlib.Path(f"{asset}.intoto.jsonl").read_text().strip())
payload_type = env["payloadType"] # application/vnd.in-toto+json
payload = base64.b64decode(env["payload"])
# Reconstruct the DSSE pre-authentication encoding (DSSEv1):
# "DSSEv1 " + LEN(type) + " " + type + " " + LEN(payload) + " " + payload
# where LEN is the utf-8 byte length and separators are single ASCII spaces.
pae = (
b"DSSEv1 "
+ str(len(payload_type.encode())).encode() + b" "
+ payload_type.encode() + b" "
+ str(len(payload)).encode() + b" "
+ payload
)
pathlib.Path("pae.bin").write_bytes(pae)
pathlib.Path("sig.bin").write_bytes(base64.b64decode(env["signatures"][0]["sig"]))
# Ed25519 ("pure" EdDSA — no pre-hash) verify with openssl. Feed the raw PAE.
r = subprocess.run(
["openssl", "pkeyutl", "-verify", "-pubin", "-inkey", "builder.pub",
"-rawin", "-in", "pae.bin", "-sigfile", "sig.bin"],
capture_output=True, text=True,
)
if r.returncode != 0 or "Signature Verified Successfully" not in r.stdout:
print("DSSE signature FAILED:", r.stdout, r.stderr, file=sys.stderr); sys.exit(1)
print("DSSE signature: OK")
stmt = json.loads(payload)
# The signed subject digest must match the asset bytes on disk.
want = next((s["digest"]["sha256"] for s in stmt["subject"] if s["name"] == asset), None)
got = hashlib.sha256(pathlib.Path(asset).read_bytes()).hexdigest()
if want is None:
print(f"asset {asset} not found among signed subjects", file=sys.stderr); sys.exit(1)
if want != got:
print(f"digest mismatch: signed={want} actual={got}", file=sys.stderr); sys.exit(1)
print(f"asset digest: OK ({got})")
# Sanity-check the builder identity and predicate type.
builder = stmt["predicate"]["runDetails"]["builder"]["id"]
if builder != "https://frem.sh/runner-controller/v1":
print(f"unexpected builder.id: {builder}", file=sys.stderr); sys.exit(1)
if stmt["predicateType"] != "https://slsa.dev/provenance/v1":
print(f"unexpected predicateType: {stmt['predicateType']}", file=sys.stderr); sys.exit(1)
print("builder.id + predicateType: OK")
PYSi les trois contrôles affichent OK, la ressource correspond exactement aux octets que fremforge a attestés, l’enveloppe a été signée par la clé de builder publiée, et la déclaration nomme le builder fremforge. La vérification échoue (code de sortie non nul) si la signature ne se valide pas avec builder.pub (enveloppe altérée ou clé erronée/remplacée), si le SHA-256 de la ressource ne correspond pas au sujet signé (ressource altérée), ou si builder.id / predicateType ne correspondent pas.
Le
keyidde l’enveloppe (fremforge-slsa-builder-v2pour tout ce qui est signé depuis le 2026-09-12) doit correspondre aukidque vous avez sélectionné dans la racine de confiance ; une enveloppe vérifiée avec l’autre clé échoue avecDSSE signature FAILED. Si fremforge fait la rotation de la clé de signature, récupérez à nouveau la racine de confiance et sélectionnez lekidque nomme l’enveloppe.
Pourquoi pas Sigstore au lancement
La pile publique Fulcio + Rekor + Trillian de Sigstore est exploitée par l’OpenSSF (Linux Foundation), une entité de droit américain. L’utiliser enverrait les métadonnées de chaque build vers une infrastructure hébergée aux États-Unis, ce qui est incompatible avec l’engagement de fremforge à n’avoir que des sous-traitants européens dans l’annexe B du DPA. fremforge exploite ses propres Fulcio + TSA auto-hébergés à sign.frem.sh / tsa.frem.sh sur T Cloud Public eu-de, utilisés à la fois pour la signature des commits (voir signature des commits sans clé) et, depuis le 2026-06-21, comme seconde signature sur la provenance de build. Ce qui est délibérément absent, c’est le déploiement public de Sigstore et Rekor.
Le SHA-256 de chaque attestation est également chaîné par hachage dans le journal d’audit de la plateforme, dont les ancres se trouvent dans un OBS Object Lock en mode COMPLIANCE de 3 ans. C’est un contrôle interne utile, et ce n’est pas un substitut à un journal de transparence ; nous ne le décrivons donc plus comme une preuve d’altération équivalente. Deux raisons, toutes deux concrètes : l’ancre ne vous est pas lisible, elle ne peut donc pas faire partie de votre vérification ; et OBS Object Lock protège chaque version d’objet — mesuré le 2026-08-14, une version plus récente peut masquer une clé verrouillée et un marqueur de suppression la cache des lectures ordinaires, deux actions à la disposition de l’opérateur. Voir §Vérifiabilité indépendante pour ce que le modèle de confiance vous apporte et ne vous apporte pas.
Releases immuables
Les propriétaires de l’organisation peuvent rendre chaque release figée à la publication : son tag, son commit
cible et ses ressources ne peuvent plus jamais changer (le titre et les notes restent modifiables), le nom de tag
d’une release supprimée ne peut jamais être réutilisé, et les tags de conteneurs peuvent aussi être figés
(tags du type v1.2.3, ou tous sauf une liste). Chaque release immuable reçoit une statement de release
in-toto signée qui nomme chaque ressource par son SHA-256 et le tag par son commit,
vérifiable avec fremforge release verify. Voir
Releases immuables et attestation de release.
Vérifiabilité indépendante
Déplacer un dépôt, un runner et un stockage d’artefacts en Allemagne répond à la question où se trouvent les données. Cela ne répond pas à lui seul à la question de savoir si l’on peut se fier à la piste d’audit sans faire confiance à l’opérateur qui l’a produite. Ce sont deux propriétés différentes, et nous préférons être explicites sur celle que nous fournissons aujourd’hui.
Ce que vous obtenez sans avoir à nous faire confiance au moment de la vérification. L’attestation accompagne
l’artefact — <asset>.intoto.jsonl, et à côté <asset>.sigstore.json —, vous détenez donc les deux
moitiés. La clé publique du builder, la CA racine Fulcio et la chaîne de la TSA figurent toutes dans une racine de
confiance statique dont nous vous encourageons à épingler une copie dans votre propre CI. La vérification recalcule le
digest de l’artefact et contrôle les signatures localement, avec curl / jq / openssl / python3. Aucune
API fremforge n’est contactée, et aucun outillage fremforge n’est nécessaire. Une fois la racine
épinglée, une modification ultérieure de notre côté ne peut pas changer rétroactivement ce que votre pipeline accepte.
Ce que vous n’obtenez pas, dit clairement. Il existe désormais un
journal de transparence en ajout seul pour les attestations de provenance à log.frem.sh,
mais il ne vous donne pas encore la propriété que l’on entend habituellement par ce terme. Concrètement :
- Le journal nous empêche de réécrire l’historique en silence : si vous détenez une preuve d’inclusion et que nous modifions ou supprimons ensuite votre attestation, votre preuve cesse d’être vérifiable et vous pouvez démontrer la contradiction. C’est réel, et c’est nouveau.
- Il est partiellement résistant aux vues divergentes, depuis le 16 septembre 2026. Un témoin indépendant —
witness.stagemole.eu, exploité par Mullvad VPN AB — cosigne chaque checkpoint : il existe donc désormais un tiers qui remarquerait que nous signons quelque chose d’inhabituel. C’est la propriété dont ce point a toujours traité, et elle n’est plus absente. Un seul témoin ne fait pas un quorum : il augmente le coût de vous montrer un arbre différent de celui de tous les autres, mais ne peut pas détecter une collusion entre le journal et son unique témoin, et son opérateur qualifie l’instance de staging. Notez aussi que les seuls témoins européens de qualité production — Glasklar Teknik, Mullvad VPN et Tillitis — partagent une société mère, Amagicom AB ; en ajouter apporterait de la disponibilité, pas de l’indépendance. - L’horodatage RFC3161 qui remplace partiellement un journal est émis par notre TSA, et la racine de confiance est distribuée depuis notre domaine. L’épinglage règle le second point pour quiconque épingle lors de l’intégration ; il ne règle pas la confiance au premier contact.
- L’ancre de notre chaîne d’audit interne ne vous est pas lisible, elle ne peut donc pas faire partie de votre vérification.
Si l’opérateur est la partie que vous devez tenir pour responsable — ce qui est la bonne question pour la défense et les infrastructures critiques —, alors ce qui comble l’écart est un journal de transparence avec des témoins externes, une autorité d’horodatage indépendante, et des builds reproductibles que vous pouvez reconstruire et comparer sans notre intervention. Le premier de ces trois éléments existe désormais et est décrit ci-dessus, avec ses limites ; l’autorité d’horodatage et les builds reproductibles n’existent pas. Notez qu’un journal auto-hébergé ne comblerait pas l’écart : un journal que nous exploitons est un journal que nous contrôlons. La vérifiabilité indépendante exige une partie extérieure, et le compromis honnête est que passer par le journal public de Sigstore publierait des digests d’artefacts, des identités de dépôts et le calendrier des releases hors de l’UE.
Nous préférons cadrer cela par rapport à une exigence réelle plutôt que de la deviner. Si c’est une contrainte d’achat pour vous, dites-nous de quelles propriétés vous avez besoin et selon quel calendrier, et nous reviendrons vers vous avec un plan daté plutôt qu’une affirmation.
Modèle d’isolation du noyau
Chaque job obtient sa propre machine virtuelle ECS éphémère sur T Cloud Public en eu-de, détruite une fois ce job terminé. Les VM sont à tâche unique : le runner est supprimé après son unique tâche et son identifiant par runner disparaît avec le job, si bien qu’une VM ne peut pas servir un second job, même au sein de la même exécution de workflow. (Avant le 2026-07-30, des jobs successifs sur un même commit pouvaient partager une VM ; ce n’est plus possible.) Les propriétés d’isolation :
| Propriété | Détail |
|---|---|
| Partage du noyau | Aucun noyau n’est partagé entre commits ni entre organisations : chaque commit obtient une VM dédiée. Les jobs du même commit peuvent partager cette VM ; traitez donc un pipeline comme un seul domaine de confiance |
| Persistance | La VM est détruite une fois les jobs du commit terminés ; aucun état du système de fichiers ne survit vers un autre commit ou une autre organisation. L’état peut passer d’un job à l’autre au sein du même commit : une étape de build qui exécute le code de dépendances tierces partage la machine avec une étape de déploiement ultérieure ; limitez donc à ce pipeline tout identifiant dont un déploiement a besoin |
| Accès à l’API de la plateforme | Aucune agence IAM n’est attachée à la VM ; l’identité dérivée des métadonnées n’a aucun rôle, l’API OTC est donc inaccessible depuis l’intérieur |
| Réseau | Groupe de sécurité par VM avec liste d’autorisation en sortie uniquement ; les runners ne peuvent pas recevoir de connexions entrantes |
| Métadonnées T Cloud Public | Bloquées par le proxy sortant anti-SSRF ; le code du runner ne peut pas atteindre le point de terminaison de métadonnées de l’instance avec une portée privilégiée |
Voir Runners CI pour la spécification complète de l’isolation et l’enregistrement de vos propres runners.
Isolation des tenants
Chaque tenant fremforge est un espace de noms à clé distincte dans la plateforme. La chaîne d’isolation va de l’URL au middleware, à la couche de requêtes de la base de données, à la couche de stockage et à la chaîne d’audit.
| Couche | Mécanisme | Ce qui empêche l’accès entre tenants |
|---|---|---|
| Routage des URL | Le middleware /<slug>/_admin/* recharge le tenant depuis le slug à chaque requête | Un utilisateur disposant d’une session pour l’organisation A qui colle l’URL de l’organisation B est rejeté au contrôle d’appartenance, pas sur la base d’un cookie périmé |
| Contrôle d’appartenance | Recherche dans l’équipe Owners de Forgejo par rapport au slug, vérifiée à chaque requête | L’api ne se fie jamais à une affirmation entrante sur les organisations auxquelles appartient l’utilisateur |
| Base de données | Chaque table liée à un tenant a une clé étrangère tenant_id ; chaque lecture ET chaque écriture filtre sur celle-ci | Un SELECT / UPDATE / DELETE sans tenant_id est structurellement impossible dans les chemins signalés par l’audit |
| Jetons signés | Chaque URL signée de courte durée (lien magique de facturation, annulation de résiliation, état OIDC) porte l’identifiant du tenant dans sa charge utile ; la vérification le recoupe avec le slug de l’URL | Un jeton émis pour le tenant A est rejeté s’il est rejoué sur l’URL du tenant B |
| Stockage (OBS) | Les SBOM, attestations et objets du journal d’audit sont tous indexés <artifact>/<tenant_id>/... ; l’émission d’URL signées revalide tenant.id === row.tenant_id avant de les délivrer | Une URL OBS présignée liée au tenant A ne peut pas être réécrite pour lire le tenant B |
| Chaîne d’audit | Chaîne de hachage infalsifiable indexée par tenant ; les ajouts d’un tenant ne modifient jamais la tête de chaîne d’un autre | Une rupture dans la chaîne du tenant A (par ex. une tentative de suppression rétroactive) n’affecte pas l’intégrité de la chaîne du tenant B |
| Accès de l’opérateur | La consultation de données client par le personnel (fremverk) est consignée dans la chaîne d’audit du client comme action d’opérateur ; les administrateurs du tenant voient les visites de l’opérateur dans leur propre journal d’audit | Aucun accès silencieux du personnel aux données client |
Délai de révocation de l’équipe Owners
L’api met en cache l’appartenance d’un client à l’équipe Owners de Forgejo pendant jusqu’à 5 minutes afin que les pages se chargent en moins de 50 ms (l’alternative serait un aller-retour vers l’API Forgejo à chaque requête, soit 100 à 200 ms de plus par page). Le compromis :
- Lorsque vous ajoutez quelqu’un à Owners côté Forgejo, il obtient l’accès administrateur en moins de 5 minutes.
- Lorsque vous retirez quelqu’un d’Owners côté Forgejo, il perd l’accès administrateur en moins de 5 minutes, pas instantanément.
Pour la plupart des opérations, ce délai est acceptable : un ancien propriétaire ne peut pas créer de nouvelles ressources Forgejo (le contrôle propre de Forgejo est immédiat), et toute action privilégiée via l’interface d’administration de fremforge est enregistrée dans le journal d’audit du tenant avec le nom d’utilisateur de l’auteur, si bien que l’activité postérieure à la révocation reste attribuable.
Si vous avez besoin d’une révocation immédiate (par ex. dans le cadre d’une réponse à incident après la compromission d’un identifiant), l’opérateur d’astreinte peut ramener le TTL du cache à zéro pour votre tenant sur demande : ouvrez un ticket à support@frem.sh avec le préfixe d’objet [urgent-revocation], le nom d’utilisateur et le slug du tenant. SLA standard : moins de 15 minutes pendant les heures ouvrées, moins de 60 minutes en dehors. Le même effet s’obtient en révoquant la session Forgejo de l’utilisateur (Owner Forgejo → profil de l’utilisateur → Sign out), ce qui invalide le cookie de session que lit l’api, sans aucune action côté fremforge.
Engagements de sécurité de la plateforme
| Gravité de la CVE | SLA de correctif (à partir de la version corrigée en amont) |
|---|---|
| Critique (CVSS ≥ 9.0) | 48 heures |
| Élevée (7.0-8.9) | 72 heures |
| Moyenne (4.0-6.9) | 7 jours |
| Faible | Prochaine fenêtre de maintenance planifiée |
Le SLA de correctif est publié contractuellement et cité mot pour mot dans l’annexe de sécurité du DPA.
Intégrité du journal d’audit : chaîne de hachage infalsifiable ancrée dans le stockage WORM OBS de T Cloud Public toutes les 2 minutes ; contrôle d’intégrité FunctionGraph horaire ; toute rupture de chaîne alerte l’astreinte.
Dépannage
Push bloqué, mais je ne trouve pas le secret dans le diff.
Le secret peut se trouver dans un fichier modifié dont le contenu complet n’est pas visible dans le diff standard. Exécutez Gitleaks localement pour identifier l’emplacement exact :
pip install gitleaks # or brew install gitleaks
gitleaks detect --source . --verboseAucune pull request de correctif pour une CVE connue.
Vérifiez que l’écosystème et le manifeste du paquet bénéficient de pull requests de correctif (Écosystèmes pris en charge) et que l’avis comporte une version corrigée. Renovate s’exécute chaque nuit pour une organisation inscrite ; Sécurité › Corriger › Mises à jour des dépendances → Exécuter maintenant lance une exécution hors cycle, et Ouvrir une demande de correctif sur le résultat demande immédiatement un correctif de sécurité. Voir Quand aucune pull request n’est ouverte.
Le téléchargement du SBOM renvoie 404.
Les SBOM ne sont générés que pour les images poussées après l’activation de la génération de SBOM. Les images poussées avant l’activation n’ont pas de SBOM. Poussez un nouveau tag pour en générer un.
La signature SSH échoue avec ssh-keygen: gpg failed to sign the data.
Assurez-vous que git config gpg.format vaut ssh (et non la valeur par défaut openpgp) et que user.signingkey pointe vers un fichier de clé publique. Sur macOS, vérifiez aussi que ssh-keygen est dans le $PATH (le ssh-keygen fourni par Apple fonctionne ; si vous avez installé OpenSSH via Homebrew, assurez-vous qu’il le masque correctement).
Renvois
- Protection des push : la liste des dérogations et le mode de défaillance
- Mises à jour des dépendances : les mises à jour de version et la référence de configuration de Renovate
- Runners CI : isolation des runners, VM ECS éphémères par job, enregistrement de vos propres runners
- Fédération de jetons OIDC : authentification cloud sans clé depuis les jobs CI
- Webhooks d’alerte de sécurité : les événements de sécurité vers votre outillage d’incident, y compris ServiceNow