Skip to content

Chaîne d'approvisionnement — ce qui se vérifie

This content is not available in your language yet.

Un acheteur en secteur régulé n’a pas besoin qu’on lui affirme quelque chose : il a besoin de pouvoir le vérifier. Cette page décrit les artefacts et les contrôles qui existent aujourd’hui, avec le chemin du fichier ou du workflow qui les produit. Ce qui n’existe pas encore est dit ailleurs, et sans détour.

Le workflow .github/workflows/sbom-release.yml produit une nomenclature CycloneDX de toutes les dépendances npm du dépôt, générée par Trivy sur une version figée :

Fenêtre de terminal
trivy fs --format cyclonedx --output source.cdx.json ./

Le document est attaché à la release sous un nom adressable et versionné, lemniscate-source-<tag>.cdx.json. Il n’est pas committé : c’est un dérivé reproductible d’une version figée, ce qui permet de le rejouer avec un autre outil et de comparer.

Un garde-fou précède l’attachement. Une nomenclature vide ou mal formée ne part pas en silence : le workflow échoue si le document n’annonce pas bomFormat: CycloneDX, s’il n’a pas de version de spécification, ou s’il ne contient aucun composant. Le décompte des composants est écrit dans le résumé du job.

Ce qui est mesuré, et ce qui ne l’est pas. Le mécanisme est en place et il s’auto-vérifie : toute modification de ce workflow ou de l’installation de Trivy déclenche une génération et une validation de la nomenclature. En revanche, l’étape d’attachement n’a pas encore été exercée sur une release réelle — l’identifiant SEC-9.3-01 du référentiel de sécurité n’est pas relevé pour cette raison. Ce qui est affirmable aujourd’hui est donc : le mécanisme existe, il est validé, et une nomenclature peut être réclamée par version. Pas davantage.

Un second niveau de nomenclature — une par artefact livrable — est prévu par la décision D1 de specs/2026-07-29-sbom-vex/spec.md et n’est pas produit à ce jour.

Un scanner qui rend 300 vulnérabilités sur une nomenclature ne dit rien d’utile tant qu’on ne sait pas lesquelles atteignent réellement le produit. La réponse à « vous avez N CVE, justifiez » vit dans security/vex/, sous forme de documents OpenVEX versionnés.

Ces documents sont committés à dessein. Ce sont des décisions humaines tracées : l’historique git dit qui a justifié quoi, et quand. Un acheteur n’obtient pas une liste d’exclusions, il obtient une chaîne de responsabilité.

La règle est tenue par notre code, pas par la clémence d’un outil

Section intitulée « La règle est tenue par notre code, pas par la clémence d’un outil »

Le validateur security/lib/vex.ts rejette tout statement not_affected dépourvu de l’une des cinq justifications du référentiel CISA :

  • component_not_present
  • vulnerable_code_not_present
  • vulnerable_code_not_in_execute_path
  • vulnerable_code_cannot_be_controlled_by_adversary
  • inline_mitigations_already_exist

Le rejet intervient avant que le scanner ne consomme le dossier. Aucune CVE n’est donc retirée d’un rapport sans justification recevable, et cette garantie ne dépend pas du comportement d’un outil tiers. Elle est couverte par security/vex/vex-validation.test.ts.

Le contrôle porte aussi sur le vrai dossier, pas seulement sur des fixtures : security/vex/vex-dir-compliance.test.ts valide security/vex/ tel qu’il est committé, de sorte qu’un statement non conforme fait rougir l’intégration continue.

Le dossier de production est volontairement vide. Tant qu’aucun document ne s’y trouve, aucune CVE n’est supprimée d’aucun rapport : l’état par défaut ne masque rien.

Aucune analyse automatisée d’atteignabilité n’est employée (décision D3 de specs/2026-07-29-sbom-vex/spec.md). Deux raisons sont consignées : les moteurs crédibles pour JavaScript et TypeScript sont des services hébergés aux États-Unis, rédhibitoire pour un produit destiné à des environnements isolés ; et leur mode d’échec est le faux négatif, c’est-à-dire une affirmation de conformité fausse.

Contrôle Fichier Déclenchement Effet
OSV différentiel sur une proposition de modification .github/workflows/osv-scanner-pr.yml à chaque proposition bloquant si elle en introduit une
OSV, dépôt complet .github/workflows/osv-scanner-scheduled.yml lundi 06h00 UTC rapport SARIF + décompte
Secrets (Trivy) .github/workflows/trivy-secret-scan.yml lundi 06h30 UTC rapport SARIF + décompte
Images de conteneurs (Trivy) .github/workflows/trivy-image-scan.yml lundi 07h00 UTC construction locale puis scan
Dépendances après suppression VEX (Trivy) .github/workflows/trivy-vex-scan.yml lundi 07h30 UTC liste résiduelle, chaque retrait justifié

