[148] Le job du tableau de bord : borné, découpé, et toujours bloquant #149

Merged
olivier merged 5 commits from lenaic/148-chaine-tableau-de-bord into develop 2026-09-04 11:59:53 +00:00
Owner

Ferme #148. Reprise après la relecture d'Olivier : ses trois points bloquants
sont traités, et le remède du troisième commandait les deux autres.

Les quatre défauts du ticket

1. L'appel npm n'était pas borné. fetch-timeout vaut 300 000 ms par défaut
et fetch-retries vaut 2, sans rien dans le dépôt pour le figer. D'où les échecs
massés à 433-435 s : une signature de délai avec réessais, pas un travail à durée
variable.

Les bornes entrent dans services/dashboard/.npmrc, pas en option de ligne de
commande : la chaîne, un poste et un conteneur doivent se comporter pareil.

Le pire cas est d'environ 3 min 55 s, pas trois minutes. Trois requêtes de
60 s, plus 5 s et 50 s d'attente entre essais, le facteur de npm valant 10.
J'avais écrit trois minutes dans trois endroits, Olivier l'a relevé, et l'écart
compte parce que tout ce dossier s'argumente sur des durées mesurées.

2. Une seule étape faisait trois choses. Séparées, et l'audit affiche sa
durée. Deux détails corrigés : date plutôt que $SECONDS, que dash ne connaît
pas, et || code=$? sans quoi sh -e sortirait avant la ligne qui l'affiche.

3. Le cache se rabattait sur une clé approchée. Les deux caches coexistent
maintenant : /root/.npm avec repli, parce qu'il est adressé par contenu et
vérifié par empreinte, et node_modules à clé exacte sans repli, parce qu'un
arbre construit pour un autre verrou passe les tests et casse ailleurs.

4. Un apt-get à chaque passage pour un contrôle d'expiration du CA que le
job Python fait déjà, inconditionnellement, dans la même exécution. Au passage,
NODE_EXTRA_CA_CERTS était posé après les commandes npm qui en avaient besoin.

Le cinquième, trouvé en chemin, et mal corrigé du premier coup

L'inventaire ne s'exécutait plus depuis que l'audit échouait. Ma première
correction séparait les étapes, ce qui ne change rien : dans Actions, une étape
en échec interrompt les suivantes. L'inventaire passe désormais avant
l'audit, plutôt qu'un if: always() qui le produirait aussi quand l'installation
a échoué et qu'il n'y a rien à inventorier.

Le verdict sort du YAML, et c'est le coeur de la reprise

Le banc restait vert sur trois désarmements qu'il annonçait refuser :

  • continue-on-error: true sur l'étape d'audit, la neutralisation idiomatique
    d'Actions, dont la clé figure déjà deux fois dans le même job ;
  • restore-keys posé au-dessus de key:, l'ordre des clés d'un mapping YAML
    étant libre alors que mon awk ne balayait que les lignes d'après ;
  • process.exit(0) en gardant v.high dans un affichage : le banc cherchait les
    sous-chaînes, pas leur usage dans la décision de sortie.

Le verdict vit donc dans .forgejo/scripts/verdict-audit-npm.js, et
tests/ci/test-verdict-audit-npm.sh l'exécute contre sept rapports figés :
sain, moderée seule, CVE haute, CVE critique, registre injoignable, rapport
tronqué, rapport sans décompte.

Une assertion sur du texte ne dit rien de ce que le code décide.

--audit-level=high cesse d'être surveillé sur la commande qui produit le JSON :
l'option n'agit que sur le code de sortie de npm, que l'étape jette puisqu'elle
recalcule son verdict. Ma garde protégeait un drapeau sans effet, et faisait
croire protégé ce qui ne l'était pas.

Changement de politique, tranché

Une panne du registre npm ne bloque plus la chaîne. Erreur de registre →
::warning:: et job vert. Une CVE haute ou critique bloque toujours.

C'est au-delà du ticket, donc ça se décide et ne se déduit pas du code. Tranché
le 4 septembre
, après que la panne a bloqué toute l'équipe trois fois dans la
matinée, dont une demande qui ne touchait pas une ligne de JavaScript.

Ce qu'on accepte : une dépendance vulnérable introduite pendant une panne du
registre passe, et n'est rattrapée qu'au passage suivant. Ce qu'on refuse :
immobiliser six personnes parce qu'un service tiers ne répond pas, sans que
personne puisse rien y corriger.

La décision est écrite dans docs/runbooks/ci.md, avec ce qu'elle coûte. Le
message de la chaîne le dit aussi en clair : « Les dépendances du tableau de bord
ne sont PAS contrôlées sur ce passage ».

Un rapport illisible échoue, il ne passe pas pour une panne : un JSON tronqué
ressemble à une panne, et le prendre pour telle laisserait passer une CVE.

Éprouvé

Dix-huit défauts réintroduits un par un, dix-huit rouges, vert sur l'arbre
sain. Le verdict est éprouvé en l'exécutant, le workflow en le lisant.

Deux trous trouvés en éprouvant mes propres correctifs. La garde sur la
délégation trouvait le nom du script dans le commentaire de l'étape, comme
celle sur apt-get avant elle. Et chercher un seul exit 1 ne suffisait pas :
en retirant celui de la branche des vulnérabilités, celui de la branche
« rapport illisible » subsistait et le banc restait vert. Le banc compare
maintenant le nombre d'erreurs annoncées au nombre de sorties en échec, et exige
une branche par défaut.

Correction sur le séquencement avec la #145

J'avais écrit « les deux se fusionnent sans conflit dans un sens ou dans
l'autre ». C'est faux, et Olivier l'a vérifié :

git merge-tree --write-tree <145> <149>
CONFLICT (content): Merge conflict in docs/runbooks/ci.md

ci.yml passe, mais les deux demandes réécrivent le même tableau des tâches du
manuel avec des contenus différents. Ma vérification n'avait porté que sur le
fichier qui, lui, passe. Le conflit se résout en quelques minutes, il faut
simplement le savoir avant de fusionner.

Ce que cette demande ne prouve pas

La durée médiane sous 90 s ne se mesure qu'après fusion, sur dix exécutions. Je
joindrai le relevé avant/après au ticket.

