Parcourir la documentation

assurance

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.

Sur cette page
  1. Synopsis
  2. Le fichier de dossier
  3. Ce qu’elle lit et écrit
  4. Le registre et sa clé
  5. Comment le verdict est construit
  6. Codes de sortie
  7. À quoi ressemble la sortie
  8. Erreurs et que faire
  9. Exemples

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.

Synopsis

Dans le terminal de votre projet
npx bmad-plus assurance init <case-file>
npx bmad-plus assurance run <case-file> [--check <id>]... [--dir <path>] [--json]
npx bmad-plus assurance verify <case-file> [--ledger-head <sha256>] [--emit-check <file.json>] [--dir <path>] [--json]

Actions
ActionCe qu’elle fait
initCopie 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.
runExécute les contrôles du dossier (tous, ou ceux nommés par --check) et ajoute un enregistrement par contrôle au registre.
verifyJuge 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
OptionArgumentDéfautCe qu’elle fait
-d, --directory<path>dossier courantDossier du projet.
--dir<path>_bmad-output/assuranceDossier des preuves dans le projet ; le registre se trouve dans <dir>/<case id>/runs.jsonl. Chemin relatif écrit avec /.
--check<id>tous les contrôlesrun seulement : n’exécuter que ce contrôle. Répétez l’option pour en nommer plusieurs.
--ledger-head<sha256>aucuneverify seulement : échouer si le registre ne contient plus l’enregistrement portant cette empreinte (64 caractères hexadécimaux minuscules).
--emit-check<file>aucuneverify 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éeAfficher le résultat en JSON (schemaVersion: 1) plutôt qu’en texte. Les erreurs deviennent { "status": "error", "message": ... }.

Le fichier de dossier

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
schemaDoit valoir bmad-plus/assurance-case/1.
idMinuscules, chiffres, points et tirets, 64 caractères au plus.
title, scopeTexte libre, de 1 à 4000 caractères chacun.
freshness.maxAgeDaysNombre entier de jours de 1 à 366. Défaut 30. Une exécution plus ancienne est stale.
freshness.sameCommittrue (défaut) lie les preuves au commit extrait et à un arbre de travail propre. Mettez false seulement pour un projet hors git.
checks[].idIdentifiant unique du contrôle.
checks[].runLa 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[].cwdDossier relatif au projet où exécuter. Défaut ..
checks[].envNoms 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[].timeoutSecondsNombre entier de 1 à 21600. Défaut 600.
checks[].expect.exitCodeCode de sortie qui vaut succès, de 0 à 255. Défaut 0.
checks[].artifactsFichiers 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, argumentObligatoires. L’argument dit pourquoi un contrôle réussi montre l’affirmation.
claims[].parentUne 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[].controlsListe 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[].evidenceListe de { check, shows }. check doit nommer un contrôle déclaré.

Ce qu’elle lit et écrit

  • 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.

Le registre et sa clé

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.

Comment le verdict est construit

Statut d’un contrôle (d’après sa dernière exécution)
StatutSignification
passedS’est exécuté, a fait ce que le contrôle attend, et est récent.
failedN’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.
staleA 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.
missingNe 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.

Codes de sortie

Codes de sortie par action
ActionCodeSignification
init0Fichier de dossier écrit.
run0Chaque contrôle choisi s’est terminé comme il l’attend.
run1Au 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.
verify0Le dossier est supported.
verify1Le dossier est unsupported.
toutes3La 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.

À quoi ressemble la sortie

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 s
Recorded 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.

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)

Vérifier au même commitSimulé
npx bmad-plus assurance verify _bmad/assurance/release-security.yaml --ledger-head 3f9c1b7e5a2d4c6e8b0a1d3f5c7e9b2a4d6f8a0c1e3b5d7f9a2c4e6b8d0f1a3c --emit-check ci/assurance.json
release-security: supported — 5/5 claim(s) supported, 4 recorded run(s)
  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, ISO27001:A.8.29
  check: /home/me/app/ci/assurance.json

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.yaml
release-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.

Erreurs et que faire

Messages affichés après assurance: (code de sortie 3)
MessageCauseQue faire
unknown action "x" (init, run, verify)Le premier argument n’est pas l’une des trois actions.Utilisez init, run ou verify.
no assurance case at <path>Le fichier de dossier n’existe pas.Vérifiez le chemin ou créez-le avec init.
<path> already existsinit n’écrase jamais.Choisissez un autre nom ou modifiez le dossier existant.
case file: name it <id>.yaml ...Le nom du fichier n’est pas <id>.yaml avec un identifiant valide.Utilisez des minuscules, chiffres, points et tirets dans l’identifiant.
<case>: claim X is only asserted; support it with evidence from a check or with sub-claimsUne affirmation n’a ni preuve ni sous-affirmation.Ajoutez evidence nommant un contrôle, ou des sous-affirmations avec parent.
evidence must name a check that runs (check: <id>); a statement, document or link is only assertedUn élément de preuve n’est pas de la forme { check, shows }.Écrivez un contrôle dont la commande montre l’affirmation, et désignez-le.
BMAD_PLUS_ASSURANCE_KEY must hold at least 32 charactersLa clé est définie mais trop courte.Générez un secret plus long, ou supprimez la variable.
the ledger <file> is not intact (record N ...)run a trouvé un enregistrement modifié, réordonné ou non authentifié (par exemple écrit avant que la clé soit définie).Gardez le fichier pour examen et enregistrez dans un autre --dir.
another run holds <file>.lock; delete it only if no run is in progressUne exécution est active, ou l’une a été interrompue.Attendez ; si aucune ne tourne, supprimez le fichier de verrou.
check X is not declared in <case>--check nomme un contrôle inconnu.Utilisez un identifiant de la liste checks.
the ledger head must be a record digest of 64 lowercase hexadecimal characters--ledger-head est mal formé.Copiez la tête exactement comme run l’a affichée.
--emit-check writes a .json file / --emit-check must not overwrite the case or its ledgerMauvais nom de sortie.Choisissez un chemin .json qui n’est ni le dossier ni le registre.
<path> passes through a symbolic link / must be a relative path inside the project, written with /Le chemin sort du projet ou traverse un lien.Utilisez un chemin relatif normal avec des séparateurs /.

Raisons qui font sortir verify avec 1

  • 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.

Exemples

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.

Expliquer un verdict non étayé

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.