La porte différentielle mérite qu’on s’y arrête : elle exécute deux scans, l’un sur la branche cible, l’autre sur la branche courante, et n’échoue que si la modification introduit une vulnérabilité. Une dette héritée ne bloque donc pas le travail courant, mais une régression ne passe pas. Le contrôle ne dépend d’aucune fonctionnalité payante.

Le scan « après suppression VEX » consomme les documents du dépôt : sa sortie est « ce qui reste réellement à traiter », chaque retrait étant adossé à un document justifié et signé par un auteur identifiable. Son comportement est vérifié sur une vulnérabilité figée dans security/vex/vex-suppression.integration.test.ts.

Le scan de secrets est lui-même éprouvé par un canari : security/trivy/secret-scan.integration.test.ts place un secret factice et vérifie qu’il est détecté. Un scanner dont on ne teste pas la détection n’est pas un contrôle, c’est une case cochée.

Enfin, un job dédié .github/workflows/security-checks.yml exécute le typage et les tests du paquet security/ sur toute proposition qui y touche.

Un scanner que l’on télécharge sans le vérifier annule l’intérêt du scan.

  • Trivy est épinglé par version et vérifié par empreinte. L’action .github/actions/install-trivy/action.yml centralise la version pour tous les workflows de sécurité et compare l’archive au fichier de sommes publié dans la même release avant de l’exécuter. Aucun tag mouvant, aucun latest (identifiant SEC-9.3-04).
  • Les actions tierces des workflows de sécurité sont épinglées par empreinte de commit, avec la version en commentaire.
  • Les dépendances sont installées par npm ci contre les fichiers de verrouillage, ce qui valide l’empreinte d’intégrité de chaque paquet installé.

Le contrôle le plus démontrable : zéro symbole d’egress sur l’artefact construit

Section intitulée « Le contrôle le plus démontrable : zéro symbole d’egress sur l’artefact construit »

C’est l’argument qui n’exige aucune confiance, parce qu’il ne porte pas sur les sources mais sur le binaire livré.

Lemniscate se construit selon un profil de déploiement choisi à la construction, et non par un réglage à l’exécution : dans l’artefact on-premise, la couche de sortie réseau n’est pas désactivée, elle est absente du graphe de modules. Une garantie qui dépendrait d’un réglage se falsifierait par un changement de configuration ; celle-ci se falsifie par inspection de l’artefact.

Fenêtre de terminal
node scripts/verify-onprem-artifact.mjs

Le script applique quatre contrôles indépendants :

  1. Les symboles présents dans le texte des artefacts construits — domaines, clés de projet, noms de bibliothèques. C’est le seul angle vérifiable par un tiers sans lire le code source.
  2. Les entrées des manifestes de construction esbuild — un bundle minifié peut ne plus porter le nom du paquet qu’il embarque, mais le manifeste liste chaque module entré dans le graphe. Deux angles de vue sur le même artefact, dont l’un survit à la minification.
  3. L’inventaire des unités déployables — l’apparition d’une unité non déclarée fait échouer le contrôle. Un périmètre qui s’érode en silence ne garantit rien.
  4. L’absence d’appel de sortie réseau au chargement d’un module.

Ce contrôle n’a aucune option de désactivation et aucune liste d’exceptions. Le critère d’acceptation de l’arbitrage AR-03 (specs/securite/arbitrages.md) est zéro occurrence. Un symbole qui réapparaît se retire du graphe de modules ; il ne se met pas sur une liste dérogatoire.

Il s’exécute sur chaque proposition de modification, dans .github/workflows/pr.yaml. Le script est lui-même couvert par scripts/verify-onprem-artifact.test.mjs — un contrôle non testé peut devenir muet sans que personne ne s’en aperçoive.

Le détail des flux, côté produit, est décrit dans Où passent les données.

Le canal est SECURITY.md, à la racine du dépôt : signalement par courriel à security@lemniscate-tech.com, sans ouverture d’issue publique, avec description, étapes de reproduction, appréciation de l’impact et mitigations éventuelles.

C’est un canal de signalement, pas un programme de prime aux vulnérabilités, et il ne prétend pas à davantage. Ce qu’il ne porte pas encore est dit dans Ce qui reste à construire.

  • Une nomenclature CycloneDX des sources, générée et validée par version.
  • Un dossier OpenVEX versionné, où chaque exclusion porte un auteur, une date et une justification du référentiel CISA, le retrait étant refusé par notre code si la justification manque.
  • Des rapports de scan reproductibles : porte différentielle bloquante, scan complet hebdomadaire, secrets, images, dépendances après suppression VEX.
  • Une politique de divulgation publiée avec canal dédié.
  • Un contrôle « zéro symbole de sortie réseau » exécuté sur l’artefact construit, vérifiable par un tiers sans accès au code source.
  • Une méthode de preuve traçable : 286 identifiants SEC-*, statuts adossés à des preuves fichier:ligne, arbitrages datés avec leurs options écartées (posture de conformité).