Durcissement avant l'ouverture du depot en public - #18
Merged
Merged
Conversation
Un tag d'action GitHub n'est pas une version, c'est un pointeur : son proprietaire le deplace quand il veut, et une action deplacee s'execute dans nos runners avec ce que porte son depot a cet instant-la. Le depot part en public, et c'est le chemin d'attaque le moins couteux qui existe contre une chaine de construction : personne n'a besoin d'un acces a ce depot-ci pour l'emprunter. softprops/action-gh-release est le cas qui compte. C'est une action TIERCE, et elle tourne dans le seul job du depot qui recoive contents: write et le github.token -- celui qui publie la page Releases dont les postes tirent ensuite leur mise a jour. Un v2 deplace publie sous le nom de la cooperative des octets que internal/update installera en LocalSystem, et le condensat ne dirait rien : SHA256SUMS-archives.txt est produit par le meme workflow. Les quatre actions sont donc epinglees sur un SHA de commit, qui designe des octets qui ne changent plus. Le tag lisible reste en commentaire au bout de la ligne : il est la pour l'humain qui relit, et c'est la seule chose que le SHA ne dit pas. Les majeures ne bougent pas -- setup-node reste en v6 et action-gh-release en v2 -- parce qu'epingler et monter de version sont deux gestes, et qu'un seul etait demande. dependabot.yml arrive DANS LE MEME COMMIT, et ce n'est pas un ajout de confort. Epingler protege la chaine et fige aussi les correctifs des actions : sans une entree github-actions qui fait monter le SHA et son commentaire ensemble, l'epinglage devient une dette que personne ne relit. Les deux tiennent ensemble ou aucun des deux ne tient. La cadence est mensuelle et les demandes sont groupees par ecosysteme. Une cooperative n'a pas de mainteneur a temps plein : une demande par dependance et par semaine, c'est un depot ou l'on cesse de regarder les demandes, donc un depot ou la mise a jour de securite passe inapercue avec les autres. L'entree npm porte un avertissement pour qui relira ces demandes. internal/web/dist est commite et le job front compare ses OCTETS au resultat de npm ci && npm run build : une montee de vite ou de svelte change les empreintes des noms de fichiers, donc l'etape « internal/web/dist est a jour » devient rouge sur une demande par ailleurs correcte. La reponse est celle que le workflow ecrit deja -- lancer make front et committer le dist dans la demande -- jamais retirer la garde, qui a rattrape deux livraisons d'un ecran perime.
Quatre en-tetes, poses par guard -- le middleware le plus externe -- avant toute decision et avant tout handler. Le refus est le cas qui decide de l'endroit : un 403 de l'Origine est une reponse comme une autre, et des en-tetes poses apres avoir tranche laisseraient nue la seule reponse qu'un attaquant obtient. frame-ancestors 'none' est la directive qui compte ici, et c'est la seule qui ferme un ACTE plutot qu'un chargement. Les gestes de depannage de §14.4 repondent sans mot de passe par decision (ADR-033), donc une page ouverte sur le poste pouvait encadrer /admin, le recouvrir, et faire cliquer « Rouleau change » ou un auto-test a un benevole qui croyait cliquer ailleurs. X-Frame-Options: DENY suit, redondant sur tout navigateur qu'un poste fait tourner, garde parce qu'il coute une ligne et couvre le navigateur que personne n'a choisi. nosniff est l'autre moitie d'un controle qui existait deja. La route des images refuse de servir un PNG sous un nom en .jpg (§10.7) -- dix des 181 photos reelles sont dans ce cas -- et un navigateur qui ignorerait le type declare pour renifler les octets defaisait ce controle depuis l'exterieur. script-src 'self' est verifiable sur les fichiers construits et l'a ete : les bundles ne contiennent ni eval, ni new Function, ni insertRule, et les deux pages d'entree ne portent aucun script en ligne. img-src 'self' sans data: est deliberé : le CSV porte ses images en base64, mais l'import les decode et les ecrit par adresse de contenu, donc un data: sur cet ecran signalerait un chemin qui a saute cette etape. style-src garde 'unsafe-inline', et c'est une decision et non un oubli. Le CSS en ligne n'execute rien, et le retirer couterait la page de secours d'un binaire construit sans front, qui porte sa mise en forme dans un attribut style. Une politique qui casse l'ecran le jour ou vite change sa facon d'emettre les styles est une politique qu'on supprime a 7 h du matin au lieu de la reparer. Pas de HSTS et pas d'upgrade-insecure-requests : le service parle HTTP en clair sur la boucle locale, et les deux epingleraient ou reecriraient une adresse qu'un benevole ne pourrait alors plus atteindre du tout. LA POLITIQUE A ETE VERIFIEE SUR LES DEUX ECRANS REELS, pas seulement en test. Poste de demonstration lance, catalogue depose, les deux ecrans ouverts dans un navigateur : zero message de console, grille et photos affichees, police chargee, flux SSE vivant. Le seul motif douteux a ete exerce nommement -- handToBrowser (Station.svelte) cree un blob: par createObjectURL pour l'export de configuration, ce qu'aucune directive CSP ne couvre : le telechargement passe, sans violation. Deux tests gardent le reste, et le second refuse qu'on elargisse script-src : c'est la ligne qu'on relache pour faire remarcher un ecran.
…e partait en clair La regle de redirection de §10.1 refusait tout saut hors de l'hote declare, et elle ne regardait que l'HOTE. Or net/http conserve l'en-tete Authorization sur une redirection vers le MEME hote -- ce que le controle laissait passer par construction -- si bien qu'un partage repondant « 302 http://meme-hote/... » a une requete https mettait username:password sur le fil, en clair, sur un reseau que quelqu'un croyait protege par TLS. Le trou ressemble a un controle ferme, et c'est ce qui le rend interessant : la verification existait, elle etait juste, et elle ne couvrait pas ce cas. Rien dans le symptome ne l'aurait dit non plus -- le catalogue arrive, le poste le sert, la pastille est verte. Aucun bruit, et le compte du producteur lisible par qui ecoute le reseau du magasin. newClient recoit donc le SCHEMA en plus de l'hote, tous deux tels que catalog.options.url les declare, et refuse un saut de https vers autre chose. Il les recoit plutot que de lire via[0] parce que la valeur declaree est deja ce que cette fonction prend en argument pour l'hote : une regle qui compare a ce qu'un exploitant a tape ne depend alors d'aucun contrat de net/http. Le schema declare est un PLANCHER et non une preference : http vers https reste suivi, et le test le verifie explicitement. Refuser une montee en TLS aurait casse un partage mal configure sans rien proteger, et le sens de la regle est que le compte ne descende jamais, pas que l'adresse ne bouge jamais. Le test exerce la regle a travers le CheckRedirect du client et non a travers un serveur : reproduire le saut de bout en bout demanderait un serveur TLS de test et son certificat auto-signe injecte dans un transport que ce paquet ne laisse deliberement personne configurer -- beaucoup d'echafaudage pour observer une comparaison. Il a ete verifie qu'il tombe sans le correctif.
…nt fermees
« It is NOT authenticated, and it is the one route of ADR-018 where that deserves
a sentence » : c'est ce que dit encore importCatalog, et c'est faux depuis
qu'ADR-033 a deplace le critere. La table des routes de server.go garde
manual-entry et catalog/import derriere le mot de passe, et un test de
session_test.go exige le 401 sur les deux -- les commentaires contredisaient donc
un test qui passe.
C'est un defaut plus ennuyeux qu'une faute de frappe. Un relecteur qui decide sur
la foi d'un commentaire -- « cette route est ouverte, donc le garde n'a rien a
voir avec elle » -- se trompe, et il se trompe sur la seule question que le
fichier existe pour trancher. Quatre endroits le disaient :
- importCatalog et manualEntry, dans internal/web/troubleshooting.go ;
- l'interface Troubleshooting de server.go, « the unauthenticated buttons of
§14.4 », alors qu'un de ses trois membres ne l'est plus ;
- adminTroubleshooting de cmd/openscale/admin.go, « the three unauthenticated
switches », le meme mensonge un etage plus bas.
Les quatre disent maintenant lequel des trois est ferme, et surtout POURQUOI le
raisonnement d'ADR-018 ne suffisait plus : « n'ecrit pas la configuration »
n'a jamais ete ce qui rendait ces gestes anodins. La saisie manuelle coupe la
balance et laisse le CLIENT taper son propre poids ; un depot de catalogue
remplace toute la grille par un fichier que quelqu'un a apporte. Les deux
laissent leur trace a la caisse, et c'est plus lourd que ce que le mot de passe
gardait avant eux.
Ce qui reste vrai est garde : les gardes de §10.4 protegent le catalogue en
service, pas un mot de passe. Un mot de passe ne rend pas sur un export ampute,
il nomme seulement qui l'a depose.
Aucun changement de comportement -- quatre commentaires, et la table des routes
reste seule a decider.
Un depot public est lu par d'autres yeux que ceux d'une cooperative, et il recoit des rapports. SECURITY.md dit ou signaler -- le formulaire prive de GitHub, jamais une issue publique -- et ce qu'une equipe benevole peut promettre : accuse de reception sous sept jours, pas de programme de recompense, et des correctifs sur la derniere version publiee seulement, parce qu'il n'y a pas de branche de maintenance pour un parc de quatre postes. Le reste du fichier est le MODELE DE MENACE ASSUME, et c'est la sa valeur. L'acces physique vaut l'acces administrateur (§15.2), les gestes de depannage repondent sans mot de passe par decision (ADR-033), le service parle HTTP en clair sur la boucle locale, le symbole EAN-13 de l'etiquette est tronque par compromis de geometrie, et les mises a jour sont verifiees par condensat et non signees. Sans ces paragraphes, on repond trois fois au meme rapport ; avec eux, celui qui allait l'ecrire trouve la reponse avant. Il dit aussi ce qui EST dans le perimetre, parce qu'une liste de non-failles sans son complement decourage les rapports utiles : tout ce qui s'exploite sans etre devant le poste -- depuis une page ouverte sur la machine, depuis le reseau du magasin, depuis un CSV que le producteur depose, depuis une reponse du partage, depuis une release -- et l'elevation du compte du kiosque vers LocalSystem. L'entree de SUIVI.md porte les quatre correctifs de cette branche ET LES QUATRE ECARTS LAISSES, avec le raisonnement de chacun. C'est cette seconde liste qui justifie l'entree : l'hote reel qui subsiste dans neuf commits de l'historique, la fiche d'installation en clair sur le poste, l'ACL du compte du kiosque sur data\updates, l'URL WebDAV publiee par /admin/api/health, et l'export CSV qui n'echappe pas ses formules. Ils ont ete peses -- poste peu sensible, reseau d'une cooperative, benevoles -- et non ignores. Ecrits nulle part, ils seraient re-signales dans six mois, ou « corriges » par quelqu'un qui ne saurait pas qu'ils ont ete decides. Deux choses y sont dites honnetement plutot que passees. Une execution de la suite complete sur douze a signale un echec jamais reproduit ensuite, et la commande de comptage qui l'a vu n'en a pas garde le nom ; ce n'est pas imputable a cette branche -- les trois tests nouveaux ont tenu cinquante executions chacun, et internal/web dix passes sous -race. Et le compteur d'en-tete n'est PAS remesure ici : la methode qui a produit « 2 572 » n'est pas celle qui donne 2 573 PASS et 7 SKIP sur une passe verte, et remplacer un chiffre par l'autre aurait pretendu a une continuite fausse. Le fichier « $(readlink », vide et suivi a la racine, part avec ce commit. C'est le residu d'une commande shell ratee, et c'est la premiere ligne que voit qui ouvre le depot.
lostmind84
force-pushed
the
security/durcissement-avant-ouverture-publique
branch
from
July 30, 2026 10:13
99a5a3d to
c6f4e4d
Compare
…aque commit
Le depot part en public sous AGPL-3.0 et il est destine a circuler entre
cooperatives. Rien n'y a a renvoyer vers une conversation privee, et le plan de la
politique de dependances faisait exactement le contraire : sa consigne de style
demandait de terminer chaque message par un lien de session d'assistant, et ses
sept messages d'exemple le portaient pour montrer comment faire.
C'est la SOURCE et pas seulement une occurrence. Une consigne ecrite dans un plan
du depot est lue et appliquee par qui reprend le travail : les 62 commits de main
qui portent un tel lien viennent de la, et sans ce commit le motif se
reproduirait au plan suivant.
La consigne reste -- Conventional Commits, sujet en francais sans accents, corps
accentue, c'est bien la convention du depot -- et elle dit maintenant l'inverse
sur ce point : aucun lien de session ni mention d'outil en pied de message. Les
sept pieds des messages d'exemple partent avec elle, la ligne vide qui les
precedait comprise, si bien que les exemples montrent desormais ce qu'ils
prescrivent.
Le controle tient en une ligne, et il doit rester sans reponse :
git log -1 --format=%B | grep -i assistant-de-session
Rien d'autre ne change dans le plan : ni une tache, ni un critere, ni un chiffre.
lostmind84
force-pushed
the
security/durcissement-avant-ouverture-publique
branch
from
July 30, 2026 10:17
43f399b to
99b898b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Passe de sécurité avant l'ouverture du dépôt en public, sous AGPL-3.0. La revue a porté sur tout le dépôt et non sur un diff : secrets, historique Git, surface HTTP, chaîne de mise à jour, scripts d'installation, CI, dépendances.
Ce que la revue n'a pas trouvé, et qui vaut d'être dit :
govulncheckne signale aucune vulnérabilité atteignable,npm auditaucune, et traversée de chemin, injection SQL et XSS n'ont pas de point d'entrée. Les trois contrôles de chemin (images.go,stager.go,catalogadmin.go) sont fermés, argon2id est correctement paramétré, et le caviardage dediagnostic.zipfait les deux moitiés du travail — par nom de clé et par valeur littérale.Les cinq commits
ci(deps)softprops/action-gh-releaseest une action tierce, dans le seul job qui reçoivecontents: write— unv2déplacé publie sous le nom de la coopérative des octets que les postes installent en LocalSystem. Épinglées sur un SHA, plusdependabot.ymlpour que l'épinglage ne devienne pas une dettefeat(web)nosniff,X-Frame-Options: DENY,Referrer-Policy, posés parguardavant toute décision — donc aussi sur les 403fix(webdav)net/httpconserveAuthorizationsur une redirection vers le même hôte : le compte du partage partait en clairdocs(web)docsSECURITY.md, l'entréeSUIVI.md, et le fichier vide$(readlinksuivi à la racineframe-ancestors 'none'est la directive qui compteLes autres bornent ce qu'une page charge. Celle-là ferme un acte : les gestes de dépannage de §14.4 répondent sans mot de passe par décision (ADR-033), donc une page ouverte sur le poste pouvait encadrer
/admin, le recouvrir, et faire cliquer « Rouleau changé » ou un auto-test à un bénévole qui croyait cliquer ailleurs.style-srcgarde'unsafe-inline': le CSS en ligne n'exécute rien, et une politique qui casse l'écran le jour où vite change sa façon d'émettre les styles est une politique qu'on supprime à 7 h du matin au lieu de la réparer.La CSP a été vérifiée sur les écrans réels
Pas seulement en test. Poste de démonstration lancé, catalogue déposé, les deux écrans ouverts dans un navigateur : zéro message de console, grille et 18 photos affichées, police chargée, flux SSE vivant. Et le seul motif douteux a été exercé nommément —
handToBrowser(Station.svelte:421) crée unblob:parcreateObjectURLpour l'export de configuration, ce qu'aucune directive CSP ne couvre : téléchargement effectué,violationsCSP: [].Trois tests Go nouveaux gardent le reste, dont un qui refuse qu'on élargisse
script-src— c'est la ligne qu'on relâche pour faire remarcher un écran. Le test du WebDAV a été vérifié : il tombe sans le correctif.Quatre écarts pesés et laissés
Le contexte est un poste peu sensible, sur le réseau d'une coopérative, devant des bénévoles.
SUIVI.mdporte le raisonnement de chacun ; le résumé :dav.lacagette-cooperative.frsubsiste dans 9 commits demain(943a961→776f184, corrigé par69e2f11) — la réécriture dedocs/00-donnees-retirees.mdn'avait couvert que les deux premiers commits. Le domaine n'existe plus et le mot de passe qui l'accompagne est un faux ; réécrire l'historique coûterait plus que le risque.install-sheet.txtreste en clair dansDataRoot, lisible par le compte du kiosque — mot de passe Windows et code de secours.Modifysurdata\updates, où LocalSystem exécute ensuiteupdate.ps1. Élévation locale réelle, mais elle demande un clavier sur le poste./admin/api/healthpublie l'URL WebDAV et le compte sans mot de passe, là oùinternal/diag/redact.goles caviarde de l'archive.=,+,-,@.Écrits nulle part, ils seraient re-signalés dans six mois — ou « corrigés » par quelqu'un qui ne saurait pas qu'ils ont été décidés.
Examinés puis écartés sans code, et désormais dans
SECURITY.md: signer les releases (une clé privée à tenir des années par une équipe bénévole, et une clé perdue bloque tout le parc), servir le poste en HTTPS, fermer les routes ouvertes d'ADR-033. Un fait vérifié plutôt que supposé les renforce : la tableweighingsne porte aucun identifiant de client, donc son export ouvert n'expose pas de donnée personnelle.Vérification
Une observation restée ouverte. Sur douze exécutions de la suite complète, une a signalé un échec dans un paquet, jamais reproduit ensuite — et la commande de comptage qui l'a vu n'en a pas gardé le nom. Ce n'est pas imputable à cette branche : les trois tests nouveaux ont tenu 50 exécutions chacun, et
internal/webdix passes sous-race.ci.ymldocumente déjà une famille de tests sensibles au planificateur (skipUnderShort) — première piste si le symptôme revient.Le compteur d'en-tête de
SUIVI.mdn'est pas remesuré ici : la méthode qui a produit « 2 572 » n'est pas celle qui donne 2 573 PASS / 7 SKIP sur une passe verte, et remplacer un chiffre par l'autre aurait prétendu à une continuité fausse.Reste à faire à la main, hors dépôt
Dans les réglages GitHub, avant la bascule en public : Secret scanning et Push protection.
dependabot.ymls'active seul.