Ferme #148. Reprise après la relecture d'Olivier : ses trois points bloquants sont traités, et le remède du troisième commandait les deux autres. ## Les quatre défauts du ticket **1. L'appel npm n'était pas borné.** `fetch-timeout` vaut 300 000 ms par défaut et `fetch-retries` vaut 2, sans rien dans le dépôt pour le figer. D'où les échecs massés à 433-435 s : une signature de délai avec réessais, pas un travail à durée variable. Les bornes entrent dans `services/dashboard/.npmrc`, pas en option de ligne de commande : la chaîne, un poste et un conteneur doivent se comporter pareil. **Le pire cas est d'environ 3 min 55 s, pas trois minutes.** Trois requêtes de 60 s, plus 5 s et 50 s d'attente entre essais, le facteur de npm valant 10. J'avais écrit trois minutes dans trois endroits, Olivier l'a relevé, et l'écart compte parce que tout ce dossier s'argumente sur des durées mesurées. **2. Une seule étape faisait trois choses.** Séparées, et l'audit affiche sa durée. Deux détails corrigés : `date` plutôt que `$SECONDS`, que dash ne connaît pas, et `|| code=$?` sans quoi `sh -e` sortirait avant la ligne qui l'affiche. **3. Le cache se rabattait sur une clé approchée.** Les deux caches coexistent maintenant : `/root/.npm` avec repli, parce qu'il est adressé par contenu et vérifié par empreinte, et `node_modules` à clé exacte sans repli, parce qu'un arbre construit pour un autre verrou passe les tests et casse ailleurs. **4. Un `apt-get` à chaque passage** pour un contrôle d'expiration du CA que le job Python fait déjà, inconditionnellement, dans la même exécution. Au passage, `NODE_EXTRA_CA_CERTS` était posé après les commandes npm qui en avaient besoin. ## Le cinquième, trouvé en chemin, et mal corrigé du premier coup L'inventaire ne s'exécutait plus depuis que l'audit échouait. Ma première correction séparait les étapes, ce qui ne change rien : dans Actions, une étape en échec interrompt les suivantes. L'inventaire passe désormais **avant** l'audit, plutôt qu'un `if: always()` qui le produirait aussi quand l'installation a échoué et qu'il n'y a rien à inventorier. ## Le verdict sort du YAML, et c'est le coeur de la reprise Le banc restait vert sur trois désarmements qu'il annonçait refuser : - `continue-on-error: true` sur l'étape d'audit, la neutralisation idiomatique d'Actions, dont la clé figure déjà deux fois dans le même job ; - `restore-keys` posé **au-dessus** de `key:`, l'ordre des clés d'un mapping YAML étant libre alors que mon `awk` ne balayait que les lignes d'après ; - `process.exit(0)` en gardant `v.high` dans un affichage : le banc cherchait les sous-chaînes, pas leur usage dans la décision de sortie. Le verdict vit donc dans `.forgejo/scripts/verdict-audit-npm.js`, et `tests/ci/test-verdict-audit-npm.sh` l'**exécute** contre sept rapports figés : sain, moderée seule, CVE haute, CVE critique, registre injoignable, rapport tronqué, rapport sans décompte. **Une assertion sur du texte ne dit rien de ce que le code décide.** `--audit-level=high` cesse d'être surveillé sur la commande qui produit le JSON : l'option n'agit que sur le code de sortie de npm, que l'étape jette puisqu'elle recalcule son verdict. Ma garde protégeait un drapeau sans effet, et faisait croire protégé ce qui ne l'était pas. ## Changement de politique, tranché **Une panne du registre npm ne bloque plus la chaîne.** Erreur de registre → `::warning::` et job vert. Une CVE haute ou critique bloque toujours. C'est au-delà du ticket, donc ça se décide et ne se déduit pas du code. **Tranché le 4 septembre**, après que la panne a bloqué toute l'équipe trois fois dans la matinée, dont une demande qui ne touchait pas une ligne de JavaScript. Ce qu'on accepte : une dépendance vulnérable introduite **pendant** une panne du registre passe, et n'est rattrapée qu'au passage suivant. Ce qu'on refuse : immobiliser six personnes parce qu'un service tiers ne répond pas, sans que personne puisse rien y corriger. La décision est écrite dans `docs/runbooks/ci.md`, avec ce qu'elle coûte. Le message de la chaîne le dit aussi en clair : « Les dépendances du tableau de bord ne sont PAS contrôlées sur ce passage ». Un rapport illisible échoue, il ne passe pas pour une panne : un JSON tronqué ressemble à une panne, et le prendre pour telle laisserait passer une CVE. ## Éprouvé **Dix-huit défauts réintroduits un par un, dix-huit rouges**, vert sur l'arbre sain. Le verdict est éprouvé en l'exécutant, le workflow en le lisant. Deux trous trouvés en éprouvant mes propres correctifs. La garde sur la délégation trouvait le nom du script dans le **commentaire** de l'étape, comme celle sur `apt-get` avant elle. Et chercher un seul `exit 1` ne suffisait pas : en retirant celui de la branche des vulnérabilités, celui de la branche « rapport illisible » subsistait et le banc restait vert. Le banc compare maintenant le nombre d'erreurs annoncées au nombre de sorties en échec, et exige une branche par défaut. ## Correction sur le séquencement avec la #145 J'avais écrit « les deux se fusionnent sans conflit dans un sens ou dans l'autre ». **C'est faux**, et Olivier l'a vérifié : ``` git merge-tree --write-tree <145> <149> CONFLICT (content): Merge conflict in docs/runbooks/ci.md ``` `ci.yml` passe, mais les deux demandes réécrivent le même tableau des tâches du manuel avec des contenus différents. Ma vérification n'avait porté que sur le fichier qui, lui, passe. Le conflit se résout en quelques minutes, il faut simplement le savoir avant de fusionner. ## Ce que cette demande ne prouve pas La durée médiane sous 90 s ne se mesure qu'après fusion, sur dix exécutions. Je joindrai le relevé avant/après au ticket.
lenaic self-assigned this 2026-09-04 09:36:53 +00:00
outillage: borner, découper et garder bloquant le job du tableau de bord
Some checks failed
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 4s
Intégration / Aucun secret commité (pull_request) Successful in 2s
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Failing after 1m45s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 2m11s
52eed13849
Le job échouait cinq fois sur treize, toujours à 433-435 s. Ce n'est pas un
travail à durée variable, c'est la signature d'un délai réseau avec réessais :
npm attend 300 000 ms par tentative et réessaie deux fois, et rien dans le dépôt
ne bornait ça. Un ralentissement de la salle bloquait la fusion de tout le monde
pendant sept minutes, avec un message final qui ne parlait pas de réseau.

