Aller au contenu

ADR: défense en profondeur contre la fuite de private/ et secrets vers le remote partagé

Contexte

Le remote partagé (brickssas/bricks-brain, team-internal) reçoit le contenu diffusable à l'équipe. Deux garde-fous existaient : le pre-commit (bloque private/ stagé et les fichiers confidentiality: restricted) et le .gitignore en denylist (issu de l'ADR 2026-06-12).

Deux faiblesses subsistaient :

  1. La denylist est faillible par défaut. Un nouveau fichier de contenu wiki, non listé dans le .gitignore, part vers le partagé sans qu'on y pense — même s'il contient une info interne sensible (chiffre business, jugement, incident).
  2. Le pre-commit est contournable (--no-verify) et ne protège qu'à l'écriture locale — rien côté serveur.

Le déclencheur : brancher des traitements automatisés sur le brain augmente le volume d'écritures et le risque qu'un contenu sensible parte par accident. Il fallait rendre la non-fuite mécanique et multi-couches, pas dépendante d'une seule barrière contournable.

Options envisagées

Option A : garder la denylist seule

  • Description : statu quo, on continue d'ajouter les chemins sensibles au .gitignore.
  • Inconvénients : un nouveau fichier sensible part par défaut ; aucune barrière serveur ; contournable.

Option B : allowlist .gitignore pure (/* + ré-inclusions)

  • Description : tout ignoré par défaut, on ré-inclut explicitement le partageable.
  • Inconvénients : fragile sur les dossiers mixtes (concepts/, decisions/, resources/, people/ mêlent partagé et local, granularité au fichier) ; risque de masquer des fichiers déjà partagés ; n'untrack pas l'existant sans git rm --cached.

Option C : défense en profondeur multi-couches (retenue)

  • Description : empiler des barrières indépendantes, dont une côté serveur non contournable, et encoder « classer avant de partager » à l'écriture.
  • Avantages : la fuite catastrophique (private/, secrets) devient impossible même en --no-verify ; le nouveau contenu wiki doit déclarer sa sensibilité ; aucun chantier de reclassification de l'existant.
  • Inconvénients : friction légère (classer les nouveaux fichiers) ; le ruleset serveur ne se contourne pas, même admin.

Décision

Option C. Quatre couches indépendantes protègent le remote partagé :

  1. Pre-commit (à l'écriture) — bloque private/ stagé, les fichiers confidentiality: restricted, et tout nouveau fichier wiki .md sans classification (voir ci-dessous).
  2. Pre-push (avant l'envoi réseau, hors ligne) — refuse tout push contenant private/** ou des secrets (.env, credentials.json, secrets.yml, .cursor/mcp.json).
  3. Ruleset serveur GitHub (push ruleset « restricted file paths », enforcement active, aucun bypass) — GitHub rejette côté serveur tout push touchant ces mêmes chemins. Résiste à --no-verify.
  4. Isolation par construction — le work-tree ne connaît que le remote partagé ; le contenu complet vit dans une couche locale/privée séparée, jamais poussée vers le partagé.

« Classer avant de partager » (private par défaut, sans chantier)

Plutôt qu'une allowlist .gitignore fragile, on encode l'intention « ne partager que l'explicitement marqué » à l'écriture : tout nouveau fichier .md sous concepts/, decisions/, resources/, areas/, projects/, people/ doit porter un frontmatter confidentiality: public|internal|restricted, sinon le commit est bloqué. Les fichiers existants ne sont pas concernés (on ne contrôle que les nouveaux arrivants). L'infra (scripts/, .cursor/, plugins/, cloud-agents/…) n'est pas soumise à cette règle.

C'est la mise en œuvre mécanique de la lentille formalisation & confidentialité (« écrire propre à l'entrée ») et un pas vers l'endgame « de la denylist à la classification » évoqué dans AGENTS.md.

Conséquences

Positives attendues

  • Fuite de private/ ou de secrets vers le partagé impossible même en contournant les hooks locaux (barrière serveur).
  • Un nouveau contenu wiki ne peut plus partir sans décision de sensibilité explicite.
  • Aucune reclassification des ~233 fichiers déjà partagés (coût de migration nul).

Négatives acceptées

  • Le ruleset serveur n'a pas de bypass : personne (même admin) ne peut pousser private/ vers le partagé. Acceptable — ça n'a jamais lieu d'être.
  • Friction : créer un fichier wiki oblige à le classer.

Reversibilité

  • Coût d'annulation : bas. Le ruleset se supprime via l'API/UI GitHub ; les hooks se retirent de .githooks/.

Plan d'implémentation

  • [x] Pre-push hook (.githooks/pre-push) — testé (blocage local d'un push private/).
  • [x] Ruleset serveur GitHub (push, restricted file paths, no bypass) — testé (rejet serveur d'un push private/).
  • [x] Extension du pre-commit : garde « classer avant de partager » — testée (4 cas).
  • Ownership : romain.

Métriques de succès

  • Zéro fichier private/** ou secret présent sur le remote partagé, en continu.
  • Zéro nouveau fichier wiki partagé sans classification.

Sources

Révisions

  • 2026-07-09 : créée et acceptée (status: accepted).