Construire un dossier d’assurance sécurité : des affirmations étayées uniquement par des contrôles réellement exécutés, consignés dans un registre chaîné, puis vérifiés à un commit.
assurance est la commande qui porte le dossier d’assurance sécurité du pack Shield. Un dossier liste des affirmations sur votre système (par exemple « aucune dépendance de production n’a d’avis connu de gravité haute »), donne un argument pour chacune et l’étaye par des preuves. La seule preuve que BMAD+ accepte est un contrôle qu’il a lui-même exécuté : une commande, son code de sortie, l’empreinte de sa sortie et celles des fichiers qu’elle a écrits, ajoutés à un registre chaîné par empreintes. Une déclaration, un document ou un lien est refusé. On l’utilise quand quelqu’un, un auditeur, un client ou vous-même plus tard, demande de montrer que des mesures tiennent à un commit donné et que « on le croit » ne suffit pas.
Attention· Ce qu’un verdict n’est pas
Un dossier « supported » montre que les contrôles nommés ont réussi à un commit. Il ne certifie pas le système et n’est pas un avis d’audit ; c’est à un auditeur qualifié de juger ce que valent les preuves. De plus, assurance run exécute les commandes écrites dans le dossier avec la même confiance que vos propres scripts : lisez un dossier venu d’ailleurs avant de le lancer.
Copie le modèle Shield vers le fichier de dossier que vous nommez. Le fichier doit s’appeler <id>.yaml ; l’identifiant vient du nom. Il n’écrase jamais un fichier existant.
run
Exécute les contrôles du dossier (tous, ou ceux nommés par --check) et ajoute un enregistrement par contrôle au registre.
verify
Juge chaque contrôle sur sa dernière exécution et chaque affirmation sur ses preuves, puis affiche le verdict. N’exécute rien.
Options de assurance
Option
Argument
Défaut
Ce qu’elle fait
-d, --directory
<path>
dossier courant
Dossier du projet.
--dir
<path>
_bmad-output/assurance
Dossier des preuves dans le projet ; le registre se trouve dans <dir>/<case id>/runs.jsonl. Chemin relatif écrit avec /.
--check
<id>
tous les contrôles
run seulement : n’exécuter que ce contrôle. Répétez l’option pour en nommer plusieurs.
--ledger-head
<sha256>
aucune
verify seulement : échouer si le registre ne contient plus l’enregistrement portant cette empreinte (64 caractères hexadécimaux minuscules).
--emit-check
<file>
aucune
verify seulement : écrire aussi le verdict en résultat de contrôle JSON pour la CI. Le nom doit finir par .json et ne doit être ni le dossier ni son registre.
--json
-
désactivée
Afficher le résultat en JSON (schemaVersion: 1) plutôt qu’en texte. Les erreurs deviennent { "status": "error", "message": ... }.
Par convention un dossier vit dans _bmad/assurance/<id>.yaml, mais tout chemin relatif au projet convient, sans .. ni lien symbolique. init écrit le modèle Shield, qui est un exemple complet et fonctionnel (quatre contrôles, une affirmation de tête et quatre sous-affirmations). Remplacez chaque exemple par le vôtre, committez le fichier, puis lancez-le. Voici la forme ; les clés inconnues sont refusées.
Forme d’un dossier (abrégée du modèle)
schema: bmad-plus/assurance-case/1
id: release-security
title: Release security case
scope: >-
The service at the commit being released.
freshness:
maxAgeDays: 30
sameCommit: true
checks:
- id: unit-tests
run: [npm, test]
timeoutSeconds: 900
- id: secret-scan
run: [gitleaks, detect, --no-banner, --redact, --report-path, reports/gitleaks.json]
artifacts: [reports/gitleaks.json]
claims:
- id: C1
claim: The release ships no committed credential and no failing control test.
argument: >-
Holds when both sub-claims hold.
- id: C1.1
parent: C1
claim: No credential is committed in the repository history.
argument: >-
The scanner reads every reachable commit and exits non-zero on a finding.
controls: [ISO27001:A.5.17, SOC2:CC6.1]
evidence:
- check: secret-scan
shows: gitleaks finds no secret and writes its report.
- id: C1.2
parent: C1
claim: The behaviour the controls rely on is covered by passing tests.
argument: >-
The suite exercises access checks and input validation.
controls: [ISO27001:A.8.29]
evidence:
- check: unit-tests
shows: The test suite passes at this commit.
Clés d’un dossier
Clé
Règle
schema
Doit valoir bmad-plus/assurance-case/1.
id
Minuscules, chiffres, points et tirets, 64 caractères au plus.
title, scope
Texte libre, de 1 à 4000 caractères chacun.
freshness.maxAgeDays
Nombre entier de jours de 1 à 366. Défaut 30. Une exécution plus ancienne est stale.
freshness.sameCommit
true (défaut) lie les preuves au commit extrait et à un arbre de travail propre. Mettez false seulement pour un projet hors git.
checks[].id
Identifiant unique du contrôle.
checks[].run
La commande sous forme de liste d’arguments séparés. Aucun shell n’intervient. Sous Windows, les programmes comme npm sont des fichiers .cmd et demandent [cmd, /c, npm, ...].
checks[].cwd
Dossier relatif au projet où exécuter. Défaut ..
checks[].env
Noms de variables d’environnement supplémentaires que le contrôle peut voir. Par défaut il hérite seulement de PATH, HOME, des dossiers temporaires et de quelques variables système Windows. BMAD_PLUS_ASSURANCE_KEY ne peut jamais y figurer.
checks[].timeoutSeconds
Nombre entier de 1 à 21600. Défaut 600.
checks[].expect.exitCode
Code de sortie qui vaut succès, de 0 à 255. Défaut 0.
checks[].artifacts
Fichiers relatifs au projet que la commande doit écrire ; chacun est condensé avec l’exécution. Un fichier laissé inchangé par l’exécution n’est pas compté comme produit.
claims[].id, claim, argument
Obligatoires. L’argument dit pourquoi un contrôle réussi montre l’affirmation.
claims[].parent
Une affirmation déclarée plus haut. Une affirmation sans preuve doit avoir au moins une sous-affirmation, sinon elle est « seulement déclarée » et le dossier ne se charge pas.
claims[].controls
Liste facultative de références FRAMEWORK:ID. Référentiels acceptés : ISO27001, SOC2, GDPR, EU-AI-Act, NIS2, NIST-800-53. Une mesure mal saisie ou inventée est refusée.
claims[].evidence
Liste de { check, shows }. check doit nommer un contrôle déclaré.
init écrit un seul fichier : le dossier, au chemin donné (les dossiers parents sont créés).
run lit le dossier, exécute chaque contrôle choisi comme processus enfant sans shell, et ajoute une ligne par contrôle à <dir>/<case id>/runs.jsonl. Pendant son travail il détient runs.jsonl.lock. Il interroge git (rev-parse, status, ls-files) pour noter le commit et si l’arbre était propre.
verify lit le dossier, le registre, git et les artefacts déclarés. Il n’écrit rien, sauf le fichier facultatif de --emit-check.
Elle n’ouvre elle-même aucune connexion réseau. Les commandes que vous déclarez font ce qu’elles font ; c’est pourquoi l’inventaire de traitement des données liste assurance-checks comme un flux qui n’existe que lorsque vous invoquez run.
Refusés partout : les chemins hors du projet et tout chemin qui traverse un lien symbolique.
Chaque enregistrement de runs.jsonl est une ligne JSON : l’identifiant du contrôle, un numéro de séquence, l’empreinte de la commande, le commit (revision) et dirty (s’il existait des modifications suivies ou des fichiers non suivis non ignorés), les heures de début et de fin, le code de sortie, un éventuel dépassement de délai, la taille et l’empreinte de la sortie avec un extrait expurgé de ses 2048 derniers octets, l’empreinte de chaque artefact déclaré (ou null si l’exécution ne l’a pas écrit), l’empreinte de l’enregistrement précédent et son propre sha256. Modifier, réordonner ou retirer un enregistrement avant le dernier casse la chaîne, et verify juge alors tout le dossier non étayé.
La chaîne seule ne révèle que les modifications négligentes : quiconque peut écrire le fichier peut ajouter un enregistrement qui se vérifie, ou couper les derniers. Deux éléments gardés hors du registre comblent cet écart.
La clé. Définissez BMAD_PLUS_ASSURANCE_KEY (au moins 32 caractères) depuis un secret de CI. run ajoute alors un HMAC-SHA256 à chaque enregistrement et verify refuse tout enregistrement que la clé n’authentifie pas. La clé n’est jamais transmise à un contrôle. Utilisez la même clé pour run et verify : un registre écrit sans clé est refusé dès que la clé est définie.
La tête. run affiche l’empreinte de son dernier enregistrement. Gardez-la là où ceux qui écrivent le registre ne peuvent pas la changer (le journal du job de CI, le dossier d’audit) et passez-la à verify --ledger-head. Un registre qui ne contient plus cet enregistrement est refusé : les exécutions retirées se voient.
Statut d’un contrôle (d’après sa dernière exécution)
Statut
Signification
passed
S’est exécuté, a fait ce que le contrôle attend, et est récent.
failed
N’a pas démarré, a dépassé le délai, est sorti avec un autre code que celui attendu, ou un artefact n’a pas été produit par l’exécution.
stale
A réussi, mais à un autre commit, sur des modifications non commitées ou des fichiers non suivis, avec une commande qui a changé depuis, avec un artefact qui a changé depuis, plus ancien que maxAgeDays, ou avec une heure de fin dans le futur.
missing
Ne s’est jamais exécuté.
Une affirmation est supported seulement quand tous ses contrôles de preuve sont passed et que toutes ses sous-affirmations sont supported ; sinon elle est unsupported, avec les raisons. Le dossier est supported seulement quand chaque affirmation l’est, que le registre est intact, que toute --ledger-head s’y trouve encore et, avec sameCommit: true, qu’il y a un commit et que l’arbre de travail n’a ni modification non commitée ni fichier non suivi (le dossier du registre et les artefacts du dossier ne comptent pas). Le verdict liste aussi controls.supported et controls.unsupported : les mesures étiquetées sur des affirmations qui ont, ou n’ont pas, de preuve exécutée.
Chaque contrôle choisi s’est terminé comme il l’attend.
run
1
Au moins un contrôle choisi a échoué (démarrage impossible, délai dépassé, mauvais code de sortie, ou artefact déclaré non produit). Les enregistrements sont écrits quand même.
verify
0
Le dossier est supported.
verify
1
Le dossier est unsupported.
toutes
3
La commande n’a pas pu faire son travail : action inconnue, dossier absent ou invalide, clé ou tête incorrecte, registre non intact avec run, verrou tenu, contrôle non déclaré, --emit-check refusé. Affiché assurance: <message> sur le flux d’erreur.
Les trois transcriptions ci-dessous sont composées à partir des modèles de messages du code source, avec des valeurs illustratives (chemins, durées et empreintes sont inventés). Elles sont marquées comme simulées pour cette raison.
Démarrer un dossier, puis l’exécuterSimulé
$ npx bmad-plus assurance init _bmad/assurance/release-security.yaml_bmad/assurance/release-security.yaml: case release-security started from the Shield template.Replace every example claim and check with those of the project, commit it, then run it.$ npx bmad-plus assurance run _bmad/assurance/release-security.yaml/home/me/app/_bmad-output/assurance/release-security/runs.jsonl passed unit-tests exit 0 in 41.3 s passed dependency-audit exit 0 in 6.8 s passed secret-scan exit 0 in 3.1 s passed review-gate exit 0 in 1.2 sRecorded 4 run(s); ledger head 3f9c1b7e5a2d4c6e8b0a1d3f5c7e9b2a4d6f8a0c1e3b5d7f9a2c4e6b8d0f1a3c.Keep the head outside the ledger and verify with bmad-plus assurance verify _bmad/assurance/release-security.yaml --ledger-head 3f9c1b7e5a2d4c6e8b0a1d3f5c7e9b2a4d6f8a0c1e3b5d7f9a2c4e6b8d0f1a3c.
npx bmad-plus assurance init _bmad/assurance/release-security.yaml
npx bmad-plus assurance run _bmad/assurance/release-security.yaml
Transcription illustrative, non capturée lors d’une exécution réelle. Source: tools/cli/commands/assurance.js (modèles de messages de initAction et runAction)
Transcription illustrative, non capturée lors d’une exécution réelle. Source: tools/cli/commands/assurance.js (modèle de message de verifyAction)
Vérifier après un changement de codeSimulé
$ npx bmad-plus assurance verify _bmad/assurance/release-security.yamlrelease-security: unsupported — 3/5 claim(s) supported, 4 recorded run(s) stale unit-tests: it ran at 1a2b3c4d5e6f, not at 9f8e7d6c5b4a unsupported claim C1: sub-claim C1.4 is unsupported unsupported claim C1.4: evidence unit-tests is stale controls with executed evidence: ISO27001:A.8.8, NIST-800-53:RA-5, ISO27001:A.5.17, SOC2:CC6.1, ISO27001:A.8.32, SOC2:CC8.1# Code de sortie 1. Relancez le contrôle périmé au nouveau commit, puis vérifiez.
Transcription illustrative, non capturée lors d’une exécution réelle. Source: tools/cli/commands/assurance.js (verifyAction), tools/cli/lib/assurance.js (motifs de judgeCheck)
Avec --emit-check, le fichier écrit est un objet JSON de schéma bmad-plus/assurance-check/1 : le statut, la révision, les affirmations avec leurs raisons, les mesures, et une entrée github (name, conclusion valant success ou failure, un titre comme 3/5 claims supported et un tableau récapitulatif en Markdown) qu’une étape de CI peut publier comme exécution de contrôle. Il ne contient aucune date, donc le même verdict produit le même fichier.
the ledger is not intact: ... ou ... is not authenticated by BMAD_PLUS_ASSURANCE_KEY : un enregistrement a été modifié, ou la clé ne correspond pas. Reconstruisez le registre en relançant les contrôles dans un nouveau --dir.
the ledger no longer holds the head ... it was anchored to : des enregistrements ont été retirés ou le registre remplacé.
the working tree has uncommitted changes or untracked files : committez, puis relancez et vérifiez. Les preuves décrivent le commit, pas votre arbre de travail.
there is no commit to bind the evidence to : le dossier n’est pas un dépôt git avec un commit. Mettez freshness.sameCommit: false seulement pour un projet hors git.
Un contrôle est missing, failed ou stale : la ligne <status> <check>: <reasons> dit pourquoi. Relancez ce contrôle au commit courant.
Faire construire et exécuter un dossier par Shield pour une version#
Vous allez publier une version et un client a demandé la preuve que les mesures de sécurité tiennent à ce commit précis. L’arbre de travail est commité.
Dans votre assistant IA
Shield, démarre un dossier d’assurance pour cette version. Garde chaque affirmation assez petite pour qu’une commande la montre, étiquette-les avec les mesures qu’elles soutiennent, puis exécute les contrôles à ce commit et vérifie le dossier. Dis-moi quelles affirmations ne sont pas étayées et pourquoi.
Ce que vous devriez obtenir Shield lance bmad-plus assurance init _bmad/assurance/<id>.yaml, remplace les affirmations et contrôles d’exemple du modèle par ceux de votre projet (une affirmation de tête, des sous-affirmations chacune appuyée par une commande qui sort en erreur quand l’affirmation tombe) et vous demande de commiter le fichier. Il lance ensuite assurance run, garde la tête de registre affichée et lance assurance verify --ledger-head <head> --emit-check .... Il rapporte chaque affirmation non étayée avec la raison donnée par verify et liste les mesures qui ont une preuve exécutée. Il dit si une clé et une tête ont servi ; sans elles, il précise que les preuves sont consignées mais pas infalsifiables. Il ne dit jamais qu’une affirmation non étayée est satisfaite.
Un job de CI a échoué sur assurance verify, alors que le dossier était vert la semaine dernière.
Dans votre assistant IA
Shield, vérifie le dossier release-security et explique chaque affirmation non étayée. Ne relance rien avant de m’avoir dit ce qui est périmé et pourquoi.
Ce que vous devriez obtenir Shield lance bmad-plus assurance verify _bmad/assurance/release-security.yaml (lecture seule) et lit la sortie : la ligne de statut, chaque contrôle non réussi avec ses raisons (autre commit, modifications non commitées, commande ou artefact modifié, plus ancien que maxAgeDays) et chaque affirmation non étayée. Il les explique en mots simples, vous dit quels contrôles relancer avec assurance run --check <id>, et attend votre feu vert avant d’exécuter quoi que ce soit, car run exécute les commandes écrites dans le dossier.