Les bornes entrent dans services/dashboard/.npmrc plutôt qu'en option sur une
ligne de commande : la chaîne, un poste et un conteneur doivent se comporter
pareil, sans quoi personne ne reproduit l'incident. 60 s par tentative, deux
réessais, trois minutes au pire contre quinze.

Trois autres défauts vivaient au même endroit.

Une seule étape enchaînait installation, audit et inventaire, donc le journal
n'imputait rien : le coût a longtemps été mis sur le dos de `npm ci`, qui prend
moins d'une seconde. Elles sont séparées, et l'audit affiche sa durée. Le
chronomètre passe par `date` et non `$SECONDS`, que dash ne connaît pas, et le
code de retour est capté par `|| code=$?`, sans quoi `sh -e` sortirait avant la
ligne qui l'affiche.

Le cache portait sur /root/.npm avec un repli `npm-`. Il porte maintenant sur
node_modules, avec une clé exacte et sans repli : un arbre construit pour un
autre verrou passe les tests et casse ailleurs. La clé porte aussi la version de
l'image, un arbre npm contenant des binaires compilés.

Le job installait openssl par apt à chaque passage, pour contrôler l'expiration
du CA local que le job Python contrôle déjà, sur le même fichier, dans la même
exécution. Plus aucun apt-get. Au passage, NODE_EXTRA_CA_CERTS était posé APRÈS
les commandes npm qui en avaient besoin.

Et l'inventaire ne s'exécutait plus du tout depuis que l'audit échouait : la
même étape enchaînait les deux. On perdait la nomenclature sans le remarquer.

`tests/ci/test-chaine-tableau-de-bord.sh` interdit le retour de chacun de ces
défauts. Éprouvé dans les deux sens : vert sur le fichier sain, rouge sur les
sept défauts réintroduits un par un, y compris un `npm audit || true`.

Le contrôle qui compte est le dernier. Borner un appel réseau ne doit pas
devenir « ignorer les vulnérabilités » : `npm audit --audit-level=high` reste
bloquant, et le banc refuse aussi bien sa disparition que sa neutralisation.

docs/runbooks/ci.md décrivait encore les six tâches d'avant le #133. Réaligné
sur quatre, avec un tableau pour distinguer en une ligne un échec d'audit dû à
une CVE d'un échec dû au registre.

Ferme #148
outillage: corriger la relecture de la #149
All checks were successful
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 5s
Intégration / Aucun secret commité (pull_request) Successful in 3s
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 27s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 2m5s
472ec346ef
Trois points bloquants, tous justes.

L'inventaire n'était pas réparé. Le diagnostic était bon, le correctif ne
l'était pas : séparer les étapes ne change rien, une étape en échec interrompt
les suivantes. Placé après l'audit, l'inventaire restait sauté exactement dans
le cas qui nous occupe. Il passe avant, plutôt qu'un `if: always()` qui le
produirait aussi quand l'installation a échoué et qu'il n'y a rien à inventorier.

La demande ne corrigeait pas la panne observée. Un 503 de npmjs mettait toujours
toute l'équipe au rouge, en trois minutes au lieu de sept, et une demande qui ne
touche que du Python restait bloquée sans que personne puisse rien y corriger.
« npm a trouvé une faille » et « npm n'a pas pu joindre son registre » rendaient
le même code de sortie. Le JSON les distingue, une panne portant un champ
`error` : une CVE bloque, une indisponibilité avertit, et le message dit que les
dépendances ne sont PAS contrôlées sur ce passage. Un JSON illisible échoue,
plutôt que de passer pour un registre en panne.

Le cache changeait de nature en mal. Le magasin /root/.npm est adressé par
contenu et vérifié par empreinte, donc un repli y est sûr, alors qu'il ne l'est
pas sur l'arbre installé. En supprimant les deux, tout changement de verrou
repartait chercher 244 paquets sur le registre qui rend justement des 503, et
sur les demandes les plus lentes. Les deux caches coexistent, chacun avec sa
règle.

Le banc gagne quatre contrôles, dont deux qu'il n'aurait pas dû ne pas avoir :
l'ordre des étapes, qui était la cause du cinquième défaut, et le décompte des
vulnérabilités, qui est le verrou réel.

Ce dernier vient d'une injection restée verte. La garde cherchait
`--audit-level=high` n'importe où dans le job, et le trouvait dans le chemin
d'échec alors que la commande décisive l'avait perdu. Elle porte maintenant sur
la ligne qui produit le JSON, et sur le décompte lui-même.

Onze défauts réintroduits un par un, onze rouges, vert sur le fichier sain.

Bornes de lecture insensibles à l'ordre des jobs, lecture du délai qui survit à
un commentaire de fin de ligne, et refus d'un jeton dans le .npmrc versionné,
qui est l'endroit canonique où en atterrit un.
olivier left a comment

Relu sur refs/pull/149/head (472ec34) : banc rejoué, désarmements du garde-fou
éprouvés un par un, séquencement avec la #145 testé par fusion à blanc.

Le diagnostic et les quatre correctifs sont justes. Ce qui reste à traiter tient
presque entièrement au banc, qui laisse passer trois neutralisations qu'il
annonce refuser — et c'est lui qui rend le reste sûr, d'où le blocage.

Ce qui tient

Vérifié plutôt que cru sur parole :

  • Les bornes dans .npmrc plutôt qu'en option de ligne de commande : bon
    arbitrage, et exactement le critère 1.
  • La suppression de l'apt-get est justifiée : le contrôle d'expiration
    existe bien côté Python (ci.yml:274, openssl x509 -checkend 0), et il est
    inconditionnel — aucun des quatre jobs n'a d'if:, de needs: ni de filtre
    paths, donc il tourne toujours dans la même exécution.
  • Le cinquième défaut (inventaire sauté parce que l'audit échouait dans la même
    étape) est une vraie trouvaille, et l'inventaire avant l'audit plutôt qu'un
    if: always() est le bon correctif, pour la bonne raison.
  • NODE_EXTRA_CA_CERTS déplacé avant les appels npm : bug latent réel.
  • Les deux caches, avec le repli seulement sur le magasin adressé par contenu :
    raisonnement correct.
  • date au lieu de $SECONDS, et || code=$? sous sh -e : les deux justes.
  • La protection de branche est bien un joker Intégration / * (confirmé par
    l'API) : l'affirmation du manuel est exacte.
  • Les quatre autres bancs de tests/ci/ passent, et le nouveau est bien câblé
    dans images.

Bloquant — le banc reste vert sur trois désarmements

Chaque cas a été injecté dans ci.yml, banc rejoué, résultat VERT :

a) continue-on-error: true sur l'étape « Audit des dépendances ». C'est
la neutralisation idiomatique d'Actions, et la clé figure déjà deux fois dans
le même job sur les étapes de cache : un copier-coller d'une ligne, le jour où
une fusion presse — exactement le scénario que décrit la demande. Le banc ne
cherche que la présence d'un exit 1, qui subsiste intact.

b) Verdict remplacé par process.exit(0), v.high et v.critical gardés
dans un console.log :

console.log("high=" + v.high + " critical=" + v.critical);
process.exit(0);

Banc vert, CVE plus bloquantes. Le contrôle cherche les deux sous-chaînes
n'importe où dans l'étape, pas leur usage dans la décision de sortie.

c) restore-keys placé au-dessus de key: dans le cache de l'arbre.
L'awk de la section 3 ne balaie que les lignes après key: dashboard-node,
or l'ordre des clés d'un mapping YAML est libre. Le témoin (restore-keys
après key:, ce qui a été éprouvé) rougit bien, lui.

Le contrôle qui rattrape (a) est un grep. Pour (c), il suffit de balayer tout
le bloc with:. Pour (b), il faut que l'assertion porte sur l'expression de
sortie, ce qui suppose d'extraire le verdict dans un fichier exécutable — voir
la dernière section.

Bloquant — --audit-level=high sur la commande décisive n'influence plus rien

--audit-level est documenté comme n'agissant que sur le code de sortie de
npm, code que l'étape jette délibérément par || true puisque le verdict est
recalculé sur le JSON. L'option est donc inerte sur le résultat.

Le « contrôle qui compte » du banc porte donc sur un drapeau sans effet : le
retirer rend le banc rouge sans rien changer au comportement — un faux positif,
pas une régression rattrapée. La garantie réelle est le décompte
v.high/v.critical, c'est-à-dire précisément celle que le point (b)
contourne. Le commentaire (« en la retirant de la commande décisive, le banc
restait vert ») décrit bien le symptôme, mais attribue la garde au mauvais
endroit. L'option garde son sens dans le chemin d'échec, pour réafficher le
rapport en clair.

Bloquant — conflit avec la #145 sur le manuel

La demande conclut « les deux se fusionnent sans conflit ». Pour ci.yml, c'est
vrai. Mais :

git merge-tree --write-tree refs/remotes/pr/145 refs/remotes/pr/149
CONFLICT (content): Merge conflict in docs/runbooks/ci.md

Deux régions, et pas un conflit d'adjacence : les deux demandes réécrivent le
même tableau des tâches avec des contenus différents — la #145 y intègre en plus
les libellés de l'exécuteur et le corollaire « les bancs de tests/ci/ tournent
sans interpréteur Python ». Elle touche ci.md à +120/-30. Le raisonnement de
séquencement n'a porté que sur le fichier qui, lui, passe.

À trancher en équipe — l'audit devient ouvert en cas de panne du registre

Sémantique nouvelle : erreur de registre → ::warning:: et job vert. Le
critère 5 reste tenu (une CVE haute bloque toujours), et le manuel est honnête
là-dessus (« L'avertissement n'est pas un contrôle réussi »). Mais c'est un
changement de politique au-delà du ticket, et la section « Le garde-fou n'est
pas désarmé » de la description ne le mentionne pas : qui ne lit que la
description ne saura pas qu'une fusion faite pendant une panne npmjs part non
auditée. À nommer dans le corps de la demande, et probablement en point du
matin — ça touche la portée pratique d'ENF-11.

Une affirmation à corriger

« Trois minutes au pire » apparaît dans .npmrc, dans la description et
dans le manuel. La demande ajoute fetch-retry-mintimeout=5000 et
fetch-retry-maxtimeout=60000, qui ne sont pas dans le ticket. Avec le facteur
10 par défaut : 3 × 60 s de requêtes + 5 s + 50 s d'attente entre essais, soit
≈ 3 min 55 s. L'écart est modeste, mais toute la demande s'argumente sur des
durées mesurées.

À noter aussi : le banc ignore ces deux réglages et ne borne pas
fetch-retriesgrep -qE '^fetch-retries=' n'en vérifie que la présence, si
bien que fetch-retries=50 resterait vert pour un pire cas d'environ 50 min.

Broutilles

  • Le contrôle fetch-retries est ancré ^fetch-retries= alors que celui de
    fetch-timeout juste au-dessus tolère l'indentation : une ligne indentée
    serait déclarée absente. Incohérent entre deux contrôles voisins.
  • « Aucun apt-get » balaie tout le bloc, commentaires compris : un futur
    commentaire expliquant pourquoi il n'y en a pas rougirait le banc. À
    restreindre aux lignes run:.
  • grep -qE '^\s+exit 1$' : \s est une extension GNU. En conteneur Debian
    c'est bon, mais qui joue le banc sur un poste macOS aura un échec fantôme.

Sur le doublon, et le remède au point (b)

J'avais ouvert une branche sur le même ticket, que je viens de fermer au profit
de celle-ci. Un morceau mérite d'être repris ici, parce qu'il ferme exactement
le trou du point (b) : le verdict y était extrait dans
.forgejo/scripts/verdict-audit-npm.js et exécuté contre cinq fixtures —
saine, CVE haute, CVE critique, point d'accès injoignable, JSON illisible — dont
une sortie réelle de npm audit --json capturée sur npm 10.9.8 en délai
d'attente. Vérifier la logique en la faisant tourner, au lieu de la constater par
sous-chaînes, rend du même coup les points (a) et (b) sans objet et documente la
distinction CVE / réseau par des cas plutôt que par un commentaire. Je peux
pousser ces fixtures et ce banc sur ta branche si tu veux.

Relu sur `refs/pull/149/head` (472ec34) : banc rejoué, désarmements du garde-fou éprouvés un par un, séquencement avec la #145 testé par fusion à blanc. Le diagnostic et les quatre correctifs sont justes. Ce qui reste à traiter tient presque entièrement au **banc**, qui laisse passer trois neutralisations qu'il annonce refuser — et c'est lui qui rend le reste sûr, d'où le blocage. ## Ce qui tient Vérifié plutôt que cru sur parole : - Les bornes dans `.npmrc` plutôt qu'en option de ligne de commande : bon arbitrage, et exactement le critère 1. - **La suppression de l'`apt-get` est justifiée** : le contrôle d'expiration existe bien côté Python (`ci.yml:274`, `openssl x509 -checkend 0`), et il est inconditionnel — aucun des quatre jobs n'a d'`if:`, de `needs:` ni de filtre `paths`, donc il tourne toujours dans la même exécution. - Le cinquième défaut (inventaire sauté parce que l'audit échouait dans la même étape) est une vraie trouvaille, et l'inventaire **avant** l'audit plutôt qu'un `if: always()` est le bon correctif, pour la bonne raison. - `NODE_EXTRA_CA_CERTS` déplacé avant les appels npm : bug latent réel. - Les deux caches, avec le repli seulement sur le magasin adressé par contenu : raisonnement correct. - `date` au lieu de `$SECONDS`, et `|| code=$?` sous `sh -e` : les deux justes. - La protection de branche est bien un joker `Intégration / *` (confirmé par l'API) : l'affirmation du manuel est exacte. - Les quatre autres bancs de `tests/ci/` passent, et le nouveau est bien câblé dans `images`. ## Bloquant — le banc reste vert sur trois désarmements Chaque cas a été injecté dans `ci.yml`, banc rejoué, résultat **VERT** : **a) `continue-on-error: true` sur l'étape « Audit des dépendances ».** C'est *la* neutralisation idiomatique d'Actions, et la clé figure déjà deux fois dans le même job sur les étapes de cache : un copier-coller d'une ligne, le jour où une fusion presse — exactement le scénario que décrit la demande. Le banc ne cherche que la présence d'un `exit 1`, qui subsiste intact. **b) Verdict remplacé par `process.exit(0)`, `v.high` et `v.critical` gardés dans un `console.log` :** ```js console.log("high=" + v.high + " critical=" + v.critical); process.exit(0); ``` Banc vert, CVE plus bloquantes. Le contrôle cherche les deux sous-chaînes *n'importe où* dans l'étape, pas leur usage dans la décision de sortie. **c) `restore-keys` placé au-dessus de `key:`** dans le cache de l'arbre. L'`awk` de la section 3 ne balaie que les lignes *après* `key: dashboard-node`, or l'ordre des clés d'un mapping YAML est libre. Le témoin (`restore-keys` après `key:`, ce qui a été éprouvé) rougit bien, lui. Le contrôle qui rattrape (a) est un `grep`. Pour (c), il suffit de balayer tout le bloc `with:`. Pour (b), il faut que l'assertion porte sur l'expression de sortie, ce qui suppose d'extraire le verdict dans un fichier exécutable — voir la dernière section. ## Bloquant — `--audit-level=high` sur la commande décisive n'influence plus rien `--audit-level` est documenté comme n'agissant **que sur le code de sortie** de npm, code que l'étape jette délibérément par `|| true` puisque le verdict est recalculé sur le JSON. L'option est donc inerte sur le résultat. Le « contrôle qui compte » du banc porte donc sur un drapeau sans effet : le retirer rend le banc rouge sans rien changer au comportement — un faux positif, pas une régression rattrapée. La garantie réelle est le décompte `v.high`/`v.critical`, c'est-à-dire précisément celle que le point (b) contourne. Le commentaire (« en la retirant de la commande décisive, le banc restait vert ») décrit bien le symptôme, mais attribue la garde au mauvais endroit. L'option garde son sens dans le chemin d'échec, pour réafficher le rapport en clair. ## Bloquant — conflit avec la #145 sur le manuel La demande conclut « les deux se fusionnent sans conflit ». Pour `ci.yml`, c'est vrai. Mais : ``` git merge-tree --write-tree refs/remotes/pr/145 refs/remotes/pr/149 CONFLICT (content): Merge conflict in docs/runbooks/ci.md ``` Deux régions, et pas un conflit d'adjacence : les deux demandes réécrivent le même tableau des tâches avec des contenus différents — la #145 y intègre en plus les libellés de l'exécuteur et le corollaire « les bancs de `tests/ci/` tournent sans interpréteur Python ». Elle touche `ci.md` à +120/-30. Le raisonnement de séquencement n'a porté que sur le fichier qui, lui, passe. ## À trancher en équipe — l'audit devient ouvert en cas de panne du registre Sémantique nouvelle : erreur de registre → `::warning::` et job **vert**. Le critère 5 reste tenu (une CVE haute bloque toujours), et le manuel est honnête là-dessus (« L'avertissement n'est pas un contrôle réussi »). Mais c'est un changement de politique au-delà du ticket, et la section « Le garde-fou n'est pas désarmé » de la description ne le mentionne pas : qui ne lit que la description ne saura pas qu'une fusion faite pendant une panne npmjs part non auditée. À nommer dans le corps de la demande, et probablement en point du matin — ça touche la portée pratique d'ENF-11. ## Une affirmation à corriger **« Trois minutes au pire »** apparaît dans `.npmrc`, dans la description et dans le manuel. La demande ajoute `fetch-retry-mintimeout=5000` et `fetch-retry-maxtimeout=60000`, qui ne sont pas dans le ticket. Avec le facteur 10 par défaut : 3 × 60 s de requêtes + 5 s + 50 s d'attente entre essais, soit **≈ 3 min 55 s**. L'écart est modeste, mais toute la demande s'argumente sur des durées mesurées. À noter aussi : le banc ignore ces deux réglages et ne borne pas `fetch-retries` — `grep -qE '^fetch-retries='` n'en vérifie que la présence, si bien que `fetch-retries=50` resterait vert pour un pire cas d'environ 50 min. ## Broutilles - Le contrôle `fetch-retries` est ancré `^fetch-retries=` alors que celui de `fetch-timeout` juste au-dessus tolère l'indentation : une ligne indentée serait déclarée absente. Incohérent entre deux contrôles voisins. - « Aucun `apt-get` » balaie tout le bloc, **commentaires compris** : un futur commentaire expliquant pourquoi il n'y en a pas rougirait le banc. À restreindre aux lignes `run:`. - `grep -qE '^\s+exit 1$'` : `\s` est une extension GNU. En conteneur Debian c'est bon, mais qui joue le banc sur un poste macOS aura un échec fantôme. ## Sur le doublon, et le remède au point (b) J'avais ouvert une branche sur le même ticket, que je viens de fermer au profit de celle-ci. Un morceau mérite d'être repris ici, parce qu'il ferme exactement le trou du point (b) : le verdict y était extrait dans `.forgejo/scripts/verdict-audit-npm.js` et **exécuté** contre cinq fixtures — saine, CVE haute, CVE critique, point d'accès injoignable, JSON illisible — dont une sortie réelle de `npm audit --json` capturée sur npm 10.9.8 en délai d'attente. Vérifier la logique en la faisant tourner, au lieu de la constater par sous-chaînes, rend du même coup les points (a) et (b) sans objet et documente la distinction CVE / réseau par des cas plutôt que par un commentaire. Je peux pousser ces fixtures et ce banc sur ta branche si tu veux.
outillage: le verdict d'audit sort du YAML et devient éprouvable
All checks were successful
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 6s
Intégration / Aucun secret commité (pull_request) Successful in 4s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 2m6s
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 2m20s
0f32ffd938
Relecture d'Olivier sur la #149. Trois points bloquants, tous justes, et le
remède du troisième commandait les deux autres.

Le banc restait vert sur trois désarmements qu'il annonçait refuser. Un
`continue-on-error: true` sur l'étape d'audit, qui est la neutralisation
idiomatique d'Actions et dont la clé figure déjà deux fois dans le même job. Un
`restore-keys` posé au-dessus de `key:`, l'ordre des clés d'un mapping YAML
étant libre alors que mon awk ne balayait que les lignes d'après. Et surtout un
`process.exit(0)` gardant `v.high` dans un affichage : le banc cherchait les
sous-chaînes, pas leur usage dans la décision.

Le verdict quitte donc le workflow pour .forgejo/scripts/verdict-audit-npm.js,
et tests/ci/test-verdict-audit-npm.sh l'EXÉCUTE contre sept rapports figés :
sain, moderée seule, CVE haute, CVE critique, registre injoignable, rapport
tronqué, rapport sans décompte. Une assertion sur du texte ne dit rien de ce que
le code décide.

`--audit-level=high` cesse d'être surveillé sur la commande qui produit le JSON.
L'option n'agit que sur le code de sortie de npm, code que l'étape jette
puisqu'elle recalcule son verdict : la garde protégeait un drapeau sans effet, et
faisait croire protégé ce qui ne l'était pas. Elle reste dans le chemin d'échec,
où elle sert à réafficher le rapport en clair.

Le pire cas n'est pas trois minutes mais environ 3 min 55 s : trois requêtes de
60 s plus 5 s et 50 s d'attente entre essais, le facteur de npm valant 10.
Corrigé dans le .npmrc et dans le manuel. Le banc borne maintenant
`fetch-retries`, dont il ne vérifiait que la présence : `fetch-retries=50` le
laissait vert pour un pire cas d'environ cinquante minutes.

Deux trous trouvés en éprouvant les correctifs eux-mêmes. La garde sur la
délégation trouvait le nom du script dans le commentaire de l'étape, comme celle
sur apt-get avant elle. Et chercher un seul « exit 1 » ne suffisait pas : en
retirant celui de la branche des vulnérabilités, celui de la branche « rapport
illisible » subsistait. Le banc compare maintenant le nombre d'erreurs annoncées
au nombre de sorties en échec, et exige une branche par défaut.

Dix-huit défauts réintroduits un par un, dix-huit rouges, vert sur l'arbre sain.

Broutilles reprises : `[[:space:]]` au lieu de `\s`, ancre de fetch-retries
alignée sur celle de fetch-timeout, apt-get cherché hors des commentaires.
lenaic requested review from olivier 2026-09-04 11:22:50 +00:00
docs: inscrire la décision sur l'audit npm en panne de registre
All checks were successful
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 1m18s
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 4s
Intégration / Aucun secret commité (pull_request) Successful in 3s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 2m3s
b755aef111
Tranché le 4 septembre : la chaîne passe au vert avec un avertissement quand le
registre npm ne répond pas, plutôt que de bloquer toute l'équipe. Une vraie
faille bloque toujours.

Ce qu'on accepte est écrit : une dépendance vulnérable introduite pendant une
panne passe, et n'est rattrapée qu'au passage suivant. Une décision qui ne vit
que dans le code n'est pas une décision.
Author
Owner

Trois points bloquants, trois corrigés, et le remède du troisième commandait les
deux autres.

Le verdict vit maintenant dans .forgejo/scripts/verdict-audit-npm.js, et
tests/ci/test-verdict-audit-npm.sh l'exécute contre sept rapports figés :
sain, moderée seule, CVE haute, CVE critique, registre injoignable, rapport
tronqué, rapport sans décompte. Ta démonstration du process.exit(0) avec
v.high gardé en affichage était imparable : une assertion sur du texte ne dit
rien de ce que le code décide.

Sur --audit-level=high, tu as raison et j'avais tort. L'option n'agit que
sur le code de sortie de npm, que l'étape jette puisqu'elle recalcule son
verdict. Ma garde protégeait un drapeau sans effet, et me faisait croire protégé
ce qui ne l'était pas. Je l'ai supprimée, pas déplacée.

Le continue-on-error et le restore-keys au-dessus de key: sont couverts,
tous deux éprouvés rouges.

Deux trous trouvés en éprouvant mes propres correctifs

La garde sur la délégation trouvait le nom du script dans le commentaire de
l'étape, comme celle sur apt-get avant elle. Et chercher un seul exit 1 ne
suffisait pas : en retirant celui de la branche des vulnérabilités, celui de la
branche « rapport illisible » subsistait et le banc restait vert. Il compare
maintenant les erreurs annoncées aux sorties en échec, et exige une branche par
défaut.

Dix-huit défauts réintroduits un par un, dix-huit rouges, vert sur l'arbre sain.

Le chiffre était faux

3 min 55 s et non trois minutes, corrigé aux trois endroits. Et le banc borne
désormais fetch-retries, dont il ne vérifiait que la présence :
fetch-retries=50 passait, pour un pire cas d'environ cinquante minutes.

Le conflit

Réel, vérifié, et la description est corrigée : ma vérification n'avait porté que
sur ci.yml. Il a disparu depuis, la #145 ne réécrivant plus le tableau des
tâches du manuel. Ça répond du même coup au point de périmètre de Gabriel sur
l'autre demande.

La politique

Tranchée, dans le sens de l'ouverture. Une panne du registre avertit sans
bloquer, une CVE bloque toujours. C'est écrit dans docs/runbooks/ci.md avec ce
que ça coûte : une dépendance vulnérable introduite pendant une panne passe, et
n'est rattrapée qu'au passage suivant.

Merci pour tes fixtures. Elles n'ont plus lieu d'être puisque le banc les porte,
mais l'idée d'éprouver en exécutant vient de toi, et c'est elle qui a fermé le
trou.

Trois points bloquants, trois corrigés, et le remède du troisième commandait les deux autres. Le verdict vit maintenant dans `.forgejo/scripts/verdict-audit-npm.js`, et `tests/ci/test-verdict-audit-npm.sh` l'**exécute** contre sept rapports figés : sain, moderée seule, CVE haute, CVE critique, registre injoignable, rapport tronqué, rapport sans décompte. Ta démonstration du `process.exit(0)` avec `v.high` gardé en affichage était imparable : une assertion sur du texte ne dit rien de ce que le code décide. **Sur `--audit-level=high`, tu as raison et j'avais tort.** L'option n'agit que sur le code de sortie de npm, que l'étape jette puisqu'elle recalcule son verdict. Ma garde protégeait un drapeau sans effet, et me faisait croire protégé ce qui ne l'était pas. Je l'ai supprimée, pas déplacée. Le `continue-on-error` et le `restore-keys` au-dessus de `key:` sont couverts, tous deux éprouvés rouges. ### Deux trous trouvés en éprouvant mes propres correctifs La garde sur la délégation trouvait le nom du script dans le **commentaire** de l'étape, comme celle sur `apt-get` avant elle. Et chercher un seul `exit 1` ne suffisait pas : en retirant celui de la branche des vulnérabilités, celui de la branche « rapport illisible » subsistait et le banc restait vert. Il compare maintenant les erreurs annoncées aux sorties en échec, et exige une branche par défaut. Dix-huit défauts réintroduits un par un, dix-huit rouges, vert sur l'arbre sain. ### Le chiffre était faux 3 min 55 s et non trois minutes, corrigé aux trois endroits. Et le banc borne désormais `fetch-retries`, dont il ne vérifiait que la présence : `fetch-retries=50` passait, pour un pire cas d'environ cinquante minutes. ### Le conflit Réel, vérifié, et la description est corrigée : ma vérification n'avait porté que sur `ci.yml`. Il a disparu depuis, la #145 ne réécrivant plus le tableau des tâches du manuel. Ça répond du même coup au point de périmètre de Gabriel sur l'autre demande. ### La politique **Tranchée, dans le sens de l'ouverture.** Une panne du registre avertit sans bloquer, une CVE bloque toujours. C'est écrit dans `docs/runbooks/ci.md` avec ce que ça coûte : une dépendance vulnérable introduite pendant une panne passe, et n'est rattrapée qu'au passage suivant. Merci pour tes fixtures. Elles n'ont plus lieu d'être puisque le banc les porte, mais l'idée d'éprouver en exécutant vient de toi, et c'est elle qui a fermé le trou.
outillage: le banc garde la condition et le fil du verdict de l'audit
All checks were successful
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 2m6s
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 5s
Intégration / Aucun secret commité (pull_request) Successful in 3s
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 2m18s
1198fb73c8
Deux désarmements passaient encore au vert sur les deux bancs, relecture de
la #149.

UNE ÉTAPE SAUTÉE EST UNE ÉTAPE VERTE. Rien ne contrôlait la condition de
l'étape d'audit : `if: false`, `== 'non'` au lieu de `== 'oui'`, et surtout
un `&& github.ref == 'refs/heads/develop'` ajouté à la condition laissaient
les bancs verts en supprimant l'audit. Le troisième est le plus vraisemblable
— « on n'audite que sur develop pour accélérer les demandes » — et il retire
l'audit précisément là où il sert, sur les demandes de fusion. La condition
doit maintenant être exactement celle des autres étapes du tableau de bord.

LE FIL ENTRE LE VERDICT ET L'ÉCHEC DE L'ÉTAPE. Éprouver ce que le script
décide ne sert à rien si sa décision n'est pas branchée : `|| true` au lieu
de `|| verdict=$?`, ou une réaffectation de `verdict` après la capture,
rendaient l'étape verte avec une CVE haute, script et fixtures intacts.
C'est le `|| true` contre lequel tout le fichier met en garde, remonté d'un
cran.

Huit défauts réintroduits un par un, huit rouges ; les six contrôles déjà en
place restent rouges sur leurs propres injections, et l'arbre sain reste vert.

Au passage, quatrième occurrence du même piège : la première écriture de la
garde plaçait sa fenêtre d'analyse sur le commentaire qui nomme le script, en
amont de l'initialisation `verdict=0`, et rougissait l'arbre sain. Les deux
nouvelles gardes ne lisent que les lignes exécutées.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
olivier approved these changes 2026-09-04 11:55:42 +00:00
olivier left a comment

Relu sur b755aef. Les trois points bloquants sont levés, et j'ai vérifié
chacun en réinjectant le défaut :

Injection Résultat
continue-on-error: true sur l'étape d'audit rouge
restore-keys au-dessus de key: rouge
process.exit(0) au lieu de bloquantes > 0 rouge (banc verdict)
exit 1 retiré de la seule branche vulnérabilités rouge
cas par défaut *) supprimé rouge
registre injoignable : exit(3)exit(0) rouge (banc verdict)
fetch-retries=50 rouge
apt-get dans un commentaire reste vert — le faux positif est parti

Le conflit avec la #145 a bien disparu (merge-tree propre contre pr/145 et
contre develop), le 3 min 55 s est corrigé aux trois endroits, et le banc
verdict échoue franchement si node manque au lieu de se sauter en silence —
comme images utilise actions/checkout, node y est de toute façon garanti.
Les six bancs sont verts.

Sortir le verdict du YAML pour l'éprouver par exécution était la bonne réponse,
et le cas sans-decompte.json → 2 ferme un trou que je n'avais pas soulevé :
conclure « aucune vulnérabilité » d'une absence de décompte aurait été le pire
des deux.

J'ai poussé un commit sur ta branche : 1198fb7

Deux désarmements passaient encore au vert sur les deux bancs. Comme ce sont
deux gardes de quelques lignes et qu'elles relèvent de la même famille que les
précédentes, je les ai écrites plutôt que de te renvoyer un troisième tour —
mais dis-le si tu préfères les réécrire, le commit se défait sans peine.

A. Une étape sautée est une étape verte. Rien ne contrôlait la condition de
l'étape d'audit. Les trois écritures suivantes laissaient les deux bancs verts
en supprimant l'audit :

if: false
if: steps.scan_dashboard.outputs.trouve == 'non'
if: steps.scan_dashboard.outputs.trouve == 'oui' && github.ref == 'refs/heads/develop'

La troisième est la plus vraisemblable — « on n'audite que sur develop pour
accélérer les demandes » — et elle retire l'audit précisément là où il sert,
sur les demandes de fusion. La condition est désormais comparée exactement à
celle des autres étapes du tableau de bord.

B. Le fil entre le verdict et l'échec de l'étape. Éprouver ce que le script
décide ne sert à rien si sa décision n'est pas branchée :

node ../../.forgejo/scripts/verdict-audit-npm.js audit.json || true

verdict reste à 0, on tombe dans 0), l'étape annonce « aucune vulnérabilité
high ou critical » et sort verte avec une CVE haute — script et fixtures
intacts, banc verdict vert. Idem en réaffectant verdict après la capture. Le
banc exige maintenant la capture, et l'absence de réaffectation avant le case.

C'est le || true contre lequel tout ce fichier met en garde, remonté d'un
cran : de npm audit || true à verdict-audit-npm.js || true. Ça vaut peut-être
une ligne dans le manuel : la garde se déplace avec la logique.

Huit défauts réintroduits un par un, huit rouges ; les six contrôles déjà en
place restent rouges sur leurs propres injections, et l'arbre sain reste vert.

Et une quatrième occurrence du même piège, sur moi cette fois : ma première
écriture de la garde B plaçait sa fenêtre d'analyse sur le commentaire qui nomme
le script, donc en amont de l'initialisation verdict=0, qu'elle prenait pour
un écrasement — arbre sain rouge. Les deux nouvelles gardes ne lisent que les
lignes exécutées, comme celles sur apt-get et sur la délégation.

Approuvé. Je fusionne dès que la chaîne est verte sur 1198fb7.

Relu sur `b755aef`. Les trois points bloquants sont levés, et j'ai vérifié chacun en réinjectant le défaut : | Injection | Résultat | |---|---| | `continue-on-error: true` sur l'étape d'audit | **rouge** | | `restore-keys` au-dessus de `key:` | **rouge** | | `process.exit(0)` au lieu de `bloquantes > 0` | **rouge** (banc verdict) | | `exit 1` retiré de la seule branche vulnérabilités | **rouge** | | cas par défaut `*)` supprimé | **rouge** | | registre injoignable : `exit(3)` → `exit(0)` | **rouge** (banc verdict) | | `fetch-retries=50` | **rouge** | | `apt-get` dans un commentaire | reste **vert** — le faux positif est parti | Le conflit avec la #145 a bien disparu (`merge-tree` propre contre `pr/145` et contre `develop`), le 3 min 55 s est corrigé aux trois endroits, et le banc verdict échoue franchement si `node` manque au lieu de se sauter en silence — comme `images` utilise `actions/checkout`, node y est de toute façon garanti. Les six bancs sont verts. Sortir le verdict du YAML pour l'éprouver par exécution était la bonne réponse, et le cas `sans-decompte.json → 2` ferme un trou que je n'avais pas soulevé : conclure « aucune vulnérabilité » d'une absence de décompte aurait été le pire des deux. ## J'ai poussé un commit sur ta branche : 1198fb7 Deux désarmements passaient encore au vert sur les deux bancs. Comme ce sont deux gardes de quelques lignes et qu'elles relèvent de la même famille que les précédentes, je les ai écrites plutôt que de te renvoyer un troisième tour — mais dis-le si tu préfères les réécrire, le commit se défait sans peine. **A. Une étape sautée est une étape verte.** Rien ne contrôlait la condition de l'étape d'audit. Les trois écritures suivantes laissaient les deux bancs verts en supprimant l'audit : ```yaml if: false if: steps.scan_dashboard.outputs.trouve == 'non' if: steps.scan_dashboard.outputs.trouve == 'oui' && github.ref == 'refs/heads/develop' ``` La troisième est la plus vraisemblable — « on n'audite que sur develop pour accélérer les demandes » — et elle retire l'audit précisément là où il sert, sur les demandes de fusion. La condition est désormais comparée exactement à celle des autres étapes du tableau de bord. **B. Le fil entre le verdict et l'échec de l'étape.** Éprouver ce que le script décide ne sert à rien si sa décision n'est pas branchée : ```sh node ../../.forgejo/scripts/verdict-audit-npm.js audit.json || true ``` `verdict` reste à 0, on tombe dans `0)`, l'étape annonce « aucune vulnérabilité high ou critical » et sort verte avec une CVE haute — script et fixtures intacts, banc verdict vert. Idem en réaffectant `verdict` après la capture. Le banc exige maintenant la capture, et l'absence de réaffectation avant le `case`. C'est le `|| true` contre lequel tout ce fichier met en garde, remonté d'un cran : de `npm audit || true` à `verdict-audit-npm.js || true`. Ça vaut peut-être une ligne dans le manuel : la garde se déplace avec la logique. Huit défauts réintroduits un par un, huit rouges ; les six contrôles déjà en place restent rouges sur leurs propres injections, et l'arbre sain reste vert. **Et une quatrième occurrence du même piège, sur moi cette fois** : ma première écriture de la garde B plaçait sa fenêtre d'analyse sur le commentaire qui nomme le script, donc en amont de l'initialisation `verdict=0`, qu'elle prenait pour un écrasement — arbre sain rouge. Les deux nouvelles gardes ne lisent que les lignes exécutées, comme celles sur `apt-get` et sur la délégation. Approuvé. Je fusionne dès que la chaîne est verte sur `1198fb7`.
olivier merged commit 75b6b94402 into develop 2026-09-04 11:59:53 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
g2/enervision!149
No description provided.